Improve download remaining time calculation.

Calculate remaining time in DownloadNotifier so we always use the
most recent progress data, which avoids showing stale times.

Weight speed calculation to prefer historical data so remaining time
is smoother on bumpy network connections.

Bug: 7631948
Change-Id: I497be0899e65086356745340b19d3987c6185bdd
diff --git a/src/com/android/providers/downloads/DownloadHandler.java b/src/com/android/providers/downloads/DownloadHandler.java
index dff09eb..2f02864 100644
--- a/src/com/android/providers/downloads/DownloadHandler.java
+++ b/src/com/android/providers/downloads/DownloadHandler.java
@@ -37,7 +37,7 @@
     private final HashMap<Long, DownloadInfo> mDownloadsInProgress =
             new HashMap<Long, DownloadInfo>();
     @GuardedBy("this")
-    private final LongSparseArray<Long> mRemainingMillis = new LongSparseArray<Long>();
+    private final LongSparseArray<Long> mCurrentSpeed = new LongSparseArray<Long>();
 
     private final int mMaxConcurrentDownloadsAllowed = Resources.getSystem().getInteger(
             com.android.internal.R.integer.config_MaxConcurrentDownloadsAllowed);
@@ -82,23 +82,19 @@
 
     public synchronized void dequeueDownload(long id) {
         mDownloadsInProgress.remove(id);
-        mRemainingMillis.remove(id);
+        mCurrentSpeed.remove(id);
         startDownloadThreadLocked();
         if (mDownloadsInProgress.size() == 0 && mDownloadsQueue.size() == 0) {
             notifyAll();
         }
     }
 
-    public synchronized void setRemainingMillis(long id, long millis) {
-        mRemainingMillis.put(id, millis);
+    public synchronized void setCurrentSpeed(long id, long speed) {
+        mCurrentSpeed.put(id, speed);
     }
 
-    /**
-     * Return remaining time until given {@link DownloadInfo} finishes, in
-     * milliseconds, or -1 if unknown.
-     */
-    public synchronized long getRemainingMillis(long id) {
-        return mRemainingMillis.get(id, -1L);
+    public synchronized long getCurrentSpeed(long id) {
+        return mCurrentSpeed.get(id, -1L);
     }
 
     // right now this is only used by tests. but there is no reason why it can't be used
diff --git a/src/com/android/providers/downloads/DownloadNotifier.java b/src/com/android/providers/downloads/DownloadNotifier.java
index f6e7a2e..f89d2d0 100644
--- a/src/com/android/providers/downloads/DownloadNotifier.java
+++ b/src/com/android/providers/downloads/DownloadNotifier.java
@@ -163,23 +163,24 @@
 
                 long current = 0;
                 long total = 0;
-                long remainingMillis = -1;
+                long speed = 0;
                 for (DownloadInfo info : cluster) {
                     if (info.mTotalBytes != -1) {
                         current += info.mCurrentBytes;
                         total += info.mTotalBytes;
-                        remainingMillis = Math.max(
-                                handler.getRemainingMillis(info.mId), remainingMillis);
+                        speed += handler.getCurrentSpeed(info.mId);
                     }
                 }
 
                 if (total > 0) {
                     final int percent = (int) ((current * 100) / total);
-                    if (remainingMillis != -1) {
+                    percentText = res.getString(R.string.download_percent, percent);
+
+                    if (speed > 0) {
+                        final long remainingMillis = ((total - current) * 1000) / speed;
                         remainingText = res.getString(R.string.download_remaining,
                                 DateUtils.formatDuration(remainingMillis));
                     }
-                    percentText = res.getString(R.string.download_percent, percent);
 
                     builder.setProgress(100, percent, false);
                 } else {
diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java
index 2bd3d36..34bc8e3 100644
--- a/src/com/android/providers/downloads/DownloadThread.java
+++ b/src/com/android/providers/downloads/DownloadThread.java
@@ -107,8 +107,6 @@
         public long mSpeedSampleStart;
         /** Bytes transferred since current sample started. */
         public long mSpeedSampleBytes;
-        /** Estimated time until finished. */
-        public long mRemainingMillis;
 
         public State(DownloadInfo info) {
             mMimeType = Intent.normalizeMimeType(info.mMimeType);
@@ -443,20 +441,13 @@
             if (state.mSpeed == 0) {
                 state.mSpeed = sampleSpeed;
             } else {
-                state.mSpeed = (state.mSpeed + sampleSpeed) / 2;
+                state.mSpeed = ((state.mSpeed * 3) + sampleSpeed) / 4;
             }
 
             state.mSpeedSampleStart = now;
             state.mSpeedSampleBytes = state.mCurrentBytes;
 
-            if (state.mSpeed != 0) {
-                state.mRemainingMillis = ((state.mTotalBytes - state.mCurrentBytes) * 1000)
-                        / state.mSpeed;
-            } else {
-                state.mRemainingMillis = -1;
-            }
-
-            DownloadHandler.getInstance().setRemainingMillis(mInfo.mId, state.mRemainingMillis);
+            DownloadHandler.getInstance().setCurrentSpeed(mInfo.mId, state.mSpeed);
         }
 
         if (state.mCurrentBytes - state.mBytesNotified > Constants.MIN_PROGRESS_STEP &&