summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Hibdon <mhibdon@google.com>2014-03-04 13:58:10 -0800
committerThe Android Automerger <android-build@google.com>2014-03-06 11:16:10 -0800
commit5cb88e822f4cd268756f872f206df32731ca4b99 (patch)
treef0196f22f8ac2ec827086c8b348061380dc59589
parentf643f5ddf47b3cf782f449c91090a9a9aadb2b63 (diff)
downloadExchange-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.java36
-rw-r--r--src/com/android/exchange/eas/EasOperation.java19
-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.java4
-rw-r--r--src/com/android/exchange/service/EmailSyncAdapterService.java55
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,