diff options
author | Martin Hibdon <mhibdon@google.com> | 2014-03-04 13:58:10 -0800 |
---|---|---|
committer | The Android Automerger <android-build@google.com> | 2014-03-06 11:16:10 -0800 |
commit | 5cb88e822f4cd268756f872f206df32731ca4b99 (patch) | |
tree | f0196f22f8ac2ec827086c8b348061380dc59589 | |
parent | f643f5ddf47b3cf782f449c91090a9a9aadb2b63 (diff) | |
download | Exchange-5cb88e822f4cd268756f872f206df32731ca4b99.tar.gz |
Make sending an EasOperation
I needed to make some changes to the EasOperation to
allow additional functionality to be put in the child
classes:
* Now child classes can override handleHttpError to
change the result code based upon the http response.
* Child classes can override onRequestMade to do
any cleanup that must be done. This is needed because
when sending a message, we need to put the message info
into a temporary file. Only after sending the httpEntity
can we delete that file.
Change-Id: I5554393d521116ce28e396a90c8ddffd48b9a3d3
-rw-r--r-- | src/com/android/exchange/adapter/SendMailParser.java | 36 | ||||
-rw-r--r-- | src/com/android/exchange/eas/EasOperation.java | 19 | ||||
-rw-r--r-- | src/com/android/exchange/eas/EasOutboxSync.java (renamed from src/com/android/exchange/service/EasOutboxSyncHandler.java) | 450 | ||||
-rw-r--r-- | src/com/android/exchange/service/EasService.java | 4 | ||||
-rw-r--r-- | src/com/android/exchange/service/EmailSyncAdapterService.java | 55 |
5 files changed, 290 insertions, 274 deletions
diff --git a/src/com/android/exchange/adapter/SendMailParser.java b/src/com/android/exchange/adapter/SendMailParser.java new file mode 100644 index 00000000..b205e09a --- /dev/null +++ b/src/com/android/exchange/adapter/SendMailParser.java @@ -0,0 +1,36 @@ +package com.android.exchange.adapter; + +import java.io.IOException; +import java.io.InputStream; + +public class SendMailParser extends Parser { + private final int mStartTag; + private int mStatus; + + public SendMailParser(final InputStream in, final int startTag) throws IOException { + super(in); + mStartTag = startTag; + } + + public int getStatus() { + return mStatus; + } + + /** + * The only useful info in the SendMail response is the status; we capture and save it + */ + @Override + public boolean parse() throws IOException { + if (nextTag(START_DOCUMENT) != mStartTag) { + throw new IOException(); + } + while (nextTag(START_DOCUMENT) != END_DOCUMENT) { + if (tag == Tags.COMPOSE_STATUS) { + mStatus = getValueInt(); + } else { + skipTag(); + } + } + return true; + } +} diff --git a/src/com/android/exchange/eas/EasOperation.java b/src/com/android/exchange/eas/EasOperation.java index 9b0ab358..821aa430 100644 --- a/src/com/android/exchange/eas/EasOperation.java +++ b/src/com/android/exchange/eas/EasOperation.java @@ -277,7 +277,11 @@ public abstract class EasOperation { // Perform the HTTP request and handle exceptions. final EasResponse response; try { - response = mConnection.executeHttpUriRequest(makeRequest(), getTimeout()); + try { + response = mConnection.executeHttpUriRequest(makeRequest(), getTimeout()); + } finally { + onRequestMade(); + } } catch (final IOException e) { // If we were stopped, return the appropriate result code. switch (mConnection.getStoppedReason()) { @@ -335,7 +339,7 @@ public abstract class EasOperation { } result = responseResult; } else { - result = RESULT_OTHER_FAILURE; + result = handleHttpError(response.getStatus()); } // Non-negative results indicate success. Return immediately and bypass the error @@ -393,6 +397,17 @@ public abstract class EasOperation { return RESULT_TOO_MANY_REDIRECTS; } + protected void onRequestMade() { + // This can be overridden to do any cleanup that must happen after the request has + // been sent. It will always be called, regardless of the status of the request. + } + + protected int handleHttpError(final int httpStatus) { + // This function can be overriden if the child class needs to change the result code + // based on the http response status. + return RESULT_OTHER_FAILURE; + } + /** * Reset the protocol version to use for this connection. If it's changed, and our account is * persisted, also write back the changes to the DB. diff --git a/src/com/android/exchange/service/EasOutboxSyncHandler.java b/src/com/android/exchange/eas/EasOutboxSync.java index c2bff155..dcc9d442 100644 --- a/src/com/android/exchange/service/EasOutboxSyncHandler.java +++ b/src/com/android/exchange/eas/EasOutboxSync.java @@ -1,16 +1,13 @@ -package com.android.exchange.service; +package com.android.exchange.eas; -import android.content.ContentUris; import android.content.Context; -import android.database.Cursor; -import android.net.TrafficStats; import android.net.Uri; import android.text.format.DateUtils; import android.util.Log; -import com.android.emailcommon.TrafficFlags; import com.android.emailcommon.internet.Rfc822Output; import com.android.emailcommon.provider.Account; +import com.android.emailcommon.provider.Mailbox; import com.android.emailcommon.provider.EmailContent.Attachment; import com.android.emailcommon.provider.EmailContent.Body; import com.android.emailcommon.provider.EmailContent.BodyColumns; @@ -18,15 +15,15 @@ import com.android.emailcommon.provider.EmailContent.MailboxColumns; import com.android.emailcommon.provider.EmailContent.Message; import com.android.emailcommon.provider.EmailContent.MessageColumns; import com.android.emailcommon.provider.EmailContent.SyncColumns; -import com.android.emailcommon.provider.Mailbox; import com.android.emailcommon.utility.Utility; -import com.android.exchange.CommandStatusException.CommandStatus; +import com.android.exchange.CommandStatusException; import com.android.exchange.Eas; import com.android.exchange.EasResponse; -import com.android.exchange.adapter.Parser; -import com.android.exchange.adapter.Parser.EmptyStreamException; +import com.android.exchange.CommandStatusException.CommandStatus; +import com.android.exchange.adapter.SendMailParser; import com.android.exchange.adapter.Serializer; import com.android.exchange.adapter.Tags; +import com.android.exchange.adapter.Parser.EmptyStreamException; import com.android.mail.utils.LogUtils; import org.apache.http.HttpEntity; @@ -39,70 +36,204 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; -import java.security.cert.CertificateException; import java.util.ArrayList; -/** - * Performs an Exchange Outbox sync, i.e. sends all mail from the Outbox. - */ -public class EasOutboxSyncHandler extends EasServerConnection { +public class EasOutboxSync extends EasOperation { + // Value for a message's server id when sending fails. public static final int SEND_FAILED = 1; - - // WHERE clause to query for unsent messages. - // TODO: Is the SEND_FAILED check actually what we want? - public static final String MAILBOX_KEY_AND_NOT_SEND_FAILED = - MessageColumns.MAILBOX_KEY + "=? and (" + SyncColumns.SERVER_ID + " is null or " + - SyncColumns.SERVER_ID + "!=" + SEND_FAILED + ')'; - // This needs to be long enough to send the longest reasonable message, without being so long // as to effectively "hang" sending of mail. The standard 30 second timeout isn't long enough // for pictures and the like. For now, we'll use 15 minutes, in the knowledge that any socket // failure would probably generate an Exception before timing out anyway public static final long SEND_MAIL_TIMEOUT = 15 * DateUtils.MINUTE_IN_MILLIS; - private final Mailbox mMailbox; + public static final int RESULT_OK = 1; + public static final int RESULT_IO_ERROR = -100; + public static final int RESULT_ITEM_NOT_FOUND = -101; + public static final int RESULT_SEND_FAILED = -102; + + private final Message mMessage; + private final boolean mIsEas14; private final File mCacheDir; + private final SmartSendInfo mSmartSendInfo; + private final int mModeTag; + private File mTmpFile; + private FileInputStream mFileStream; - public EasOutboxSyncHandler(final Context context, final Account account, - final Mailbox mailbox) { + public EasOutboxSync(final Context context, final Account account, final Message message, + final boolean useSmartSend) { super(context, account); - mMailbox = mailbox; + mMessage = message; + mIsEas14 = (Double.parseDouble(mAccount.mProtocolVersion) >= + Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE); mCacheDir = context.getCacheDir(); + if (useSmartSend) { + mSmartSendInfo = SmartSendInfo.getSmartSendInfo(mContext, mAccount, mMessage); + } else { + mSmartSendInfo = null; + } + mModeTag = getModeTag(mSmartSendInfo); } - public void performSync() { - // Use SMTP flags for sending mail - TrafficStats.setThreadStatsTag(TrafficFlags.getSmtpFlags(mContext, mAccount)); - // Get a cursor to Outbox messages - final Cursor c = mContext.getContentResolver().query(Message.CONTENT_URI, - Message.CONTENT_PROJECTION, MAILBOX_KEY_AND_NOT_SEND_FAILED, - new String[] {Long.toString(mMailbox.mId)}, null); + @Override + protected String getCommand() { + String cmd = "SendMail"; + if (mSmartSendInfo != null) { + // In EAS 14, we don't send itemId and collectionId in the command + if (mIsEas14) { + cmd = mSmartSendInfo.isForward() ? "SmartForward" : "SmartReply"; + } else { + cmd = mSmartSendInfo.generateSmartSendCmd(); + } + } + // If we're not EAS 14, add our save-in-sent setting here + if (!mIsEas14) { + cmd += "&SaveInSent=T"; + } + return cmd; + } + + @Override + protected HttpEntity getRequestEntity() throws IOException { try { - // Loop through the messages, sending each one - while (c.moveToNext()) { - final Message message = new Message(); - message.restore(c); - if (Utility.hasUnloadedAttachments(mContext, message.mId)) { - // We'll just have to wait on this... - continue; - } + mTmpFile = File.createTempFile("eas_", "tmp", mCacheDir); + } catch (final IOException e) { + LogUtils.w(LOG_TAG, "IO error creating temp file"); + throw new IllegalStateException("Failure creating temp file"); + } + + if (!writeMessageToTempFile(mTmpFile, mMessage, mSmartSendInfo)) { + LogUtils.w(LOG_TAG, "IO error writing to temp file"); + throw new IllegalStateException("Failure writing to temp file"); + } + + try { + mFileStream = new FileInputStream(mTmpFile); + } catch (final FileNotFoundException e) { + LogUtils.w(LOG_TAG, "IO error creating fileInputStream"); + throw new IllegalStateException("Failure creating fileInputStream"); + } + final long fileLength = mTmpFile.length(); + final HttpEntity entity; + if (mIsEas14) { + entity = new SendMailEntity(mFileStream, fileLength, mModeTag, mMessage, + mSmartSendInfo); + } else { + entity = new InputStreamEntity(mFileStream, fileLength); + } + + return entity; + } + + @Override + protected int handleHttpError(int httpStatus) { + if (httpStatus == HttpStatus.SC_INTERNAL_SERVER_ERROR && mSmartSendInfo != null) { + // Let's retry without "smart" commands. + return RESULT_ITEM_NOT_FOUND; + } else { + return RESULT_OTHER_FAILURE; + } + } - // TODO: Fix -- how do we want to signal to UI that we started syncing? - // Note the entire callback mechanism here needs improving. - //sendMessageStatus(message.mId, null, EmailServiceStatus.IN_PROGRESS, 0); + @Override + protected void onRequestMade() { + try { + mFileStream.close(); + } catch (IOException e) { + LogUtils.w(LOG_TAG, "IOException closing fileStream %s", e); + } + if (mTmpFile != null && mTmpFile.exists()) { + mTmpFile.delete(); + } + } - if (!sendOneMessage(message, - SmartSendInfo.getSmartSendInfo(mContext, mAccount, message))) { - break; + @Override + protected int handleResponse(EasResponse response) throws IOException, CommandStatusException { + if (mIsEas14) { + try { + // Try to parse the result + final SendMailParser p = new SendMailParser(response.getInputStream(), mModeTag); + // If we get here, the SendMail failed; go figure + p.parse(); + // The parser holds the status + final int status = p.getStatus(); + if (CommandStatus.isNeedsProvisioning(status)) { + LogUtils.w(LOG_TAG, "Needs provisioning sending mail"); + return RESULT_PROVISIONING_ERROR; + } else if (status == CommandStatus.ITEM_NOT_FOUND && + mSmartSendInfo != null) { + // Let's retry without "smart" commands. + LogUtils.w(LOG_TAG, "Needs provisioning sending mail"); + return RESULT_ITEM_NOT_FOUND; } + + // TODO: Set syncServerId = SEND_FAILED in DB? + LogUtils.d(LOG_TAG, "General failure sending mail"); + return RESULT_SEND_FAILED; + } catch (final EmptyStreamException e) { + // This is actually fine; an empty stream means SendMail succeeded + LogUtils.d(LOG_TAG, "empty response sending mail"); + return RESULT_OK; + } catch (final IOException e) { + // Parsing failed in some other way. + LogUtils.w(LOG_TAG, "IOException sending mail"); + return RESULT_IO_ERROR; } + } else { + // FLAG: Do we need to parse results for earlier versions? + } + return RESULT_OK; + } + + /** + * Writes message to the temp file. + * @param tmpFile The temp file to use. + * @param message The {@link Message} to write. + * @param smartSendInfo The {@link SmartSendInfo} for this message send attempt. + * @return Whether we could successfully write the file. + */ + private boolean writeMessageToTempFile(final File tmpFile, final Message message, + final SmartSendInfo smartSendInfo) { + final FileOutputStream fileStream; + try { + fileStream = new FileOutputStream(tmpFile); + Log.d(LogUtils.TAG, "created outputstream"); + } catch (final FileNotFoundException e) { + Log.e(LogUtils.TAG, "Failed to create message file", e); + return false; + } + try { + final boolean smartSend = smartSendInfo != null; + final ArrayList<Attachment> attachments = + smartSend ? smartSendInfo.mRequiredAtts : null; + Rfc822Output.writeTo(mContext, message, fileStream, smartSend, true, attachments); + } catch (final Exception e) { + Log.e(LogUtils.TAG, "Failed to write message file", e); + return false; } finally { - // TODO: Some sort of sendMessageStatus() is needed here. - c.close(); + try { + fileStream.close(); + } catch (final IOException e) { + // should not happen + Log.e(LogUtils.TAG, "Failed to close file - should not happen", e); + } } + return true; + } + + private int getModeTag(final SmartSendInfo smartSendInfo) { + if (mIsEas14) { + if (smartSendInfo == null) { + return Tags.COMPOSE_SEND_MAIL; + } else if (smartSendInfo.isForward()) { + return Tags.COMPOSE_SMART_FORWARD; + } else { + return Tags.COMPOSE_SMART_REPLY; + } + } + return 0; } /** @@ -118,8 +249,8 @@ public class EasOutboxSyncHandler extends EasServerConnection { final boolean mIsReply; final ArrayList<Attachment> mRequiredAtts; - private SmartSendInfo(final String itemId, final String collectionId, final boolean isReply, - final ArrayList<Attachment> requiredAtts) { + private SmartSendInfo(final String itemId, final String collectionId, + final boolean isReply,ArrayList<Attachment> requiredAtts) { mItemId = itemId; mCollectionId = collectionId; mIsReply = isReply; @@ -354,221 +485,4 @@ public class EasOutboxSyncHandler extends EasServerConnection { s.end().end().done(); } } - - private static class SendMailParser extends Parser { - private final int mStartTag; - private int mStatus; - - public SendMailParser(final InputStream in, final int startTag) throws IOException { - super(in); - mStartTag = startTag; - } - - public int getStatus() { - return mStatus; - } - - /** - * The only useful info in the SendMail response is the status; we capture and save it - */ - @Override - public boolean parse() throws IOException { - if (nextTag(START_DOCUMENT) != mStartTag) { - throw new IOException(); - } - while (nextTag(START_DOCUMENT) != END_DOCUMENT) { - if (tag == Tags.COMPOSE_STATUS) { - mStatus = getValueInt(); - } else { - skipTag(); - } - } - return true; - } - } - - /** - * Attempt to send one message. - * @param message The message to send. - * @param smartSendInfo The SmartSendInfo for this message, or null if we don't have or don't - * want to use smart send. - * @return Whether or not sending this message succeeded. - * TODO: Improve how we handle the types of failures. I've left the old error codes in as TODOs - * for future reference. - */ - private boolean sendOneMessage(final Message message, final SmartSendInfo smartSendInfo) { - final File tmpFile; - try { - tmpFile = File.createTempFile("eas_", "tmp", mCacheDir); - } catch (final IOException e) { - return false; // TODO: Handle SyncStatus.FAILURE_IO; - } - - final EasResponse resp; - // Send behavior differs pre and post EAS14. - final boolean isEas14 = (Double.parseDouble(mAccount.mProtocolVersion) >= - Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE); - final int modeTag = getModeTag(isEas14, smartSendInfo); - try { - if (!writeMessageToTempFile(tmpFile, message, smartSendInfo)) { - return false; // TODO: Handle SyncStatus.FAILURE_IO; - } - - final FileInputStream fileStream; - try { - fileStream = new FileInputStream(tmpFile); - } catch (final FileNotFoundException e) { - return false; // TODO: Handle SyncStatus.FAILURE_IO; - } - try { - - final long fileLength = tmpFile.length(); - final HttpEntity entity; - if (isEas14) { - entity = new SendMailEntity(fileStream, fileLength, modeTag, message, - smartSendInfo); - } else { - entity = new InputStreamEntity(fileStream, fileLength); - } - - // Create the appropriate command. - String cmd = "SendMail"; - if (smartSendInfo != null) { - // In EAS 14, we don't send itemId and collectionId in the command - if (isEas14) { - cmd = smartSendInfo.isForward() ? "SmartForward" : "SmartReply"; - } else { - cmd = smartSendInfo.generateSmartSendCmd(); - } - } - // If we're not EAS 14, add our save-in-sent setting here - if (!isEas14) { - cmd += "&SaveInSent=T"; - } - // Finally, post SendMail to the server - try { - resp = sendHttpClientPost(cmd, entity, SEND_MAIL_TIMEOUT); - } catch (final IOException e) { - return false; // TODO: Handle SyncStatus.FAILURE_IO; - } catch (final CertificateException e) { - return false; - } - - } finally { - try { - fileStream.close(); - } catch (final IOException e) { - // TODO: Should we do anything here, or is it ok to just proceed? - } - } - } finally { - if (tmpFile.exists()) { - tmpFile.delete(); - } - } - - try { - final int code = resp.getStatus(); - if (code == HttpStatus.SC_OK) { - // HTTP OK before EAS 14 is a thumbs up; in EAS 14, we've got to parse - // the reply - if (isEas14) { - try { - // Try to parse the result - final SendMailParser p = new SendMailParser(resp.getInputStream(), modeTag); - // If we get here, the SendMail failed; go figure - p.parse(); - // The parser holds the status - final int status = p.getStatus(); - if (CommandStatus.isNeedsProvisioning(status)) { - return false; // TODO: Handle SyncStatus.FAILURE_SECURITY; - } else if (status == CommandStatus.ITEM_NOT_FOUND && - smartSendInfo != null) { - // Let's retry without "smart" commands. - return sendOneMessage(message, null); - } - // TODO: Set syncServerId = SEND_FAILED in DB? - return false; // TODO: Handle SyncStatus.FAILURE_MESSAGE; - } catch (final EmptyStreamException e) { - // This is actually fine; an empty stream means SendMail succeeded - } catch (final IOException e) { - // Parsing failed in some other way. - return false; // TODO: Handle SyncStatus.FAILURE_IO; - } - } - } else if (code == HttpStatus.SC_INTERNAL_SERVER_ERROR && smartSendInfo != null) { - // Let's retry without "smart" commands. - return sendOneMessage(message, null); - } else { - if (resp.isAuthError()) { - LogUtils.d(LogUtils.TAG, "Got auth error from server during outbox sync"); - return false; // TODO: Handle SyncStatus.FAILURE_LOGIN; - } else if (resp.isProvisionError()) { - LogUtils.d(LogUtils.TAG, "Got provision error from server during outbox sync."); - return false; // TODO: Handle SyncStatus.FAILURE_SECURITY; - } else { - // TODO: Handle some other error - LogUtils.d(LogUtils.TAG, - "Got other HTTP error from server during outbox sync: %d", code); - return false; - } - } - } finally { - resp.close(); - } - - // If we manage to get here, the message sent successfully. Hooray! - // Delete the sent message. - mContext.getContentResolver().delete( - ContentUris.withAppendedId(Message.CONTENT_URI, message.mId), null, null); - return true; - } - - /** - * Writes message to the temp file. - * @param tmpFile The temp file to use. - * @param message The {@link Message} to write. - * @param smartSendInfo The {@link SmartSendInfo} for this message send attempt. - * @return Whether we could successfully write the file. - */ - private boolean writeMessageToTempFile(final File tmpFile, final Message message, - final SmartSendInfo smartSendInfo) { - final FileOutputStream fileStream; - try { - fileStream = new FileOutputStream(tmpFile); - } catch (final FileNotFoundException e) { - Log.e(LogUtils.TAG, "Failed to create message file", e); - return false; - } - try { - final boolean smartSend = smartSendInfo != null; - final ArrayList<Attachment> attachments = - smartSend ? smartSendInfo.mRequiredAtts : null; - Rfc822Output.writeTo(mContext, message, fileStream, smartSend, true, attachments); - } catch (final Exception e) { - Log.e(LogUtils.TAG, "Failed to write message file", e); - return false; - } finally { - try { - fileStream.close(); - } catch (final IOException e) { - // should not happen - Log.e(LogUtils.TAG, "Failed to close file - should not happen", e); - } - } - return true; - } - - private static int getModeTag(final boolean isEas14, final SmartSendInfo smartSendInfo) { - if (isEas14) { - if (smartSendInfo == null) { - return Tags.COMPOSE_SEND_MAIL; - } else if (smartSendInfo.isForward()) { - return Tags.COMPOSE_SMART_FORWARD; - } else { - return Tags.COMPOSE_SMART_REPLY; - } - } - return 0; - } } diff --git a/src/com/android/exchange/service/EasService.java b/src/com/android/exchange/service/EasService.java index 5792802b..0b221c88 100644 --- a/src/com/android/exchange/service/EasService.java +++ b/src/com/android/exchange/service/EasService.java @@ -71,7 +71,9 @@ public class EasService extends Service { */ private final IEmailService.Stub mBinder = new IEmailService.Stub() { @Override - public void sendMail(final long accountId) {} + public void sendMail(final long accountId) { + LogUtils.d(TAG, "IEmailService.sendMail: %d", accountId); + } @Override public void loadAttachment(final IEmailServiceCallback callback, final long accountId, diff --git a/src/com/android/exchange/service/EmailSyncAdapterService.java b/src/com/android/exchange/service/EmailSyncAdapterService.java index 0e4bfed2..b59822f6 100644 --- a/src/com/android/exchange/service/EmailSyncAdapterService.java +++ b/src/com/android/exchange/service/EmailSyncAdapterService.java @@ -47,6 +47,9 @@ import com.android.emailcommon.TempDirectory; import com.android.emailcommon.provider.Account; import com.android.emailcommon.provider.EmailContent; import com.android.emailcommon.provider.EmailContent.AccountColumns; +import com.android.emailcommon.provider.EmailContent.Message; +import com.android.emailcommon.provider.EmailContent.MessageColumns; +import com.android.emailcommon.provider.EmailContent.SyncColumns; import com.android.emailcommon.provider.HostAuth; import com.android.emailcommon.provider.Mailbox; import com.android.emailcommon.service.EmailServiceStatus; @@ -64,6 +67,7 @@ import com.android.exchange.eas.EasFolderSync; import com.android.exchange.eas.EasLoadAttachment; import com.android.exchange.eas.EasMoveItems; import com.android.exchange.eas.EasOperation; +import com.android.exchange.eas.EasOutboxSync; import com.android.exchange.eas.EasPing; import com.android.exchange.eas.EasSearch; import com.android.exchange.eas.EasSync; @@ -113,6 +117,12 @@ public class EmailSyncAdapterService extends AbstractSyncAdapterService { private static final Object sSyncAdapterLock = new Object(); private static AbstractThreadedSyncAdapter sSyncAdapter = null; + // Value for a message's server id when sending fails. + public static final int SEND_FAILED = 1; + public static final String MAILBOX_KEY_AND_NOT_SEND_FAILED = + MessageColumns.MAILBOX_KEY + "=? and (" + SyncColumns.SERVER_ID + " is null or " + + SyncColumns.SERVER_ID + "!=" + SEND_FAILED + ')'; + /** * Bookkeeping for handling synchronization between pings and syncs. * "Ping" refers to a hanging POST or GET that is used to receive push notifications. Ping is @@ -869,9 +879,8 @@ public class EmailSyncAdapterService extends AbstractSyncAdapterService { updateMailbox(context, mailbox, cv, isMailboxSync ? EmailContent.SYNC_STATUS_USER : EmailContent.SYNC_STATUS_BACKGROUND); if (mailbox.mType == Mailbox.TYPE_OUTBOX) { - final EasOutboxSyncHandler outboxSyncHandler = - new EasOutboxSyncHandler(context, account, mailbox); - outboxSyncHandler.performSync(); + int result = syncOutbox(context, cr, account, mailbox); + // TODO: in some cases we might want to abort the sync completely. success = true; } else if(mailbox.isSyncable()) { final EasSyncHandler syncHandler = EasSyncHandler.getEasSyncHandler(context, cr, @@ -892,6 +901,46 @@ public class EmailSyncAdapterService extends AbstractSyncAdapterService { return success; } } + + private int syncOutbox(Context context, ContentResolver cr, Account account, Mailbox mailbox) { + // Get a cursor to Outbox messages + final Cursor c = cr.query(Message.CONTENT_URI, + Message.CONTENT_PROJECTION, MAILBOX_KEY_AND_NOT_SEND_FAILED, + new String[] {Long.toString(mailbox.mId)}, null); + try { + // Loop through the messages, sending each one + while (c.moveToNext()) { + final Message message = new Message(); + message.restore(c); + if (Utility.hasUnloadedAttachments(context, message.mId)) { + // We'll just have to wait on this... + continue; + } + + // TODO: Fix -- how do we want to signal to UI that we started syncing? + // Note the entire callback mechanism here needs improving. + //sendMessageStatus(message.mId, null, EmailServiceStatus.IN_PROGRESS, 0); + + EasOperation op = new EasOutboxSync(context, account, message, true); + int result = op.performOperation(); + if (result == EasOutboxSync.RESULT_ITEM_NOT_FOUND) { + // This can happen if we are using smartReply, and the message we are referring + // to has disappeared from the server. Try again with smartReply disabled. + op = new EasOutboxSync(context, account, message, false); + result = op.performOperation(); + } + if (result != EasOutboxSync.RESULT_OK) { + LogUtils.w(TAG, "Aborting outbox synx for error %d", result); + return result; + } + } + } finally { + // TODO: Some sort of sendMessageStatus() is needed here. + c.close(); + } + return EasOutboxSync.RESULT_OK; + } + private void showAuthNotification(long accountId, String accountName) { final PendingIntent pendingIntent = PendingIntent.getActivity( this, |