Index: chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java |
index 4fa1fbc10ba4ef882eaaa9a54060984b4514a729..9c82199a6c6fd0380299eb350352865d14680b2f 100644 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java |
@@ -39,14 +39,12 @@ import org.chromium.ui.widget.Toast; |
import java.io.File; |
import java.util.ArrayList; |
import java.util.Arrays; |
+import java.util.HashMap; |
import java.util.HashSet; |
import java.util.Iterator; |
import java.util.List; |
import java.util.Set; |
-import java.util.Vector; |
-import java.util.concurrent.ConcurrentHashMap; |
import java.util.concurrent.TimeUnit; |
-import java.util.concurrent.atomic.AtomicBoolean; |
/** |
* Chrome implementation of the {@link DownloadController.DownloadNotificationService} interface. |
@@ -106,23 +104,21 @@ public class DownloadManagerService extends BroadcastReceiver implements |
private static boolean sIsNetworkMetered; |
private final SharedPreferences mSharedPrefs; |
- private final ConcurrentHashMap<String, DownloadProgress> mDownloadProgressMap = |
- new ConcurrentHashMap<String, DownloadProgress>(4, 0.75f, 2); |
+ private final HashMap<String, DownloadProgress> mDownloadProgressMap = |
+ new HashMap<String, DownloadProgress>(4, 0.75f); |
private final DownloadNotifier mDownloadNotifier; |
// Delay between UI updates. |
private final long mUpdateDelayInMillis; |
- // Flag to track if we need to post a task to update download notifications. |
- private final AtomicBoolean mIsUIUpdateScheduled; |
private final Handler mHandler; |
private final Context mContext; |
private final LongSparseArray<DownloadItem> mSystemDownloadIdMap = |
new LongSparseArray<DownloadItem>(); |
// Using vector for thread safety. |
- @VisibleForTesting protected final Vector<String> mAutoResumableDownloadIds = |
- new Vector<String>(); |
+ @VisibleForTesting protected final List<String> mAutoResumableDownloadIds = |
+ new ArrayList<String>(); |
private final List<DownloadUmaStatsEntry> mUmaEntries = new ArrayList<DownloadUmaStatsEntry>(); |
private final DownloadHistoryAdapter mDownloadHistoryAdapter; |
@@ -131,6 +127,8 @@ public class DownloadManagerService extends BroadcastReceiver implements |
private long mNativeDownloadManagerService; |
private DownloadManagerDelegate mDownloadManagerDelegate; |
private NetworkChangeNotifierAutoDetect mNetworkChangeNotifier; |
+ // Flag to track if we need to post a task to update download notifications. |
+ private boolean mIsUIUpdateScheduled; |
/** |
* Class representing progress of a download. |
@@ -138,8 +136,10 @@ public class DownloadManagerService extends BroadcastReceiver implements |
private static class DownloadProgress { |
final long mStartTimeInMillis; |
boolean mCanDownloadWhileMetered; |
- volatile DownloadItem mDownloadItem; |
- volatile int mDownloadStatus; |
+ DownloadItem mDownloadItem; |
+ int mDownloadStatus; |
+ boolean mIsAutoResumable; |
+ boolean mIsUpdated; |
DownloadProgress(long startTimeInMillis, boolean canDownloadWhileMetered, |
DownloadItem downloadItem, int downloadStatus) { |
@@ -147,6 +147,17 @@ public class DownloadManagerService extends BroadcastReceiver implements |
mCanDownloadWhileMetered = canDownloadWhileMetered; |
mDownloadItem = downloadItem; |
mDownloadStatus = downloadStatus; |
+ mIsAutoResumable = false; |
+ mIsUpdated = true; |
+ } |
+ |
+ DownloadProgress(DownloadProgress progress) { |
+ mStartTimeInMillis = progress.mStartTimeInMillis; |
+ mCanDownloadWhileMetered = progress.mCanDownloadWhileMetered; |
+ mDownloadItem = progress.mDownloadItem; |
+ mDownloadStatus = progress.mDownloadStatus; |
+ mIsAutoResumable = progress.mIsAutoResumable; |
+ mIsUpdated = progress.mIsUpdated; |
} |
} |
@@ -231,7 +242,6 @@ public class DownloadManagerService extends BroadcastReceiver implements |
mDownloadNotifier = downloadNotifier; |
mUpdateDelayInMillis = updateDelayInMillis; |
mHandler = handler; |
- mIsUIUpdateScheduled = new AtomicBoolean(false); |
mOMADownloadHandler = new OMADownloadHandler(context); |
mDownloadSnackbarController = new DownloadSnackbarController(context); |
mDownloadManagerDelegate = new DownloadManagerDelegate(mContext); |
@@ -291,19 +301,19 @@ public class DownloadManagerService extends BroadcastReceiver implements |
@Override |
public void onDownloadUpdated(final DownloadInfo downloadInfo) { |
DownloadItem item = new DownloadItem(false, downloadInfo); |
- updateDownloadProgress(item, DOWNLOAD_STATUS_IN_PROGRESS); |
// If user manually paused a download, this download is no longer auto resumable. |
if (downloadInfo.isPaused()) { |
removeAutoResumableDownload(item.getId()); |
} |
+ updateDownloadProgress(item, DOWNLOAD_STATUS_IN_PROGRESS); |
scheduleUpdateIfNeeded(); |
} |
@Override |
public void onDownloadCancelled(final DownloadInfo downloadInfo) { |
DownloadItem item = new DownloadItem(false, downloadInfo); |
- updateDownloadProgress(new DownloadItem(false, downloadInfo), DOWNLOAD_STATUS_CANCELLED); |
removeAutoResumableDownload(item.getId()); |
+ updateDownloadProgress(new DownloadItem(false, downloadInfo), DOWNLOAD_STATUS_CANCELLED); |
scheduleUpdateIfNeeded(); |
} |
@@ -517,70 +527,58 @@ public class DownloadManagerService extends BroadcastReceiver implements |
} |
/** |
- * Updates notifications for all current downloads. Should not be called from UI thread. |
- * |
+ * Updates notifications for a given list of downloads. Should not be called from UI thread. |
+ * @param progresses A list of notifications to update. |
* @return A List of failed downloads. |
*/ |
- private List<DownloadItem> updateAllNotifications() { |
+ private List<DownloadItem> updateAllNotifications(List<DownloadProgress> progresses) { |
assert !ThreadUtils.runningOnUiThread(); |
List<DownloadItem> downloadItems = new ArrayList<DownloadItem>(); |
- for (DownloadProgress progress : mDownloadProgressMap.values()) { |
- if (progress != null) { |
- DownloadItem item = progress.mDownloadItem; |
- DownloadInfo info = item.getDownloadInfo(); |
- switch (progress.mDownloadStatus) { |
- case DOWNLOAD_STATUS_COMPLETE: |
- mDownloadProgressMap.remove(item.getId()); |
- boolean success = addCompletedDownload(item); |
- if (success) { |
- boolean canResolve = isOMADownloadDescription(info) |
- || canResolveDownloadItem(mContext, item); |
- long systemDownloadId = item.getSystemDownloadId(); |
- mDownloadNotifier.notifyDownloadSuccessful( |
- info, systemDownloadId, canResolve, |
- getLaunchIntentFromDownloadId(mContext, systemDownloadId)); |
- broadcastDownloadSuccessful(info); |
- } else { |
- downloadItems.add(item); |
- mDownloadNotifier.notifyDownloadFailed(info); |
- } |
- break; |
- case DOWNLOAD_STATUS_FAILED: |
- mDownloadProgressMap.remove(item.getId()); |
- mDownloadNotifier.notifyDownloadFailed(info); |
+ for (int i = 0; i < progresses.size(); ++i) { |
+ DownloadProgress progress = progresses.get(i); |
+ DownloadItem item = progress.mDownloadItem; |
+ DownloadInfo info = item.getDownloadInfo(); |
+ switch (progress.mDownloadStatus) { |
+ case DOWNLOAD_STATUS_COMPLETE: |
+ boolean success = addCompletedDownload(item); |
+ if (success) { |
+ boolean canResolve = isOMADownloadDescription(info) |
+ || canResolveDownloadItem(mContext, item); |
+ long systemDownloadId = item.getSystemDownloadId(); |
+ mDownloadNotifier.notifyDownloadSuccessful( |
+ info, systemDownloadId, canResolve, |
+ getLaunchIntentFromDownloadId(mContext, systemDownloadId)); |
+ broadcastDownloadSuccessful(info); |
+ } else { |
downloadItems.add(item); |
- Log.w(TAG, "Download failed: " + info.getFilePath()); |
- break; |
- case DOWNLOAD_STATUS_IN_PROGRESS: |
- if (info.isPaused()) { |
- mDownloadProgressMap.remove(item.getId()); |
- mDownloadNotifier.notifyDownloadPaused(info, false); |
- if (info.isResumable()) { |
- recordDownloadResumption(UMA_DOWNLOAD_RESUMPTION_MANUAL_PAUSE); |
- } |
- } else { |
- mDownloadNotifier.notifyDownloadProgress(info, |
- progress.mStartTimeInMillis, progress.mCanDownloadWhileMetered); |
- } |
- break; |
- case DOWNLOAD_STATUS_CANCELLED: |
- mDownloadProgressMap.remove(item.getId()); |
- mDownloadNotifier.notifyDownloadCanceled(item.getId()); |
- break; |
- case DOWNLOAD_STATUS_INTERRUPTED: |
- // If the download can be auto resumed, keep it in the progress map so we |
- // can resume it once network becomes available. |
- boolean isAutoResumable = |
- mAutoResumableDownloadIds.contains(item.getId()); |
- if (!isAutoResumable) { |
- mDownloadProgressMap.remove(item.getId()); |
+ mDownloadNotifier.notifyDownloadFailed(info); |
+ } |
+ break; |
+ case DOWNLOAD_STATUS_FAILED: |
+ mDownloadNotifier.notifyDownloadFailed(info); |
+ downloadItems.add(item); |
+ Log.w(TAG, "Download failed: " + info.getFilePath()); |
+ break; |
+ case DOWNLOAD_STATUS_IN_PROGRESS: |
+ if (info.isPaused()) { |
+ mDownloadNotifier.notifyDownloadPaused(info, false); |
+ if (info.isResumable()) { |
+ recordDownloadResumption(UMA_DOWNLOAD_RESUMPTION_MANUAL_PAUSE); |
} |
- mDownloadNotifier.notifyDownloadPaused(info, isAutoResumable); |
- break; |
- default: |
- assert false; |
- break; |
- } |
+ } else { |
+ mDownloadNotifier.notifyDownloadProgress(info, |
+ progress.mStartTimeInMillis, progress.mCanDownloadWhileMetered); |
+ } |
+ break; |
+ case DOWNLOAD_STATUS_CANCELLED: |
+ mDownloadNotifier.notifyDownloadCanceled(item.getId()); |
+ break; |
+ case DOWNLOAD_STATUS_INTERRUPTED: |
+ mDownloadNotifier.notifyDownloadPaused(info, progress.mIsAutoResumable); |
+ break; |
+ default: |
+ assert false; |
+ break; |
} |
} |
return downloadItems; |
@@ -638,30 +636,50 @@ public class DownloadManagerService extends BroadcastReceiver implements |
* Schedule an update if there is no update scheduled. |
*/ |
private void scheduleUpdateIfNeeded() { |
- if (mIsUIUpdateScheduled.compareAndSet(false, true)) { |
- Runnable updateTask = new Runnable() { |
- @Override |
- public void run() { |
- new AsyncTask<Void, Void, List<DownloadItem>>() { |
- @Override |
- public List<DownloadItem> doInBackground(Void... params) { |
- return updateAllNotifications(); |
- } |
+ if (mIsUIUpdateScheduled) return; |
- @Override |
- protected void onPostExecute(List<DownloadItem> result) { |
- for (int i = 0; i < result.size(); ++i) { |
- // TODO(qinmin): get the failure message from native. |
- onDownloadFailed(result.get(i).getDownloadInfo().getFileName(), |
- DownloadManager.ERROR_UNKNOWN); |
- } |
- } |
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
- mIsUIUpdateScheduled.set(false); |
- } |
- }; |
- mHandler.postDelayed(updateTask, mUpdateDelayInMillis); |
+ mIsUIUpdateScheduled = true; |
+ final List<DownloadProgress> progressToUpdate = new ArrayList<DownloadProgress>(); |
+ for (DownloadProgress progress : mDownloadProgressMap.values()) { |
+ if (progress.mIsUpdated) { |
+ progressToUpdate.add(new DownloadProgress(progress)); |
+ progress.mIsUpdated = false; |
+ } |
+ // Remove progress entry from mDownloadProgressMap if they are no longer needed. |
+ if ((progress.mDownloadStatus != DOWNLOAD_STATUS_IN_PROGRESS |
+ || progress.mDownloadItem.getDownloadInfo().isPaused()) |
+ && (progress.mDownloadStatus != DOWNLOAD_STATUS_INTERRUPTED |
+ || !progress.mIsAutoResumable)) { |
+ mDownloadProgressMap.remove(progress.mDownloadItem.getId()); |
+ } |
} |
+ if (progressToUpdate.isEmpty()) { |
+ mIsUIUpdateScheduled = false; |
+ return; |
+ } |
+ new AsyncTask<Void, Void, List<DownloadItem>>() { |
+ @Override |
+ public List<DownloadItem> doInBackground(Void... params) { |
+ return updateAllNotifications(progressToUpdate); |
+ } |
+ |
+ @Override |
+ protected void onPostExecute(List<DownloadItem> result) { |
+ for (int i = 0; i < result.size(); ++i) { |
+ // TODO(qinmin): get the failure message from native. |
+ onDownloadFailed(result.get(i).getDownloadInfo().getFileName(), |
+ DownloadManager.ERROR_UNKNOWN); |
+ } |
+ } |
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
+ Runnable scheduleNextUpdateTask = new Runnable(){ |
+ @Override |
+ public void run() { |
+ mIsUIUpdateScheduled = false; |
+ scheduleUpdateIfNeeded(); |
+ } |
+ }; |
+ mHandler.postDelayed(scheduleNextUpdateTask, mUpdateDelayInMillis); |
} |
/** |
@@ -678,7 +696,8 @@ public class DownloadManagerService extends BroadcastReceiver implements |
long startTime = System.currentTimeMillis(); |
progress = new DownloadProgress( |
startTime, isActiveNetworkMetered(mContext), downloadItem, downloadStatus); |
- mDownloadProgressMap.putIfAbsent(id, progress); |
+ progress.mIsUpdated = true; |
+ mDownloadProgressMap.put(id, progress); |
if (getUmaStatsEntry(downloadItem.getId()) == null) { |
addUmaStatsEntry(new DownloadUmaStatsEntry( |
downloadItem.getId(), startTime, 0, false, false)); |
@@ -689,6 +708,8 @@ public class DownloadManagerService extends BroadcastReceiver implements |
progress.mDownloadStatus = downloadStatus; |
progress.mDownloadItem = downloadItem; |
+ progress.mIsUpdated = true; |
+ progress.mIsAutoResumable = mAutoResumableDownloadIds.contains(id); |
DownloadUmaStatsEntry entry; |
switch (downloadStatus) { |
case DOWNLOAD_STATUS_COMPLETE: |