Add watchdog to AttachmentDownloadService
* Detect attachment downloads that have stalled and restart them * Catch a couple of cases in which we weren't sending callbacks Bug: 3122242 Change-Id: Id2bfd3b26182004b301cf8665f4feb6e62b98b73
This commit is contained in:
parent
2201b38fe3
commit
3bbc690600
|
@ -232,6 +232,9 @@
|
|||
<receiver android:name="com.android.exchange.MailboxAlarmReceiver"/>
|
||||
<!--EXCHANGE-REMOVE-SECTION-END-->
|
||||
|
||||
<receiver android:name=".service.AttachmentDownloadService$Watchdog"
|
||||
android:enabled="true"/>
|
||||
|
||||
<receiver android:name=".service.EmailBroadcastReceiver" android:enabled="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
|
||||
package com.android.email.service;
|
||||
|
||||
import com.android.email.Controller.ControllerService;
|
||||
import com.android.email.Email;
|
||||
import com.android.email.ExchangeUtils.NullEmailService;
|
||||
import com.android.email.NotificationController;
|
||||
import com.android.email.Utility;
|
||||
import com.android.email.Controller.ControllerService;
|
||||
import com.android.email.ExchangeUtils.NullEmailService;
|
||||
import com.android.email.provider.AttachmentProvider;
|
||||
import com.android.email.provider.EmailContent;
|
||||
import com.android.email.provider.EmailContent.Account;
|
||||
|
@ -28,7 +28,10 @@ import com.android.email.provider.EmailContent.Attachment;
|
|||
import com.android.email.provider.EmailContent.Message;
|
||||
import com.android.exchange.ExchangeService;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
@ -45,12 +48,17 @@ import java.util.Comparator;
|
|||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class AttachmentDownloadService extends Service implements Runnable {
|
||||
public static final String TAG = "AttachmentService";
|
||||
|
||||
// Our idle time, waiting for notifications; this is something of a failsafe
|
||||
private static final int PROCESS_QUEUE_WAIT_TIME = 30 * ((int)DateUtils.MINUTE_IN_MILLIS);
|
||||
// How often our watchdog checks for callback timeouts
|
||||
private static final int WATCHDOG_CHECK_INTERVAL = 15 * ((int)DateUtils.SECOND_IN_MILLIS);
|
||||
// How long we'll wait for a callback before canceling a download and retrying
|
||||
private static final int CALLBACK_TIMEOUT = 30 * ((int)DateUtils.SECOND_IN_MILLIS);
|
||||
|
||||
private static final int PRIORITY_NONE = -1;
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -71,12 +79,29 @@ public class AttachmentDownloadService extends Service implements Runnable {
|
|||
|
||||
/*package*/ Context mContext;
|
||||
/*package*/ final DownloadSet mDownloadSet = new DownloadSet(new DownloadComparator());
|
||||
|
||||
private final HashMap<Long, Class<? extends Service>> mAccountServiceMap =
|
||||
new HashMap<Long, Class<? extends Service>>();
|
||||
private final ServiceCallback mServiceCallback = new ServiceCallback();
|
||||
private final Object mLock = new Object();
|
||||
private volatile boolean mStop = false;
|
||||
|
||||
|
||||
/**
|
||||
* Watchdog alarm receiver; responsible for making sure that downloads in progress are not
|
||||
* stalled, as determined by the timing of the most recent service callback
|
||||
*/
|
||||
public static class Watchdog extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(final Context context, Intent intent) {
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
watchdogAlarm();
|
||||
}
|
||||
}, "AttachmentDownloadService Watchdog").start();
|
||||
}
|
||||
}
|
||||
|
||||
public static class DownloadRequest {
|
||||
final int priority;
|
||||
final long time;
|
||||
|
@ -149,6 +174,8 @@ public class AttachmentDownloadService extends Service implements Runnable {
|
|||
*/
|
||||
/*package*/ class DownloadSet extends TreeSet<DownloadRequest> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private PendingIntent mWatchdogPendingIntent;
|
||||
private AlarmManager mAlarmManager;
|
||||
|
||||
/*package*/ DownloadSet(Comparator<? super DownloadRequest> comparator) {
|
||||
super(comparator);
|
||||
|
@ -157,8 +184,8 @@ public class AttachmentDownloadService extends Service implements Runnable {
|
|||
/**
|
||||
* Maps attachment id to DownloadRequest
|
||||
*/
|
||||
/*package*/ final HashMap<Long, DownloadRequest> mDownloadsInProgress =
|
||||
new HashMap<Long, DownloadRequest>();
|
||||
/*package*/ final ConcurrentHashMap<Long, DownloadRequest> mDownloadsInProgress =
|
||||
new ConcurrentHashMap<Long, DownloadRequest>();
|
||||
|
||||
/**
|
||||
* onChange is called by the AttachmentReceiver upon receipt of a valid notification from
|
||||
|
@ -260,6 +287,67 @@ public class AttachmentDownloadService extends Service implements Runnable {
|
|||
return count;
|
||||
}
|
||||
|
||||
private void onWatchdogAlarm() {
|
||||
long now = System.currentTimeMillis();
|
||||
for (DownloadRequest req: mDownloadsInProgress.values()) {
|
||||
// Check how long it's been since receiving a callback
|
||||
long timeSinceCallback = now - req.lastCallbackTime;
|
||||
if (timeSinceCallback > CALLBACK_TIMEOUT) {
|
||||
if (Email.DEBUG) {
|
||||
Log.d(TAG, "== , Download of " + req.attachmentId +
|
||||
" timed out");
|
||||
}
|
||||
mDownloadsInProgress.remove(req);
|
||||
// STOPSHIP Remove this before ship
|
||||
} else if (Email.DEBUG) {
|
||||
Log.d(TAG, "== , Download of " + req.attachmentId +
|
||||
" last callback " + (timeSinceCallback/1000) + " secs ago");
|
||||
}
|
||||
}
|
||||
// If there are downloads in progress, reset alarm
|
||||
if (mDownloadsInProgress.isEmpty()) {
|
||||
if (mAlarmManager != null && mWatchdogPendingIntent != null) {
|
||||
mAlarmManager.cancel(mWatchdogPendingIntent);
|
||||
}
|
||||
}
|
||||
// Check whether we can start new downloads...
|
||||
processQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the work of starting an attachment download using the EmailService interface, and
|
||||
* set our watchdog alarm
|
||||
*
|
||||
* @param serviceClass the class that will attempt the download
|
||||
* @param req the DownloadRequest
|
||||
* @throws RemoteException
|
||||
*/
|
||||
private void startDownload(Class<? extends Service> serviceClass, DownloadRequest req)
|
||||
throws RemoteException {
|
||||
File file = AttachmentProvider.getAttachmentFilename(mContext, req.accountId,
|
||||
req.attachmentId);
|
||||
req.startTime = System.currentTimeMillis();
|
||||
req.inProgress = true;
|
||||
mDownloadsInProgress.put(req.attachmentId, req);
|
||||
if (serviceClass.equals(NullEmailService.class)) return;
|
||||
// Now, call the service
|
||||
EmailServiceProxy proxy =
|
||||
new EmailServiceProxy(mContext, serviceClass, mServiceCallback);
|
||||
proxy.loadAttachment(req.attachmentId, file.getAbsolutePath(),
|
||||
AttachmentProvider.getAttachmentUri(req.accountId, req.attachmentId)
|
||||
.toString());
|
||||
// Lazily initialize our (reusable) pending intent
|
||||
if (mWatchdogPendingIntent == null) {
|
||||
Intent alarmIntent = new Intent(mContext, Watchdog.class);
|
||||
mWatchdogPendingIntent = PendingIntent.getBroadcast(mContext, 0, alarmIntent, 0);
|
||||
mAlarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
|
||||
}
|
||||
// Set the alarm
|
||||
mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
|
||||
System.currentTimeMillis() + WATCHDOG_CHECK_INTERVAL, WATCHDOG_CHECK_INTERVAL,
|
||||
mWatchdogPendingIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to execute the DownloadRequest, enforcing the maximum downloads per account
|
||||
* parameter
|
||||
|
@ -277,23 +365,11 @@ public class AttachmentDownloadService extends Service implements Runnable {
|
|||
}
|
||||
Class<? extends Service> serviceClass = getServiceClassForAccount(req.accountId);
|
||||
if (serviceClass == null) return false;
|
||||
EmailServiceProxy proxy =
|
||||
new EmailServiceProxy(mContext, serviceClass, mServiceCallback);
|
||||
try {
|
||||
File file = AttachmentProvider.getAttachmentFilename(mContext, req.accountId,
|
||||
req.attachmentId);
|
||||
if (Email.DEBUG) {
|
||||
Log.d(TAG, ">> Starting download for attachment #" + req.attachmentId);
|
||||
}
|
||||
// Don't actually run the load if this is the NullEmailService (used in unit tests)
|
||||
if (!serviceClass.equals(NullEmailService.class)) {
|
||||
req.startTime = System.currentTimeMillis();
|
||||
proxy.loadAttachment(req.attachmentId, file.getAbsolutePath(),
|
||||
AttachmentProvider.getAttachmentUri(req.accountId, req.attachmentId)
|
||||
.toString());
|
||||
}
|
||||
mDownloadsInProgress.put(req.attachmentId, req);
|
||||
req.inProgress = true;
|
||||
startDownload(serviceClass, req);
|
||||
} catch (RemoteException e) {
|
||||
// TODO: Consider whether we need to do more in this case...
|
||||
// For now, fix up our data to reflect the failure
|
||||
|
@ -543,6 +619,12 @@ public class AttachmentDownloadService extends Service implements Runnable {
|
|||
return false;
|
||||
}
|
||||
|
||||
public static void watchdogAlarm() {
|
||||
if (sRunningService != null) {
|
||||
sRunningService.mDownloadSet.onWatchdogAlarm();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called directly by EmailProvider whenever an attachment is inserted or changed
|
||||
* @param id the attachment's id
|
||||
|
|
|
@ -167,6 +167,14 @@ public class EmailServiceProxy implements IEmailService {
|
|||
if (mCallback != null) mService.setCallback(mCallback);
|
||||
mService.loadAttachment(attachmentId, destinationFile, contentUriString);
|
||||
} catch (RemoteException e) {
|
||||
try {
|
||||
// Try to send a callback (if set)
|
||||
if (mCallback != null) {
|
||||
mCallback.loadAttachmentStatus(-1, attachmentId,
|
||||
EmailServiceStatus.REMOTE_EXCEPTION, 0);
|
||||
}
|
||||
} catch (RemoteException e1) {
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -952,7 +952,12 @@ public class EasSyncService extends AbstractSyncService {
|
|||
protected void loadAttachment(PartRequest req) throws IOException {
|
||||
Attachment att = req.mAttachment;
|
||||
Message msg = Message.restoreMessageWithId(mContext, att.mMessageKey);
|
||||
doProgressCallback(msg.mId, att.mId, 0);
|
||||
if (msg == null) {
|
||||
doStatusCallback(att.mMessageKey, att.mId, EmailServiceStatus.MESSAGE_NOT_FOUND);
|
||||
return;
|
||||
} else {
|
||||
doProgressCallback(msg.mId, att.mId, 0);
|
||||
}
|
||||
|
||||
String cmd = "GetAttachment&AttachmentName=" + att.mLocation;
|
||||
HttpResponse res = sendHttpClientPost(cmd, null, COMMAND_TIMEOUT);
|
||||
|
|
Loading…
Reference in New Issue