Merge "Introduction of DownloadQueue and associated tests." into ub-mail-master
This commit is contained in:
commit
825aa02a6f
@ -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);
|
||||
|
@ -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;
|
||||
/**
|
||||
* 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;
|
||||
|
||||
private DownloadRequest(Context context, Attachment attachment) {
|
||||
attachmentId = attachment.mId;
|
||||
Message msg = Message.restoreMessageWithId(context, attachment.mMessageKey);
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
390
tests/src/com/android/email/service/AttachmentServiceTests.java
Normal file
390
tests/src/com/android/email/service/AttachmentServiceTests.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user