summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnthony Lee <anthonylee@google.com>2014-05-12 10:17:43 -0700
committerAnthony Lee <anthonylee@google.com>2014-05-12 13:50:50 -0700
commit0e49862711852775acc91aa8d2dee2b6b70ea1d0 (patch)
tree6d3a0b50d23fb90a87c1e47ca8aee9b06f5dc6d7
parentd222c6b817a9979bd26070d22bac60ab56e8d67a (diff)
downloadandroid_packages_apps_Email-0e49862711852775acc91aa8d2dee2b6b70ea1d0.tar.gz
android_packages_apps_Email-0e49862711852775acc91aa8d2dee2b6b70ea1d0.tar.bz2
android_packages_apps_Email-0e49862711852775acc91aa8d2dee2b6b70ea1d0.zip
Introduction of DownloadQueue and associated tests.
Also a bit of cleanup around log tags and DownloadRequest. Change-Id: I33221f099ed691e5258e1741cf0a63381404f520
-rw-r--r--proguard.flags15
-rw-r--r--src/com/android/email/service/AttachmentService.java421
-rw-r--r--tests/src/com/android/email/service/AttachmentServiceTests.java390
3 files changed, 688 insertions, 138 deletions
diff --git a/proguard.flags b/proguard.flags
index a9be26c14..a0feee6b8 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -139,6 +139,21 @@
-keep class com.android.emailcommon.mail.Flag
+-keepclasseswithmembers class com.android.email.service.AttachmentService$DownloadQueue {
+ *** addRequest(com.android.email.service.AttachmentService$DownloadRequest);
+ *** removeRequest(com.android.email.service.AttachmentService$DownloadRequest);
+ *** getNextRequest();
+ *** findRequestById(long);
+ *** getSize();
+ *** isEmpty();
+}
+
+-keepclasseswithmembers class com.android.email.service.AttachmentService$DownloadRequest {
+ <init>(int, long);
+ *** hashCode();
+ *** equals(java.lang.Object);
+}
+
-keepclasseswithmembers class com.android.emailcommon.mail.Folder {
*** getUnreadMessageCount();
*** delete(boolean);
diff --git a/src/com/android/email/service/AttachmentService.java b/src/com/android/email/service/AttachmentService.java
index e74d12bf8..f6514e15b 100644
--- a/src/com/android/email/service/AttachmentService.java
+++ b/src/com/android/email/service/AttachmentService.java
@@ -48,6 +48,7 @@ import com.android.emailcommon.utility.AttachmentUtilities;
import com.android.emailcommon.utility.Utility;
import com.android.mail.providers.UIProvider.AttachmentState;
import com.android.mail.utils.LogUtils;
+import com.google.common.annotations.VisibleForTesting;
import java.io.File;
import java.io.FileDescriptor;
@@ -55,13 +56,15 @@ import java.io.PrintWriter;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.PriorityQueue;
import java.util.Queue;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
public class AttachmentService extends Service implements Runnable {
- public static final String TAG = LogUtils.TAG;
+ // For logging.
+ public static final String LOG_TAG = "AttachmentService";
// Minimum wait time before retrying a download that failed due to connection error
private static final long CONNECTION_ERROR_RETRY_MILLIS = 10 * DateUtils.SECOND_IN_MILLIS;
@@ -78,14 +81,16 @@ public class AttachmentService extends Service implements Runnable {
private static final int CALLBACK_TIMEOUT = 30 * ((int)DateUtils.SECOND_IN_MILLIS);
// Try to download an attachment in the background this many times before giving up
private static final int MAX_DOWNLOAD_RETRIES = 5;
- private static final int PRIORITY_NONE = -1;
- @SuppressWarnings("unused")
- // Low priority will be used for opportunistic downloads
- private static final int PRIORITY_BACKGROUND = 0;
- // Normal priority is for forwarded downloads in outgoing mail
- private static final int PRIORITY_SEND_MAIL = 1;
+
+ /* package */ static final int PRIORITY_NONE = -1;
// High priority is for user requests
- private static final int PRIORITY_FOREGROUND = 2;
+ /* package */ static final int PRIORITY_FOREGROUND = 0;
+ /* package */ static final int PRIORITY_HIGHEST = PRIORITY_FOREGROUND;
+ // Normal priority is for forwarded downloads in outgoing mail
+ /* package */ static final int PRIORITY_SEND_MAIL = 1;
+ // Low priority will be used for opportunistic downloads
+ /* package */ static final int PRIORITY_BACKGROUND = 2;
+ /* package */ static final int PRIORITY_LOWEST = PRIORITY_BACKGROUND;
// Minimum free storage in order to perform prefetch (25% of total memory)
private static final float PREFETCH_MINIMUM_STORAGE_AVAILABLE = 0.25F;
@@ -174,62 +179,86 @@ public class AttachmentService extends Service implements Runnable {
}
}
- public static class DownloadRequest {
- final int priority;
- final long time;
- final long attachmentId;
- final long messageId;
- final long accountId;
- boolean inProgress = false;
- int lastStatusCode;
- int lastProgress;
- long lastCallbackTime;
- long startTime;
- long retryCount;
- long retryStartTime;
-
- private DownloadRequest(Context context, Attachment attachment) {
- attachmentId = attachment.mId;
- Message msg = Message.restoreMessageWithId(context, attachment.mMessageKey);
+ /**
+ * This class is used to contain the details and state of a particular request to download
+ * an attachment. These objects are constructed and either placed in the {@link DownloadQueue}
+ * or in the in-progress map used to keep track of downloads that are currently happening
+ * in the system
+ */
+// public static class DownloadRequest {
+ /*package*/ static class DownloadRequest {
+ // Details of the request.
+ final int mPriority;
+ final long mTime;
+ final long mAttachmentId;
+ final long mMessageId;
+ final long mAccountId;
+
+ // Status of the request.
+ boolean mInProgress = false;
+ int mLastStatusCode;
+ int mLastProgress;
+ long mLastCallbackTime;
+ long mStartTime;
+ long mRetryCount;
+ long mRetryStartTime;
+
+ /**
+ * This constructor is mainly used for tests
+ * @param attPriority The priority of this attachment
+ * @param attId The id of the row in the attachment table.
+ */
+ @VisibleForTesting
+ DownloadRequest(final int attPriority, final long attId) {
+ // This constructor should only be used for unit tests.
+ mTime = SystemClock.elapsedRealtime();
+ mPriority = attPriority;
+ mAttachmentId = attId;
+ mAccountId = -1;
+ mMessageId = -1;
+ }
+
+ private DownloadRequest(final Context context, final Attachment attachment) {
+ mAttachmentId = attachment.mId;
+ final Message msg = Message.restoreMessageWithId(context, attachment.mMessageKey);
if (msg != null) {
- accountId = msg.mAccountKey;
- messageId = msg.mId;
+ mAccountId = msg.mAccountKey;
+ mMessageId = msg.mId;
} else {
- accountId = messageId = -1;
+ mAccountId = mMessageId = -1;
}
- priority = getPriority(attachment);
- time = SystemClock.elapsedRealtime();
+ mPriority = getPriority(attachment);
+ mTime = SystemClock.elapsedRealtime();
}
- private DownloadRequest(DownloadRequest orig, long newTime) {
- priority = orig.priority;
- attachmentId = orig.attachmentId;
- messageId = orig.messageId;
- accountId = orig.accountId;
- time = newTime;
- inProgress = orig.inProgress;
- lastStatusCode = orig.lastStatusCode;
- lastProgress = orig.lastProgress;
- lastCallbackTime = orig.lastCallbackTime;
- startTime = orig.startTime;
- retryCount = orig.retryCount;
- retryStartTime = orig.retryStartTime;
+ private DownloadRequest(final DownloadRequest orig, final long newTime) {
+ mPriority = orig.mPriority;
+ mAttachmentId = orig.mAttachmentId;
+ mMessageId = orig.mMessageId;
+ mAccountId = orig.mAccountId;
+ mTime = newTime;
+ mInProgress = orig.mInProgress;
+ mLastStatusCode = orig.mLastStatusCode;
+ mLastProgress = orig.mLastProgress;
+ mLastCallbackTime = orig.mLastCallbackTime;
+ mStartTime = orig.mStartTime;
+ mRetryCount = orig.mRetryCount;
+ mRetryStartTime = orig.mRetryStartTime;
}
-
@Override
public int hashCode() {
- return (int)attachmentId;
+ return (int)mAttachmentId;
}
/**
* Two download requests are equals if their attachment id's are equals
*/
@Override
- public boolean equals(Object object) {
+ public boolean equals(final Object object) {
if (!(object instanceof DownloadRequest)) return false;
- DownloadRequest req = (DownloadRequest)object;
- return req.attachmentId == attachmentId;
+ final DownloadRequest req = (DownloadRequest)object;
+ return req.mAttachmentId == mAttachmentId;
}
}
@@ -237,17 +266,17 @@ public class AttachmentService extends Service implements Runnable {
* Comparator class for the download set; we first compare by priority. Requests with equal
* priority are compared by the time the request was created (older requests come first)
*/
- /*protected*/ static class DownloadComparator implements Comparator<DownloadRequest> {
+ /*package*/ static class DownloadComparator implements Comparator<DownloadRequest> {
@Override
public int compare(DownloadRequest req1, DownloadRequest req2) {
int res;
- if (req1.priority != req2.priority) {
- res = (req1.priority < req2.priority) ? -1 : 1;
+ if (req1.mPriority != req2.mPriority) {
+ res = (req1.mPriority < req2.mPriority) ? -1 : 1;
} else {
- if (req1.time == req2.time) {
+ if (req1.mTime == req2.mTime) {
res = 0;
} else {
- res = (req1.time > req2.time) ? -1 : 1;
+ res = (req1.mTime < req2.mTime) ? -1 : 1;
}
}
return res;
@@ -255,6 +284,122 @@ public class AttachmentService extends Service implements Runnable {
}
/**
+ * This class is used to organize the various download requests that are pending.
+ * We need a class that allows us to prioritize a collection of {@link DownloadRequest} objects
+ * while being able to pull off request with the highest priority but we also need
+ * to be able to find a particular {@link DownloadRequest} by id or by reference for retrieval.
+ * Bonus points for an implementation that does not require an iterator to accomplish its tasks
+ * as we can avoid pesky ConcurrentModificationException when one thread has the iterator
+ * and another thread modifies the collection.
+ */
+ /*package*/ static class DownloadQueue {
+ private final int DEFAULT_SIZE = 10;
+
+ // For synchronization
+ private final Object mLock = new Object();
+
+ // For prioritization of DownloadRequests.
+ /*package*/ final PriorityQueue<DownloadRequest> mRequestQueue =
+ new PriorityQueue<DownloadRequest>(DEFAULT_SIZE,
+ new AttachmentService.DownloadComparator());
+
+ // Secondary collection to quickly find objects w/o the help of an iterator.
+ // This class should be kept in lock step with the priority queue.
+ /*package*/ final ConcurrentHashMap<Long, DownloadRequest> mRequestMap =
+ new ConcurrentHashMap<Long, DownloadRequest>();
+
+ /**
+ * This function will add the request to our collections if it does not already
+ * exist. If it does exist, the function will silently succeed.
+ * @param request The {@link DownloadRequest} that should be added to our queue
+ * @return true if it was added (or already exists), false otherwise
+ */
+ public synchronized boolean addRequest(final DownloadRequest request) {
+ // It is key to keep the map and queue in lock step
+ if (request == null) {
+ // We can't add a null entry into the queue
+ LogUtils.wtf(AttachmentService.LOG_TAG, "Adding a null DownloadRequest");
+ return false;
+ }
+ final long requestId = request.mAttachmentId;
+ if (requestId < 0) {
+ // Invalid request
+ LogUtils.wtf(AttachmentService.LOG_TAG,
+ "Adding a DownloadRequest with an invalid id");
+ return false;
+ }
+ synchronized (mLock) {
+ // Check to see if this request is is already in the queue
+ final boolean exists = mRequestMap.containsKey(requestId);
+ if (!exists) {
+ mRequestQueue.offer(request);
+ mRequestMap.put(requestId, request);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * This function will remove the specified request from the internal collections.
+ * @param request The {@link DownloadRequest} that should be removed from our queue
+ * @return true if it was removed or the request was invalid (meaning that the request
+ * is not in our queue), false otherwise.
+ */
+ public synchronized boolean removeRequest(final DownloadRequest request) {
+ if (request == null) {
+ // If it is invalid, its not in the queue.
+ return true;
+ }
+ final boolean result;
+ synchronized (mLock) {
+ // It is key to keep the map and queue in lock step
+ result = mRequestQueue.remove(request);
+ if (result) {
+ mRequestMap.remove(request.mAttachmentId);
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Return the next request from our queue.
+ * @return The next {@link DownloadRequest} object or null if the queue is empty
+ */
+ public synchronized DownloadRequest getNextRequest() {
+ // It is key to keep the map and queue in lock step
+ final DownloadRequest returnRequest;
+ synchronized (mLock) {
+ returnRequest = mRequestQueue.poll();
+ if (returnRequest != null) {
+ final long requestId = returnRequest.mAttachmentId;
+ mRequestMap.remove(requestId);
+ }
+ }
+ return returnRequest;
+ }
+
+ /**
+ * Return the {@link DownloadRequest} with the given ID (attachment ID)
+ * @param requestId The ID of the request in question
+ * @return The associated {@link DownloadRequest} object or null if it does not exist
+ */
+ public DownloadRequest findRequestById(final long requestId) {
+ if (requestId < 0) {
+ return null;
+ }
+ return mRequestMap.get(requestId);
+ }
+
+ public int getSize() {
+ return mRequestMap.size();
+ }
+
+ public boolean isEmpty() {
+ return mRequestMap.isEmpty();
+ }
+ }
+
+ /**
* The DownloadSet is a TreeSet sorted by priority class (e.g. low, high, etc.) and the
* time of the request. Higher priority requests
* are always processed first; among equals, the oldest request is processed first. The
@@ -293,15 +438,15 @@ public class AttachmentService extends Service implements Runnable {
DownloadRequest req = findDownloadRequest(att.mId);
long priority = getPriority(att);
if (priority == PRIORITY_NONE) {
- if (LogUtils.isLoggable(TAG, LogUtils.DEBUG)) {
- LogUtils.d(TAG, "== Attachment changed: " + att.mId);
+ if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
+ LogUtils.d(LOG_TAG, "== Attachment changed: " + att.mId);
}
// In this case, there is no download priority for this attachment
if (req != null) {
// If it exists in the map, remove it
// NOTE: We don't yet support deleting downloads in progress
- if (LogUtils.isLoggable(TAG, LogUtils.DEBUG)) {
- LogUtils.d(TAG, "== Attachment " + att.mId + " was in queue, removing");
+ if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
+ LogUtils.d(LOG_TAG, "== Attachment " + att.mId + " was in queue, removing");
}
remove(req);
}
@@ -338,9 +483,9 @@ public class AttachmentService extends Service implements Runnable {
}
// If the request already existed, we'll update the priority (so that the time is
// up-to-date); otherwise, we create a new request
- if (LogUtils.isLoggable(TAG, LogUtils.DEBUG)) {
- LogUtils.d(TAG, "== Download queued for attachment " + att.mId + ", class " +
- req.priority + ", priority time " + req.time);
+ if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
+ LogUtils.d(LOG_TAG, "== Download queued for attachment " + att.mId + ", class " +
+ req.mPriority + ", priority time " + req.mTime);
}
}
// Process the queue if we're in a wait
@@ -356,7 +501,7 @@ public class AttachmentService extends Service implements Runnable {
Iterator<DownloadRequest> iterator = iterator();
while(iterator.hasNext()) {
DownloadRequest req = iterator.next();
- if (req.attachmentId == id) {
+ if (req.mAttachmentId == id) {
return req;
}
}
@@ -373,8 +518,8 @@ public class AttachmentService extends Service implements Runnable {
* the limit on maximum downloads
*/
/*package*/ synchronized void processQueue() {
- if (LogUtils.isLoggable(TAG, LogUtils.DEBUG)) {
- LogUtils.d(TAG, "== Checking attachment queue, " + mDownloadSet.size()
+ if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
+ LogUtils.d(LOG_TAG, "== Checking attachment queue, " + mDownloadSet.size()
+ " entries");
}
Iterator<DownloadRequest> iterator = mDownloadSet.descendingIterator();
@@ -383,19 +528,19 @@ public class AttachmentService extends Service implements Runnable {
(mDownloadsInProgress.size() < MAX_SIMULTANEOUS_DOWNLOADS)) {
DownloadRequest req = iterator.next();
// Enforce per-account limit here
- if (downloadsForAccount(req.accountId) >= MAX_SIMULTANEOUS_DOWNLOADS_PER_ACCOUNT) {
- if (LogUtils.isLoggable(TAG, LogUtils.DEBUG)) {
- LogUtils.d(TAG, "== Skip #" + req.attachmentId + "; maxed for acct #" +
- req.accountId);
+ if (downloadsForAccount(req.mAccountId) >= MAX_SIMULTANEOUS_DOWNLOADS_PER_ACCOUNT) {
+ if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
+ LogUtils.d(LOG_TAG, "== Skip #" + req.mAttachmentId + "; maxed for acct #" +
+ req.mAccountId);
}
continue;
- } else if (Attachment.restoreAttachmentWithId(mContext, req.attachmentId) == null) {
+ } else if (Attachment.restoreAttachmentWithId(mContext, req.mAttachmentId) == null) {
continue;
}
- if (!req.inProgress) {
+ if (!req.mInProgress) {
final long currentTime = SystemClock.elapsedRealtime();
- if (req.retryCount > 0 && req.retryStartTime > currentTime) {
- LogUtils.d(TAG, "== waiting to retry attachment %d", req.attachmentId);
+ if (req.mRetryCount > 0 && req.mRetryStartTime > currentTime) {
+ LogUtils.d(LOG_TAG, "== waiting to retry attachment %d", req.mAttachmentId);
setWatchdogAlarm(CONNECTION_ERROR_RETRY_MILLIS);
continue;
}
@@ -462,7 +607,7 @@ public class AttachmentService extends Service implements Runnable {
// query results. We are most likely here for other reasons such
// as the inability to view the attachment. In that case, let's just
// skip it for now.
- LogUtils.e(TAG, "== skip attachment %d, it is ineligible", att.mId);
+ LogUtils.e(LOG_TAG, "== skip attachment %d, it is ineligible", att.mId);
}
}
}
@@ -480,7 +625,7 @@ public class AttachmentService extends Service implements Runnable {
/*package*/ synchronized int downloadsForAccount(long accountId) {
int count = 0;
for (DownloadRequest req: mDownloadsInProgress.values()) {
- if (req.accountId == accountId) {
+ if (req.mAccountId == accountId) {
count++;
}
}
@@ -499,10 +644,10 @@ public class AttachmentService extends Service implements Runnable {
long now = System.currentTimeMillis();
for (DownloadRequest req: mDownloadsInProgress.values()) {
// Check how long it's been since receiving a callback
- long timeSinceCallback = now - req.lastCallbackTime;
+ long timeSinceCallback = now - req.mLastCallbackTime;
if (timeSinceCallback > CALLBACK_TIMEOUT) {
- if (LogUtils.isLoggable(TAG, LogUtils.DEBUG)) {
- LogUtils.d(TAG, "== Download of " + req.attachmentId + " timed out");
+ if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
+ LogUtils.d(LOG_TAG, "== Download of " + req.mAttachmentId + " timed out");
}
cancelDownload(req);
}
@@ -513,8 +658,8 @@ public class AttachmentService extends Service implements Runnable {
}
// If there are downloads in progress, reset alarm
if (!mDownloadsInProgress.isEmpty()) {
- if (LogUtils.isLoggable(TAG, LogUtils.DEBUG)) {
- LogUtils.d(TAG, "Reschedule watchdog...");
+ if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
+ LogUtils.d(LOG_TAG, "Reschedule watchdog...");
}
setWatchdogAlarm();
}
@@ -528,15 +673,15 @@ public class AttachmentService extends Service implements Runnable {
*/
/*package*/ synchronized boolean tryStartDownload(DownloadRequest req) {
EmailServiceProxy service = EmailServiceUtils.getServiceForAccount(
- AttachmentService.this, req.accountId);
+ AttachmentService.this, req.mAccountId);
// Do not download the same attachment multiple times
- boolean alreadyInProgress = mDownloadsInProgress.get(req.attachmentId) != null;
+ boolean alreadyInProgress = mDownloadsInProgress.get(req.mAttachmentId) != null;
if (alreadyInProgress) return false;
try {
- if (LogUtils.isLoggable(TAG, LogUtils.DEBUG)) {
- LogUtils.d(TAG, ">> Starting download for attachment #" + req.attachmentId);
+ if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
+ LogUtils.d(LOG_TAG, ">> Starting download for attachment #" + req.mAttachmentId);
}
startDownload(service, req);
} catch (RemoteException e) {
@@ -578,25 +723,25 @@ public class AttachmentService extends Service implements Runnable {
*/
private void startDownload(EmailServiceProxy service, DownloadRequest req)
throws RemoteException {
- req.startTime = System.currentTimeMillis();
- req.inProgress = true;
- mDownloadsInProgress.put(req.attachmentId, req);
- service.loadAttachment(mServiceCallback, req.accountId, req.attachmentId,
- req.priority != PRIORITY_FOREGROUND);
+ req.mStartTime = System.currentTimeMillis();
+ req.mInProgress = true;
+ mDownloadsInProgress.put(req.mAttachmentId, req);
+ service.loadAttachment(mServiceCallback, req.mAccountId, req.mAttachmentId,
+ req.mPriority != PRIORITY_FOREGROUND);
setWatchdogAlarm();
}
private void cancelDownload(DownloadRequest req) {
- LogUtils.d(TAG, "cancelDownload #%d", req.attachmentId);
- req.inProgress = false;
- mDownloadsInProgress.remove(req.attachmentId);
+ LogUtils.d(LOG_TAG, "cancelDownload #%d", req.mAttachmentId);
+ req.mInProgress = false;
+ mDownloadsInProgress.remove(req.mAttachmentId);
// Remove the download from our queue, and then decide whether or not to add it back.
remove(req);
- req.retryCount++;
- if (req.retryCount > CONNECTION_ERROR_MAX_RETRIES) {
- LogUtils.d(TAG, "too many failures, giving up");
+ req.mRetryCount++;
+ if (req.mRetryCount > CONNECTION_ERROR_MAX_RETRIES) {
+ LogUtils.d(LOG_TAG, "too many failures, giving up");
} else {
- LogUtils.d(TAG, "moving to end of queue, will retry");
+ LogUtils.d(LOG_TAG, "moving to end of queue, will retry");
// The time field of DownloadRequest is final, because it's unsafe to change it
// as long as the DownloadRequest is in the DownloadSet. It's needed for the
// comparator, so changing time would make the request unfindable.
@@ -636,27 +781,27 @@ public class AttachmentService extends Service implements Runnable {
if (statusCode == EmailServiceStatus.CONNECTION_ERROR) {
// If this needs to be retried, just process the queue again
if (req != null) {
- req.retryCount++;
- if (req.retryCount > CONNECTION_ERROR_MAX_RETRIES) {
- LogUtils.d(TAG, "Connection Error #%d, giving up", attachmentId);
+ req.mRetryCount++;
+ if (req.mRetryCount > CONNECTION_ERROR_MAX_RETRIES) {
+ LogUtils.d(LOG_TAG, "Connection Error #%d, giving up", attachmentId);
remove(req);
- } else if (req.retryCount > CONNECTION_ERROR_DELAY_THRESHOLD) {
+ } else if (req.mRetryCount > CONNECTION_ERROR_DELAY_THRESHOLD) {
// TODO: I'm not sure this is a great retry/backoff policy, but we're
// afraid of changing behavior too much in case something relies upon it.
// So now, for the first five errors, we'll retry immediately. For the next
// five tries, we'll add a ten second delay between each. After that, we'll
// give up.
- LogUtils.d(TAG, "ConnectionError #%d, retried %d times, adding delay",
- attachmentId, req.retryCount);
- req.inProgress = false;
- req.retryStartTime = SystemClock.elapsedRealtime() +
+ LogUtils.d(LOG_TAG, "ConnectionError #%d, retried %d times, adding delay",
+ attachmentId, req.mRetryCount);
+ req.mInProgress = false;
+ req.mRetryStartTime = SystemClock.elapsedRealtime() +
CONNECTION_ERROR_RETRY_MILLIS;
setWatchdogAlarm(CONNECTION_ERROR_RETRY_MILLIS);
} else {
- LogUtils.d(TAG, "ConnectionError #%d, retried %d times, adding delay",
- attachmentId, req.retryCount);
- req.inProgress = false;
- req.retryStartTime = 0;
+ LogUtils.d(LOG_TAG, "ConnectionError #%d, retried %d times, adding delay",
+ attachmentId, req.mRetryCount);
+ req.mInProgress = false;
+ req.mRetryStartTime = 0;
kick();
}
}
@@ -667,14 +812,14 @@ public class AttachmentService extends Service implements Runnable {
if (req != null) {
remove(req);
}
- if (LogUtils.isLoggable(TAG, LogUtils.DEBUG)) {
+ if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
long secs = 0;
if (req != null) {
- secs = (System.currentTimeMillis() - req.time) / 1000;
+ secs = (System.currentTimeMillis() - req.mTime) / 1000;
}
String status = (statusCode == EmailServiceStatus.SUCCESS) ? "Success" :
"Error " + statusCode;
- LogUtils.d(TAG, "<< Download finished for attachment #" + attachmentId + "; " + secs
+ LogUtils.d(LOG_TAG, "<< Download finished for attachment #" + attachmentId + "; " + secs
+ " seconds from request, status: " + status);
}
@@ -703,9 +848,9 @@ public class AttachmentService extends Service implements Runnable {
// try to send pending mail now (as mediated by MailService)
if ((req != null) &&
!Utility.hasUnloadedAttachments(mContext, attachment.mMessageKey)) {
- if (LogUtils.isLoggable(TAG, LogUtils.DEBUG)) {
- LogUtils.d(TAG, "== Downloads finished for outgoing msg #"
- + req.messageId);
+ if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
+ LogUtils.d(LOG_TAG, "== Downloads finished for outgoing msg #"
+ + req.mMessageId);
}
EmailServiceProxy service = EmailServiceUtils.getServiceForAccount(
mContext, accountId);
@@ -781,7 +926,7 @@ public class AttachmentService extends Service implements Runnable {
// Record status and progress
DownloadRequest req = mDownloadSet.getDownloadInProgress(attachmentId);
if (req != null) {
- if (LogUtils.isLoggable(TAG, LogUtils.DEBUG)) {
+ if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
String code;
switch(statusCode) {
case EmailServiceStatus.SUCCESS: code = "Success"; break;
@@ -789,14 +934,14 @@ public class AttachmentService extends Service implements Runnable {
default: code = Integer.toString(statusCode); break;
}
if (statusCode != EmailServiceStatus.IN_PROGRESS) {
- LogUtils.d(TAG, ">> Attachment status " + attachmentId + ": " + code);
- } else if (progress >= (req.lastProgress + 10)) {
- LogUtils.d(TAG, ">> Attachment progress %d: %d%%", attachmentId, progress);
+ LogUtils.d(LOG_TAG, ">> Attachment status " + attachmentId + ": " + code);
+ } else if (progress >= (req.mLastProgress + 10)) {
+ LogUtils.d(LOG_TAG, ">> Attachment progress %d: %d%%", attachmentId, progress);
}
}
- req.lastStatusCode = statusCode;
- req.lastProgress = progress;
- req.lastCallbackTime = System.currentTimeMillis();
+ req.mLastStatusCode = statusCode;
+ req.mLastProgress = progress;
+ req.mLastCallbackTime = System.currentTimeMillis();
Attachment attachment = Attachment.restoreAttachmentWithId(mContext, attachmentId);
if (attachment != null && statusCode == EmailServiceStatus.IN_PROGRESS) {
ContentValues values = new ContentValues();
@@ -836,8 +981,8 @@ public class AttachmentService extends Service implements Runnable {
/*package*/ boolean dequeue(long attachmentId) {
DownloadRequest req = mDownloadSet.findDownloadRequest(attachmentId);
if (req != null) {
- if (LogUtils.isLoggable(TAG, LogUtils.DEBUG)) {
- LogUtils.d(TAG, "Dequeued attachmentId: " + attachmentId);
+ if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
+ LogUtils.d(LOG_TAG, "Dequeued attachmentId: " + attachmentId);
}
mDownloadSet.remove(req);
return true;
@@ -982,8 +1127,8 @@ public class AttachmentService extends Service implements Runnable {
if (accountStorage < perAccountMaxStorage) {
return true;
} else {
- if (LogUtils.isLoggable(TAG, LogUtils.DEBUG)) {
- LogUtils.d(TAG, ">> Prefetch not allowed for account " + account.mId + "; used " +
+ if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
+ LogUtils.d(LOG_TAG, ">> Prefetch not allowed for account " + account.mId + "; used " +
accountStorage + ", limit " + perAccountMaxStorage);
}
return false;
@@ -994,7 +1139,7 @@ public class AttachmentService extends Service implements Runnable {
public void run() {
// These fields are only used within the service thread
mContext = this;
- mConnectivityManager = new EmailConnectivityManager(this, TAG);
+ mConnectivityManager = new EmailConnectivityManager(this, LOG_TAG);
mAccountManagerStub = new AccountManagerStub(this);
// Run through all attachments in the database that require download and add them to
@@ -1004,7 +1149,7 @@ public class AttachmentService extends Service implements Runnable {
EmailContent.ID_PROJECTION, "(" + AttachmentColumns.FLAGS + " & ?) != 0",
new String[] {Integer.toString(mask)}, null);
try {
- LogUtils.d(TAG, "Count: " + c.getCount());
+ LogUtils.d(LOG_TAG, "Count: " + c.getCount());
while (c.moveToNext()) {
Attachment attachment = Attachment.restoreAttachmentWithId(
this, c.getLong(EmailContent.ID_PROJECTION_COLUMN));
@@ -1033,7 +1178,7 @@ public class AttachmentService extends Service implements Runnable {
}
mDownloadSet.processQueue();
if (mDownloadSet.isEmpty()) {
- LogUtils.d(TAG, "*** All done; shutting down service");
+ LogUtils.d(LOG_TAG, "*** All done; shutting down service");
stopSelf();
break;
}
@@ -1101,10 +1246,10 @@ public class AttachmentService extends Service implements Runnable {
// First, start up any required downloads, in priority order
while (iterator.hasNext()) {
DownloadRequest req = iterator.next();
- pw.println(" Account: " + req.accountId + ", Attachment: " + req.attachmentId);
- pw.println(" Priority: " + req.priority + ", Time: " + req.time +
- (req.inProgress ? " [In progress]" : ""));
- Attachment att = Attachment.restoreAttachmentWithId(this, req.attachmentId);
+ pw.println(" Account: " + req.mAccountId + ", Attachment: " + req.mAttachmentId);
+ pw.println(" Priority: " + req.mPriority + ", Time: " + req.mTime +
+ (req.mInProgress ? " [In progress]" : ""));
+ Attachment att = Attachment.restoreAttachmentWithId(this, req.mAttachmentId);
if (att == null) {
pw.println(" Attachment not in database?");
} else if (att.mFileName != null) {
@@ -1127,14 +1272,14 @@ public class AttachmentService extends Service implements Runnable {
}
pw.println(" Size: " + att.mSize);
}
- if (req.inProgress) {
- pw.println(" Status: " + req.lastStatusCode + ", Progress: " +
- req.lastProgress);
- pw.println(" Started: " + req.startTime + ", Callback: " +
- req.lastCallbackTime);
- pw.println(" Elapsed: " + ((time - req.startTime) / 1000L) + "s");
- if (req.lastCallbackTime > 0) {
- pw.println(" CB: " + ((time - req.lastCallbackTime) / 1000L) + "s");
+ if (req.mInProgress) {
+ pw.println(" Status: " + req.mLastStatusCode + ", Progress: " +
+ req.mLastProgress);
+ pw.println(" Started: " + req.mStartTime + ", Callback: " +
+ req.mLastCallbackTime);
+ pw.println(" Elapsed: " + ((time - req.mStartTime) / 1000L) + "s");
+ if (req.mLastCallbackTime > 0) {
+ pw.println(" CB: " + ((time - req.mLastCallbackTime) / 1000L) + "s");
}
}
}
diff --git a/tests/src/com/android/email/service/AttachmentServiceTests.java b/tests/src/com/android/email/service/AttachmentServiceTests.java
new file mode 100644
index 000000000..044721535
--- /dev/null
+++ b/tests/src/com/android/email/service/AttachmentServiceTests.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2014 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.email.service;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.emailcommon.provider.EmailContent;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests of the AttachmentService
+ *
+ * You can run this entire test case with:
+ * runtest -c com.android.email.service.AttachmentServiceTests email
+ */
+@SmallTest
+public class AttachmentServiceTests extends TestCase {
+
+ public void testDownloadRequestIsEquals() {
+ final AttachmentService.DownloadRequest dr =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 1);
+ final AttachmentService.DownloadRequest dr2 =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 2);
+ assertTrue(dr.equals(dr));
+ assertFalse(dr.equals(dr2));
+ }
+
+ public void testDownloadQueueEmptyQueue() {
+ final AttachmentService.DownloadQueue dq = new AttachmentService.DownloadQueue();
+ assertEquals(0, dq.getSize());
+ assertTrue(dq.isEmpty());
+ }
+
+ public void testDownloadQueueAddRequest() {
+ final AttachmentService.DownloadQueue dq = new AttachmentService.DownloadQueue();
+ final AttachmentService.DownloadRequest dr =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 1);
+ final boolean result = dq.addRequest(dr);
+ assertTrue(result);
+ assertEquals(1, dq.getSize());
+ assertFalse(dq.isEmpty());
+ }
+
+ public void testDownloadQueueAddRequestNull() {
+ final AttachmentService.DownloadQueue dq = new AttachmentService.DownloadQueue();
+ final boolean result = dq.addRequest(null);
+ assertFalse(result);
+ assertEquals(0, dq.getSize());
+ assertTrue(dq.isEmpty());
+ }
+
+ public void testDownloadQueueAddRequestExisting() {
+ final AttachmentService.DownloadQueue dq = new AttachmentService.DownloadQueue();
+ final AttachmentService.DownloadRequest dr =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 1);
+ boolean result = dq.addRequest(dr);
+ assertTrue(result);
+ assertEquals(1, dq.getSize());
+ assertFalse(dq.isEmpty());
+
+ // Now try to add the same one again. The queue should remain the same size.
+ result = dq.addRequest(dr);
+ assertTrue(result);
+ assertEquals(1, dq.getSize());
+ assertFalse(dq.isEmpty());
+ }
+
+ public void testDownloadQueueRemoveRequest() {
+ final AttachmentService.DownloadQueue dq = new AttachmentService.DownloadQueue();
+ final AttachmentService.DownloadRequest dr =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 1);
+ boolean result = dq.addRequest(dr);
+ assertTrue(result);
+ assertEquals(1, dq.getSize());
+ assertFalse(dq.isEmpty());
+
+ // Now remove the request and check the status of the queue
+ result = dq.removeRequest(dr);
+ assertTrue(result);
+
+ // The queue should be empty.
+ assertEquals(0, dq.getSize());
+ assertTrue(dq.isEmpty());
+ }
+
+ public void testDownloadQueueRemoveRequestNull() {
+ final AttachmentService.DownloadQueue dq = new AttachmentService.DownloadQueue();
+ final AttachmentService.DownloadRequest dr =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 1);
+ boolean result = dq.addRequest(dr);
+ assertTrue(result);
+ assertEquals(dq.getSize(), 1);
+ assertFalse(dq.isEmpty());
+
+ // Now remove the request and check the status of the queue
+ result = dq.removeRequest(null);
+ assertTrue(result);
+
+ // The queue should still have 1.
+ assertEquals(1, dq.getSize());
+ assertFalse(dq.isEmpty());
+ }
+
+ public void testDownloadQueueRemoveRequestDoesNotExist() {
+ final AttachmentService.DownloadQueue dq = new AttachmentService.DownloadQueue();
+ final AttachmentService.DownloadRequest dr =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 1);
+ boolean result = dq.addRequest(dr);
+ assertTrue(result);
+ assertEquals(1, dq.getSize());
+ assertFalse(dq.isEmpty());
+
+ // Generate a new request and try to remove it.
+ result = dq.removeRequest(new AttachmentService.DownloadRequest(
+ AttachmentService.PRIORITY_FOREGROUND, 2));
+ assertFalse(result);
+
+ // The queue should still have 1.
+ assertEquals(1, dq.getSize());
+ assertFalse(dq.isEmpty());
+ }
+
+ public void testDownloadQueueFindRequestById() {
+ final AttachmentService.DownloadQueue dq = new AttachmentService.DownloadQueue();
+ final AttachmentService.DownloadRequest dr =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 1);
+ final boolean result = dq.addRequest(dr);
+ assertTrue(result);
+
+ final AttachmentService.DownloadRequest drResult = dq.findRequestById(1);
+ assertNotNull(drResult);
+
+ // Now let's make sure that these objects are the same
+ assertEquals(dr, drResult);
+ }
+
+ public void testDownloadQueueFindRequestByIdInvalidId() {
+ final AttachmentService.DownloadQueue dq = new AttachmentService.DownloadQueue();
+ final AttachmentService.DownloadRequest dr =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 1);
+ final boolean result = dq.addRequest(dr);
+ assertTrue(result);
+
+ final AttachmentService.DownloadRequest drResult = dq.findRequestById(-1);
+ assertNull(drResult);
+ }
+
+ public void testDownloadQueueFindRequestByIdUnknownId() {
+ final AttachmentService.DownloadQueue dq = new AttachmentService.DownloadQueue();
+ final AttachmentService.DownloadRequest dr =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 1);
+ final boolean result = dq.addRequest(dr);
+ assertTrue(result);
+
+ final AttachmentService.DownloadRequest drResult = dq.findRequestById(5);
+ assertNull(drResult);
+ }
+
+ /**
+ * This is just to test the FIFOness of our queue. We test priorities in a latter
+ * test case.
+ */
+ public void testDownloadQueueGetNextRequest() {
+ final AttachmentService.DownloadQueue dq = new AttachmentService.DownloadQueue();
+ final AttachmentService.DownloadRequest dr =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 1);
+ boolean result = dq.addRequest(dr);
+ assertTrue(result);
+
+ final AttachmentService.DownloadRequest dr2 =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_SEND_MAIL, 2);
+ result = dq.addRequest(dr2);
+ assertTrue(result);
+
+ final AttachmentService.DownloadRequest dr3 =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_BACKGROUND, 3);
+ result = dq.addRequest(dr3);
+ assertTrue(result);
+
+ assertEquals(3, dq.getSize());
+ assertFalse(dq.isEmpty());
+
+ AttachmentService.DownloadRequest drResult = dq.getNextRequest();
+ assertEquals(dr, drResult);
+ assertEquals(2, dq.getSize());
+ assertFalse(dq.isEmpty());
+
+ drResult = dq.getNextRequest();
+ assertEquals(dr2, drResult);
+ assertEquals(1, dq.getSize());
+ assertFalse(dq.isEmpty());
+
+ drResult = dq.getNextRequest();
+ assertEquals(dr3, drResult);
+ assertEquals(0, dq.getSize());
+ assertTrue(dq.isEmpty());
+ }
+
+ public void testDownloadQueueGetNextRequestEmptyQueue() {
+ final AttachmentService.DownloadQueue dq = new AttachmentService.DownloadQueue();
+ AttachmentService.DownloadRequest drResult = dq.getNextRequest();
+ assertNull(drResult);
+ }
+
+ public void testDownloadQueueSizeReporting() {
+ final AttachmentService.DownloadQueue dq = new AttachmentService.DownloadQueue();
+
+ // Start adding some download request objects, note that the empty queue case has been
+ // tested in above.
+ final AttachmentService.DownloadRequest dr =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 1);
+
+ // Add the first DownloadRequest to the queue
+ boolean result = dq.addRequest(dr);
+ assertTrue(result);
+ assertEquals(1, dq.getSize());
+ assertFalse(dq.isEmpty());
+
+ // Add the same one again, the size should be the same.
+ result = dq.addRequest(dr);
+ assertTrue(result);
+ assertEquals(1, dq.getSize());
+ assertFalse(dq.isEmpty());
+
+ final AttachmentService.DownloadRequest dr2 =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 2);
+
+ // Add another DownloadRequest
+ result = dq.addRequest(dr2);
+ assertTrue(result);
+ assertEquals(2, dq.getSize());
+ assertFalse(dq.isEmpty());
+
+ final AttachmentService.DownloadRequest dr3 =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 3);
+
+ result = dq.addRequest(dr3);
+ assertTrue(result);
+ assertEquals(3, dq.getSize());
+ assertFalse(dq.isEmpty());
+
+ // Remove a request and check new size.
+ AttachmentService.DownloadRequest returnRequest = dq.getNextRequest();
+ assertNotNull(returnRequest);
+ assertEquals(2, dq.getSize());
+ assertFalse(dq.isEmpty());
+
+ final AttachmentService.DownloadRequest dr4 =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 4);
+
+ // Adding the last DownloadRequest
+ result = dq.addRequest(dr4);
+ assertTrue(result);
+ assertEquals(3, dq.getSize());
+ assertFalse(dq.isEmpty());
+
+ // Start removing all the final requests and check sizes.
+ returnRequest = dq.getNextRequest();
+ assertNotNull(returnRequest);
+ assertEquals(2, dq.getSize());
+ assertFalse(dq.isEmpty());
+
+ returnRequest = dq.getNextRequest();
+ assertNotNull(returnRequest);
+ assertEquals(1, dq.getSize());
+ assertFalse(dq.isEmpty());
+
+ returnRequest = dq.getNextRequest();
+ assertNotNull(returnRequest);
+ assertEquals(0, dq.getSize());
+ assertTrue(dq.isEmpty());
+ }
+
+ /**
+ * Insert DownloadRequest obje cts in a random priority sequence and make sure that
+ * The highest priority items come out of the queue first.
+ */
+ public void testDownloadQueueTestPriority() {
+ final AttachmentService.DownloadQueue dq = new AttachmentService.DownloadQueue();
+
+ // Start adding some download request objects, note that the empty queue case has been
+ // tested in above.
+ final AttachmentService.DownloadRequest dr =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 1);
+ boolean result = dq.addRequest(dr);
+ assertTrue(result);
+
+ final AttachmentService.DownloadRequest dr2 =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_BACKGROUND, 2);
+ result = dq.addRequest(dr2);
+ assertTrue(result);
+
+ final AttachmentService.DownloadRequest dr3 =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_SEND_MAIL, 3);
+ result = dq.addRequest(dr3);
+ assertTrue(result);
+
+ final AttachmentService.DownloadRequest dr4 =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_SEND_MAIL, 4);
+ result = dq.addRequest(dr4);
+ assertTrue(result);
+
+ final AttachmentService.DownloadRequest dr5 =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 5);
+ result = dq.addRequest(dr5);
+ assertTrue(result);
+
+ final AttachmentService.DownloadRequest dr6 =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_BACKGROUND, 6);
+ result = dq.addRequest(dr6);
+ assertTrue(result);
+
+ // Set the priority to the highest possible value and everything show be
+ // in descending order.
+ int lastPriority = AttachmentService.PRIORITY_HIGHEST;
+ for (int i = 0; i < dq.getSize(); i++){
+ final AttachmentService.DownloadRequest returnRequest = dq.getNextRequest();
+ assertNotNull(returnRequest);
+ final int requestPriority = returnRequest.mPriority;
+ // The values should be going up or staying the same...indicating a lower priority
+ assertTrue(requestPriority >= lastPriority);
+ lastPriority = requestPriority;
+ }
+ }
+
+ /**
+ * Insert DownloadRequest objects in a random time based sequence and make sure that
+ * The oldest requests come out of the queue first.
+ */
+ public void testDownloadQueueTestDate() {
+ final AttachmentService.DownloadQueue dq = new AttachmentService.DownloadQueue();
+
+ // Start adding some unique attachments but with the same priority
+ final AttachmentService.DownloadRequest dr =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 1);
+ boolean result = dq.addRequest(dr);
+ assertTrue(result);
+
+ final AttachmentService.DownloadRequest dr2 =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 2);
+ result = dq.addRequest(dr2);
+ assertTrue(result);
+
+ final AttachmentService.DownloadRequest dr3 =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 3);
+ result = dq.addRequest(dr3);
+ assertTrue(result);
+
+ final AttachmentService.DownloadRequest dr4 =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 4);
+ result = dq.addRequest(dr4);
+ assertTrue(result);
+
+ final AttachmentService.DownloadRequest dr5 =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 5);
+ result = dq.addRequest(dr5);
+ assertTrue(result);
+
+ final AttachmentService.DownloadRequest dr6 =
+ new AttachmentService.DownloadRequest(AttachmentService.PRIORITY_FOREGROUND, 6);
+ result = dq.addRequest(dr6);
+ assertTrue(result);
+
+ // The output should return requests in increasing time.
+ long lastTime = 0;
+ for (int i = 0; i < dq.getSize(); i++){
+ final AttachmentService.DownloadRequest returnRequest = dq.getNextRequest();
+ assertNotNull(returnRequest);
+ final long requestTime = returnRequest.mTime;
+ // The time should be going up.
+ assertTrue(requestTime >= lastTime);
+ lastTime = requestTime;
+ }
+ }
+}