EmailSyncParser.java revision 2f369a47e14916a34f49c79c0a246a2e3ac3072f
1c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hupackage com.android.exchange.adapter; 2c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 3c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.content.ContentProviderOperation; 4c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.content.ContentResolver; 5c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.content.ContentUris; 6c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.content.ContentValues; 7c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.content.Context; 8c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.content.OperationApplicationException; 9c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.database.Cursor; 1036e181376b06c8905af2d22bf494a2423f50aa65Alon Albertimport android.os.Parcel; 11c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.os.RemoteException; 12c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.os.TransactionTooLargeException; 13c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.provider.CalendarContract; 14c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.text.Html; 15c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.text.SpannedString; 16c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.text.TextUtils; 17c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.util.Base64; 1836e181376b06c8905af2d22bf494a2423f50aa65Alon Albertimport android.util.Log; 19c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.webkit.MimeTypeMap; 20c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 21c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.internet.MimeMessage; 22c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.internet.MimeUtility; 23c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.mail.Address; 24c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.mail.MeetingInfo; 25c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.mail.MessagingException; 26c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.mail.PackedString; 27c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.mail.Part; 28c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.provider.Account; 29c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.provider.EmailContent; 30e544c545c9cca446538fcf402863d271f85052c2Tony Mantlerimport com.android.emailcommon.provider.EmailContent.MessageColumns; 31e544c545c9cca446538fcf402863d271f85052c2Tony Mantlerimport com.android.emailcommon.provider.EmailContent.SyncColumns; 32c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.provider.Mailbox; 33c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.provider.Policy; 34c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.provider.ProviderUnavailableException; 35c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.utility.AttachmentUtilities; 36c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.utility.ConversionUtilities; 37c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.utility.TextUtilities; 38c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.utility.Utility; 39c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.exchange.CommandStatusException; 40c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.exchange.Eas; 41c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.exchange.utility.CalendarUtilities; 42c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.mail.utils.LogUtils; 43c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.google.common.annotations.VisibleForTesting; 44c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 45c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.io.ByteArrayInputStream; 46c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.io.IOException; 47c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.io.InputStream; 482f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shraunerimport java.text.ParseException; 49c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.util.ArrayList; 50c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.util.HashMap; 51c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.util.Map; 52c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 53c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu/** 54c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * Parser for Sync on an email collection. 55c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu */ 56c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hupublic class EmailSyncParser extends AbstractSyncParser { 57110837ebff288a75f9bda067c38e2c46797d99b5Alon Albert private static final String TAG = Eas.LOG_TAG; 58c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 59e544c545c9cca446538fcf402863d271f85052c2Tony Mantler private static final String WHERE_SERVER_ID_AND_MAILBOX_KEY = SyncColumns.SERVER_ID 60e544c545c9cca446538fcf402863d271f85052c2Tony Mantler + "=? and " + MessageColumns.MAILBOX_KEY + "=?"; 61c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 62c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private final String mMailboxIdAsString; 63c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 64c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private final ArrayList<EmailContent.Message> 65c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu newEmails = new ArrayList<EmailContent.Message>(); 66c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private final ArrayList<EmailContent.Message> fetchedEmails = 67c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu new ArrayList<EmailContent.Message>(); 68c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private final ArrayList<Long> deletedEmails = new ArrayList<Long>(); 69c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private final ArrayList<ServerChange> changedEmails = new ArrayList<ServerChange>(); 70c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 71c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private static final int MESSAGE_ID_SUBJECT_ID_COLUMN = 0; 72c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private static final int MESSAGE_ID_SUBJECT_SUBJECT_COLUMN = 1; 73c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private static final String[] MESSAGE_ID_SUBJECT_PROJECTION = 74e544c545c9cca446538fcf402863d271f85052c2Tony Mantler new String[] { MessageColumns._ID, MessageColumns.SUBJECT }; 75c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 76c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu @VisibleForTesting 77c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu static final int LAST_VERB_REPLY = 1; 78c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu @VisibleForTesting 79c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu static final int LAST_VERB_REPLY_ALL = 2; 80c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu @VisibleForTesting 81c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu static final int LAST_VERB_FORWARD = 3; 82c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 83c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private final Policy mPolicy; 8436e181376b06c8905af2d22bf494a2423f50aa65Alon Albert 8536e181376b06c8905af2d22bf494a2423f50aa65Alon Albert // Max times to retry when we get a TransactionTooLargeException exception 8636e181376b06c8905af2d22bf494a2423f50aa65Alon Albert private static final int MAX_RETRIES = 10; 8736e181376b06c8905af2d22bf494a2423f50aa65Alon Albert 8836e181376b06c8905af2d22bf494a2423f50aa65Alon Albert // Max number of ops per batch. It could end up more than this but once we detect we are at or 8936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert // above this number, we flush. 9036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert private static final int MAX_OPS_PER_BATCH = 50; 9136e181376b06c8905af2d22bf494a2423f50aa65Alon Albert 92c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private boolean mFetchNeeded = false; 93c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 94c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private final Map<String, Integer> mMessageUpdateStatus = new HashMap(); 95c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 96c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public EmailSyncParser(final Context context, final ContentResolver resolver, 97c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final InputStream in, final Mailbox mailbox, final Account account) 98c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu throws IOException { 99c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu super(context, resolver, in, mailbox, account); 100c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu mMailboxIdAsString = Long.toString(mMailbox.mId); 101c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (mAccount.mPolicyKey != 0) { 102c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu mPolicy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey); 103c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } else { 104c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu mPolicy = null; 105c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 106c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 107c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 1080756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon public EmailSyncParser(final Parser parser, final Context context, 1090756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon final ContentResolver resolver, final Mailbox mailbox, final Account account) 1100756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon throws IOException { 1110756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon super(parser, context, resolver, mailbox, account); 1120756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon mMailboxIdAsString = Long.toString(mMailbox.mId); 1130756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon if (mAccount.mPolicyKey != 0) { 1140756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon mPolicy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey); 1150756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon } else { 1160756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon mPolicy = null; 1170756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon } 1180756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon } 1190756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon 120eba5b43135b9a3d3fae590b701de849955aba246Yu Ping Hu public EmailSyncParser(final Context context, final InputStream in, final Mailbox mailbox, 121eba5b43135b9a3d3fae590b701de849955aba246Yu Ping Hu final Account account) throws IOException { 122eba5b43135b9a3d3fae590b701de849955aba246Yu Ping Hu this(context, context.getContentResolver(), in, mailbox, account); 123eba5b43135b9a3d3fae590b701de849955aba246Yu Ping Hu } 124eba5b43135b9a3d3fae590b701de849955aba246Yu Ping Hu 125c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public boolean fetchNeeded() { 126c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu return mFetchNeeded; 127c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 128c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 129c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public Map<String, Integer> getMessageStatuses() { 130c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu return mMessageUpdateStatus; 131c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 132c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 1330756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon public void addData(EmailContent.Message msg, int endingTag) throws IOException { 134c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ArrayList<EmailContent.Attachment> atts = new ArrayList<EmailContent.Attachment>(); 135c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu boolean truncated = false; 136c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 137c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu while (nextTag(endingTag) != END) { 138c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu switch (tag) { 139c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_ATTACHMENTS: 140c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.BASE_ATTACHMENTS: // BASE_ATTACHMENTS is used in EAS 12.0 and up 141c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu attachmentsParser(atts, msg); 142c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 143c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_TO: 144a0c90aa8eabacdd97c1fea873514eb7a4adf6bcaJames Lemieux msg.mTo = Address.toString(Address.parse(getValue())); 145c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 146c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_FROM: 147c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu Address[] froms = Address.parse(getValue()); 148c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (froms != null && froms.length > 0) { 149c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mDisplayName = froms[0].toFriendly(); 150c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 151a0c90aa8eabacdd97c1fea873514eb7a4adf6bcaJames Lemieux msg.mFrom = Address.toString(froms); 152c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 153c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_CC: 154a0c90aa8eabacdd97c1fea873514eb7a4adf6bcaJames Lemieux msg.mCc = Address.toString(Address.parse(getValue())); 155c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 156c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_REPLY_TO: 157a0c90aa8eabacdd97c1fea873514eb7a4adf6bcaJames Lemieux msg.mReplyTo = Address.toString(Address.parse(getValue())); 158c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 159c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_DATE_RECEIVED: 1602f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner try { 1612f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner msg.mTimeStamp = Utility.parseEmailDateTimeToMillis(getValue()); 1622f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner } catch (ParseException e) { 1632f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner LogUtils.w(TAG, "Parse error for EMAIL_DATE_RECEIVED tag.", e); 1642f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner } 165c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 166c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_SUBJECT: 167c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mSubject = getValue(); 168c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 169c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_READ: 170c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mFlagRead = getValueInt() == 1; 171c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 172c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.BASE_BODY: 173c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu bodyParser(msg); 174c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 175c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_FLAG: 176c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mFlagFavorite = flagParser(); 177c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 178c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_MIME_TRUNCATED: 179c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu truncated = getValueInt() == 1; 180c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 181c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_MIME_DATA: 182c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // We get MIME data for EAS 2.5. First we parse it, then we take the 183c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // html and/or plain text data and store it in the message 184c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (truncated) { 185c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // If the MIME data is truncated, don't bother parsing it, because 186c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // it will take time and throw an exception anyway when EOF is reached 187c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // In this case, we will load the body separately by tagging the message 188c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // "partially loaded". 189c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // Get the data (and ignore it) 190c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu getValue(); 191c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu userLog("Partially loaded: ", msg.mServerId); 192c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mFlagLoaded = EmailContent.Message.FLAG_LOADED_PARTIAL; 193c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu mFetchNeeded = true; 194c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } else { 195c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu mimeBodyParser(msg, getValue()); 196c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 197c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 198c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_BODY: 199c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String text = getValue(); 200c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mText = text; 201c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 202c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_MESSAGE_CLASS: 203c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String messageClass = getValue(); 204c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (messageClass.equals("IPM.Schedule.Meeting.Request")) { 205c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mFlags |= EmailContent.Message.FLAG_INCOMING_MEETING_INVITE; 206c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } else if (messageClass.equals("IPM.Schedule.Meeting.Canceled")) { 207c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mFlags |= EmailContent.Message.FLAG_INCOMING_MEETING_CANCEL; 208c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 209c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 210c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_MEETING_REQUEST: 211c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu meetingRequestParser(msg); 212c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 213c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_THREAD_TOPIC: 214c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mThreadTopic = getValue(); 215c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 216c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.RIGHTS_LICENSE: 217c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu skipParser(tag); 218c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 219c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL2_CONVERSATION_ID: 220c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mServerConversationId = 221c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu Base64.encodeToString(getValueBytes(), Base64.URL_SAFE); 222c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 223c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL2_CONVERSATION_INDEX: 224c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // Ignore this byte array since we're not constructing a tree. 225c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu getValueBytes(); 226c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 227c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL2_LAST_VERB_EXECUTED: 228c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu int val = getValueInt(); 229c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (val == LAST_VERB_REPLY || val == LAST_VERB_REPLY_ALL) { 230c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // We aren't required to distinguish between reply and reply all here 231c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mFlags |= EmailContent.Message.FLAG_REPLIED_TO; 232c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } else if (val == LAST_VERB_FORWARD) { 233c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mFlags |= EmailContent.Message.FLAG_FORWARDED; 234c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 235c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 236c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu default: 237c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu skipTag(); 238c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 239c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 240c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 241c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (atts.size() > 0) { 242c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mAttachments = atts; 243c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 244c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 245c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if ((msg.mFlags & EmailContent.Message.FLAG_INCOMING_MEETING_MASK) != 0) { 246c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String text = TextUtilities.makeSnippetFromHtmlText( 247c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mText != null ? msg.mText : msg.mHtml); 248c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (TextUtils.isEmpty(text)) { 249c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // Create text for this invitation 250c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String meetingInfo = msg.mMeetingInfo; 251c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (!TextUtils.isEmpty(meetingInfo)) { 252c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu PackedString ps = new PackedString(meetingInfo); 253c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ContentValues values = new ContentValues(); 254c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu putFromMeeting(ps, MeetingInfo.MEETING_LOCATION, values, 255c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu CalendarContract.Events.EVENT_LOCATION); 256c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String dtstart = ps.get(MeetingInfo.MEETING_DTSTART); 257c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (!TextUtils.isEmpty(dtstart)) { 2582f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner try { 2592f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner final long startTime = 2602f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner Utility.parseEmailDateTimeToMillis(dtstart); 2612f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner values.put(CalendarContract.Events.DTSTART, startTime); 2622f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner } catch (ParseException e) { 2632f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner LogUtils.w(TAG, "Parse error for MEETING_DTSTART tag.", e); 2642f369a47e14916a34f49c79c0a246a2e3ac3072fJay Shrauner } 265c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 266c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu putFromMeeting(ps, MeetingInfo.MEETING_ALL_DAY, values, 267c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu CalendarContract.Events.ALL_DAY); 268c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mText = CalendarUtilities.buildMessageTextFromEntityValues( 269c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu mContext, values, null); 270c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mHtml = Html.toHtml(new SpannedString(msg.mText)); 271c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 272c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 273c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 274c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 275c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 276c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private static void putFromMeeting(PackedString ps, String field, ContentValues values, 277c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String column) { 278c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String val = ps.get(field); 279c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (!TextUtils.isEmpty(val)) { 280c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu values.put(column, val); 281c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 282c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 283c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 284c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu /** 285c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * Set up the meetingInfo field in the message with various pieces of information gleaned 286c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * from MeetingRequest tags. This information will be used later to generate an appropriate 287c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * reply email if the user chooses to respond 288c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * @param msg the Message being built 289c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * @throws IOException 290c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu */ 291c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private void meetingRequestParser(EmailContent.Message msg) throws IOException { 292c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu PackedString.Builder packedString = new PackedString.Builder(); 293c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu while (nextTag(Tags.EMAIL_MEETING_REQUEST) != END) { 294c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu switch (tag) { 295c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_DTSTAMP: 296c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu packedString.put(MeetingInfo.MEETING_DTSTAMP, getValue()); 297c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 298c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_START_TIME: 299c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu packedString.put(MeetingInfo.MEETING_DTSTART, getValue()); 300c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 301c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_END_TIME: 302c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu packedString.put(MeetingInfo.MEETING_DTEND, getValue()); 303c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 304c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_ORGANIZER: 305c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu packedString.put(MeetingInfo.MEETING_ORGANIZER_EMAIL, getValue()); 306c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 307c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_LOCATION: 308c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu packedString.put(MeetingInfo.MEETING_LOCATION, getValue()); 309c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 310c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_GLOBAL_OBJID: 311c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu packedString.put(MeetingInfo.MEETING_UID, 312c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu CalendarUtilities.getUidFromGlobalObjId(getValue())); 313c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 314c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_CATEGORIES: 315c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu skipParser(tag); 316c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 317c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_RECURRENCES: 318c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu recurrencesParser(); 319c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 320c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_RESPONSE_REQUESTED: 321c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu packedString.put(MeetingInfo.MEETING_RESPONSE_REQUESTED, getValue()); 322c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 323c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_ALL_DAY_EVENT: 324c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (getValueInt() == 1) { 325c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu packedString.put(MeetingInfo.MEETING_ALL_DAY, "1"); 326c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 327c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 328c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu default: 329c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu skipTag(); 330c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 331c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 332c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (msg.mSubject != null) { 333c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu packedString.put(MeetingInfo.MEETING_TITLE, msg.mSubject); 334c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 335c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mMeetingInfo = packedString.toString(); 336c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 337c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 338c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private void recurrencesParser() throws IOException { 339c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu while (nextTag(Tags.EMAIL_RECURRENCES) != END) { 340c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu switch (tag) { 341c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_RECURRENCE: 342c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu skipParser(tag); 343c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 344c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu default: 345c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu skipTag(); 346c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 347c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 348c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 349c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 350c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu /** 351c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * Parse a message from the server stream. 352c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * @return the parsed Message 353c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * @throws IOException 354c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu */ 355c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private EmailContent.Message addParser() throws IOException, CommandStatusException { 356c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu EmailContent.Message msg = new EmailContent.Message(); 357c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mAccountKey = mAccount.mId; 358c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mMailboxKey = mMailbox.mId; 359c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mFlagLoaded = EmailContent.Message.FLAG_LOADED_COMPLETE; 360c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // Default to 1 (success) in case we don't get this tag 361c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu int status = 1; 362c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 363c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu while (nextTag(Tags.SYNC_ADD) != END) { 364c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu switch (tag) { 365c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.SYNC_SERVER_ID: 366c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mServerId = getValue(); 367c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 368c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.SYNC_STATUS: 369c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu status = getValueInt(); 370c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 371c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.SYNC_APPLICATION_DATA: 372c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu addData(msg, tag); 373c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 374c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu default: 375c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu skipTag(); 376c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 377c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 378c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // For sync, status 1 = success 379c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (status != 1) { 380c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu throw new CommandStatusException(status, msg.mServerId); 381c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 382c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu return msg; 383c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 384c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 385c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // For now, we only care about the "active" state 386c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private Boolean flagParser() throws IOException { 387c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu Boolean state = false; 388c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu while (nextTag(Tags.EMAIL_FLAG) != END) { 389c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu switch (tag) { 390c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_FLAG_STATUS: 391c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu state = getValueInt() == 2; 392c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 393c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu default: 394c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu skipTag(); 395c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 396c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 397c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu return state; 398c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 399c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 400c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private void bodyParser(EmailContent.Message msg) throws IOException { 401c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String bodyType = Eas.BODY_PREFERENCE_TEXT; 402c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String body = ""; 403c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu while (nextTag(Tags.EMAIL_BODY) != END) { 404c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu switch (tag) { 405c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.BASE_TYPE: 406c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu bodyType = getValue(); 407c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 408c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.BASE_DATA: 409c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu body = getValue(); 410c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 411c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu default: 412c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu skipTag(); 413c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 414c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 415c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // We always ask for TEXT or HTML; there's no third option 416c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (bodyType.equals(Eas.BODY_PREFERENCE_HTML)) { 417c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mHtml = body; 418c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } else { 419c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mText = body; 420c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 421c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 422c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 423c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu /** 424c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * Parses untruncated MIME data, saving away the text parts 425c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * @param msg the message we're building 426c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * @param mimeData the MIME data we've received from the server 427c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * @throws IOException 428c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu */ 429c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private static void mimeBodyParser(EmailContent.Message msg, String mimeData) 430c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu throws IOException { 431c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu try { 432c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ByteArrayInputStream in = new ByteArrayInputStream(mimeData.getBytes()); 433c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // The constructor parses the message 434c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu MimeMessage mimeMessage = new MimeMessage(in); 435c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // Now process body parts & attachments 436c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ArrayList<Part> viewables = new ArrayList<Part>(); 437c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // We'll ignore the attachments, as we'll get them directly from EAS 438c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ArrayList<Part> attachments = new ArrayList<Part>(); 439c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu MimeUtility.collectParts(mimeMessage, viewables, attachments); 440c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // parseBodyFields fills in the content fields of the Body 441c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ConversionUtilities.BodyFieldData data = 442c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ConversionUtilities.parseBodyFields(viewables); 443c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // But we need them in the message itself for handling during commit() 444c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.setFlags(data.isQuotedReply, data.isQuotedForward); 445c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mSnippet = data.snippet; 446c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mHtml = data.htmlContent; 447c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mText = data.textContent; 448c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } catch (MessagingException e) { 449c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // This would most likely indicate a broken stream 450c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu throw new IOException(e); 451c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 452c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 453c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 454c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private void attachmentsParser(ArrayList<EmailContent.Attachment> atts, 455c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu EmailContent.Message msg) throws IOException { 456c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu while (nextTag(Tags.EMAIL_ATTACHMENTS) != END) { 457c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu switch (tag) { 458c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_ATTACHMENT: 459c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.BASE_ATTACHMENT: // BASE_ATTACHMENT is used in EAS 12.0 and up 460c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu attachmentParser(atts, msg); 461c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 462c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu default: 463c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu skipTag(); 464c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 465c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 466c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 467c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 468c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private void attachmentParser(ArrayList<EmailContent.Attachment> atts, 469c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu EmailContent.Message msg) throws IOException { 470c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String fileName = null; 471c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String length = null; 472c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String location = null; 473c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu boolean isInline = false; 474c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String contentId = null; 475c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 476c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu while (nextTag(Tags.EMAIL_ATTACHMENT) != END) { 477c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu switch (tag) { 478c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // We handle both EAS 2.5 and 12.0+ attachments here 479c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_DISPLAY_NAME: 480c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.BASE_DISPLAY_NAME: 481c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu fileName = getValue(); 482c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 483c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_ATT_NAME: 484c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.BASE_FILE_REFERENCE: 485c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu location = getValue(); 486c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 487c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_ATT_SIZE: 488c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.BASE_ESTIMATED_DATA_SIZE: 489c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu length = getValue(); 490c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 491c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.BASE_IS_INLINE: 492c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu isInline = getValueInt() == 1; 493c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 494c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.BASE_CONTENT_ID: 495c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu contentId = getValue(); 496c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 497c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu default: 498c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu skipTag(); 499c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 500c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 501c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 502c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if ((fileName != null) && (length != null) && (location != null)) { 503c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu EmailContent.Attachment att = new EmailContent.Attachment(); 504c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu att.mEncoding = "base64"; 505c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu att.mSize = Long.parseLong(length); 506c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu att.mFileName = fileName; 507c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu att.mLocation = location; 508c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu att.mMimeType = getMimeTypeFromFileName(fileName); 509c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu att.mAccountKey = mAccount.mId; 510c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // Save away the contentId, if we've got one (for inline images); note that the 511c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // EAS docs appear to be wrong about the tags used; inline images come with 512c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // contentId rather than contentLocation, when sent from Ex03, Ex07, and Ex10 513c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (isInline && !TextUtils.isEmpty(contentId)) { 514c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu att.mContentId = contentId; 515c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 516c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // Check if this attachment can't be downloaded due to an account policy 517c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (mPolicy != null) { 518c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (mPolicy.mDontAllowAttachments || 519c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu (mPolicy.mMaxAttachmentSize > 0 && 520c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu (att.mSize > mPolicy.mMaxAttachmentSize))) { 521c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu att.mFlags = EmailContent.Attachment.FLAG_POLICY_DISALLOWS_DOWNLOAD; 522c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 523c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 524c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu atts.add(att); 525c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.mFlagAttachment = true; 526c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 527c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 528c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 529c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu /** 530c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * Returns an appropriate mimetype for the given file name's extension. If a mimetype 531c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * cannot be determined, {@code application/<<x>>} [where @{code <<x>> is the extension, 532c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * if it exists or {@code application/octet-stream}]. 533c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * At the moment, this is somewhat lame, since many file types aren't recognized 534c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * @param fileName the file name to ponder 535c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu */ 536c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // Note: The MimeTypeMap method currently uses a very limited set of mime types 537c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // A bug has been filed against this issue. 538c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public String getMimeTypeFromFileName(String fileName) { 539c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String mimeType; 540c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu int lastDot = fileName.lastIndexOf('.'); 541c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String extension = null; 542c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if ((lastDot > 0) && (lastDot < fileName.length() - 1)) { 543c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu extension = fileName.substring(lastDot + 1).toLowerCase(); 544c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 545c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (extension == null) { 546c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // A reasonable default for now. 547c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu mimeType = "application/octet-stream"; 548c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } else { 549c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); 550c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (mimeType == null) { 551c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu mimeType = "application/" + extension; 552c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 553c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 554c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu return mimeType; 555c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 556c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 557c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private Cursor getServerIdCursor(String serverId, String[] projection) { 558c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu Cursor c = mContentResolver.query(EmailContent.Message.CONTENT_URI, projection, 559c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu WHERE_SERVER_ID_AND_MAILBOX_KEY, new String[] {serverId, mMailboxIdAsString}, 560c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu null); 561c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (c == null) throw new ProviderUnavailableException(); 562c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (c.getCount() > 1) { 563c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu userLog("Multiple messages with the same serverId/mailbox: " + serverId); 564c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 565c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu return c; 566c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 567c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 568c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu @VisibleForTesting 569c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu void deleteParser(ArrayList<Long> deletes, int entryTag) throws IOException { 570c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu while (nextTag(entryTag) != END) { 571c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu switch (tag) { 572c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.SYNC_SERVER_ID: 573c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String serverId = getValue(); 574c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // Find the message in this mailbox with the given serverId 575c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu Cursor c = getServerIdCursor(serverId, MESSAGE_ID_SUBJECT_PROJECTION); 576c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu try { 577c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (c.moveToFirst()) { 578c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu deletes.add(c.getLong(MESSAGE_ID_SUBJECT_ID_COLUMN)); 579c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (Eas.USER_LOG) { 580c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu userLog("Deleting ", serverId + ", " 581c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu + c.getString(MESSAGE_ID_SUBJECT_SUBJECT_COLUMN)); 582c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 583c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 584c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } finally { 585c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu c.close(); 586c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 587c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 588c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu default: 589c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu skipTag(); 590c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 591c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 592c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 593c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 594c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu @VisibleForTesting 595c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu class ServerChange { 596c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final long id; 597c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final Boolean read; 598c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final Boolean flag; 599c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final Integer flags; 600c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 601c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ServerChange(long _id, Boolean _read, Boolean _flag, Integer _flags) { 602c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu id = _id; 603c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu read = _read; 604c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu flag = _flag; 605c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu flags = _flags; 606c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 607c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 608c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 609c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu @VisibleForTesting 610c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu void changeParser(ArrayList<ServerChange> changes) throws IOException { 611c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String serverId = null; 612c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu Boolean oldRead = false; 613c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu Boolean oldFlag = false; 614c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu int flags = 0; 615c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu long id = 0; 616c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu while (nextTag(Tags.SYNC_CHANGE) != END) { 617c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu switch (tag) { 618c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.SYNC_SERVER_ID: 619c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu serverId = getValue(); 620c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu Cursor c = getServerIdCursor(serverId, EmailContent.Message.LIST_PROJECTION); 621c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu try { 622c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (c.moveToFirst()) { 623c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu userLog("Changing ", serverId); 624c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu oldRead = c.getInt(EmailContent.Message.LIST_READ_COLUMN) 625c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu == EmailContent.Message.READ; 626c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu oldFlag = c.getInt(EmailContent.Message.LIST_FAVORITE_COLUMN) == 1; 627c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu flags = c.getInt(EmailContent.Message.LIST_FLAGS_COLUMN); 628c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu id = c.getLong(EmailContent.Message.LIST_ID_COLUMN); 629c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 630c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } finally { 631c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu c.close(); 632c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 633c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 634c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.SYNC_APPLICATION_DATA: 635c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu changeApplicationDataParser(changes, oldRead, oldFlag, flags, id); 636c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 637c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu default: 638c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu skipTag(); 639c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 640c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 641c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 642c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 643c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private void changeApplicationDataParser(ArrayList<ServerChange> changes, Boolean oldRead, 644c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu Boolean oldFlag, int oldFlags, long id) throws IOException { 645c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu Boolean read = null; 646c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu Boolean flag = null; 647c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu Integer flags = null; 648c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu while (nextTag(Tags.SYNC_APPLICATION_DATA) != END) { 649c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu switch (tag) { 650c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_READ: 651c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu read = getValueInt() == 1; 652c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 653c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL_FLAG: 654c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu flag = flagParser(); 655c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 656c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu case Tags.EMAIL2_LAST_VERB_EXECUTED: 657c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu int val = getValueInt(); 658c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // Clear out the old replied/forward flags and add in the new flag 659c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu flags = oldFlags & ~(EmailContent.Message.FLAG_REPLIED_TO 660c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu | EmailContent.Message.FLAG_FORWARDED); 661c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (val == LAST_VERB_REPLY || val == LAST_VERB_REPLY_ALL) { 662c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // We aren't required to distinguish between reply and reply all here 663c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu flags |= EmailContent.Message.FLAG_REPLIED_TO; 664c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } else if (val == LAST_VERB_FORWARD) { 665c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu flags |= EmailContent.Message.FLAG_FORWARDED; 666c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 667c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu break; 668c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu default: 669c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu skipTag(); 670c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 671c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 672c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // See if there are flag changes re: read, flag (favorite) or replied/forwarded 673c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (((read != null) && !oldRead.equals(read)) || 674c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ((flag != null) && !oldFlag.equals(flag)) || (flags != null)) { 675c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu changes.add(new ServerChange(id, read, flag, flags)); 676c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 677c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 678c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 679c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu /* (non-Javadoc) 680c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * @see com.android.exchange.adapter.EasContentParser#commandsParser() 681c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu */ 682c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu @Override 683c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public void commandsParser() throws IOException, CommandStatusException { 684c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu while (nextTag(Tags.SYNC_COMMANDS) != END) { 685c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (tag == Tags.SYNC_ADD) { 686c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu newEmails.add(addParser()); 687c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } else if (tag == Tags.SYNC_DELETE || tag == Tags.SYNC_SOFT_DELETE) { 688c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu deleteParser(deletedEmails, tag); 689c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } else if (tag == Tags.SYNC_CHANGE) { 690c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu changeParser(changedEmails); 691c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } else 692c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu skipTag(); 693c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 694c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 695c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 696c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // EAS values for status element of sync responses. 697c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // TODO: Not all are used yet, but I wanted to transcribe all possible values. 698c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public static final int EAS_SYNC_STATUS_SUCCESS = 1; 699c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public static final int EAS_SYNC_STATUS_BAD_SYNC_KEY = 3; 700c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public static final int EAS_SYNC_STATUS_PROTOCOL_ERROR = 4; 701c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public static final int EAS_SYNC_STATUS_SERVER_ERROR = 5; 702c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public static final int EAS_SYNC_STATUS_BAD_CLIENT_DATA = 6; 703c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public static final int EAS_SYNC_STATUS_CONFLICT = 7; 704c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public static final int EAS_SYNC_STATUS_OBJECT_NOT_FOUND = 8; 705c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public static final int EAS_SYNC_STATUS_CANNOT_COMPLETE = 9; 706c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public static final int EAS_SYNC_STATUS_FOLDER_SYNC_NEEDED = 12; 707c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public static final int EAS_SYNC_STATUS_INCOMPLETE_REQUEST = 13; 708c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public static final int EAS_SYNC_STATUS_BAD_HEARTBEAT_VALUE = 14; 709c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public static final int EAS_SYNC_STATUS_TOO_MANY_COLLECTIONS = 15; 710c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public static final int EAS_SYNC_STATUS_RETRY = 16; 711c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 712c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public static boolean shouldRetry(final int status) { 713c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu return status == EAS_SYNC_STATUS_SERVER_ERROR || status == EAS_SYNC_STATUS_RETRY; 714c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 715c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 716c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu /** 717c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * Parse the status for a single message update. 718c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * @param endTag the tag we end with 719c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * @throws IOException 720c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu */ 721c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public void messageUpdateParser(int endTag) throws IOException { 722c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // We get serverId and status in the responses 723c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String serverId = null; 724c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu int status = -1; 725c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu while (nextTag(endTag) != END) { 726c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (tag == Tags.SYNC_STATUS) { 727c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu status = getValueInt(); 728c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } else if (tag == Tags.SYNC_SERVER_ID) { 729c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu serverId = getValue(); 730c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } else { 731c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu skipTag(); 732c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 733c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 734c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (serverId != null && status != -1) { 735c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu mMessageUpdateStatus.put(serverId, status); 736c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 737c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 738c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 739c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu @Override 740c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public void responsesParser() throws IOException { 741c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu while (nextTag(Tags.SYNC_RESPONSES) != END) { 742c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (tag == Tags.SYNC_ADD || tag == Tags.SYNC_CHANGE || tag == Tags.SYNC_DELETE) { 743c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu messageUpdateParser(tag); 744c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } else if (tag == Tags.SYNC_FETCH) { 745c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu try { 746c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu fetchedEmails.add(addParser()); 747c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } catch (CommandStatusException sse) { 748c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (sse.mStatus == 8) { 749c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // 8 = object not found; delete the message from EmailProvider 750c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // No other status should be seen in a fetch response, except, perhaps, 751c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // for some temporary server failure 752c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu mContentResolver.delete(EmailContent.Message.CONTENT_URI, 753c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu WHERE_SERVER_ID_AND_MAILBOX_KEY, 754c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu new String[] {sse.mItemId, mMailboxIdAsString}); 755c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 756c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 757c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 758c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 759c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 760c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 761722006c7b116240d9383c84d5e76ee69796dd571Alon Albert @Override 762722006c7b116240d9383c84d5e76ee69796dd571Alon Albert protected void wipe() { 763722006c7b116240d9383c84d5e76ee69796dd571Alon Albert LogUtils.i(TAG, "Wiping mailbox " + mMailbox); 7645cc7ea3e24f1c05f71a3223ac6fa8b69d211735cYu Ping Hu Mailbox.resyncMailbox(mContentResolver, new android.accounts.Account(mAccount.mEmailAddress, 7655cc7ea3e24f1c05f71a3223ac6fa8b69d211735cYu Ping Hu Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), mMailbox.mId); 766722006c7b116240d9383c84d5e76ee69796dd571Alon Albert } 767722006c7b116240d9383c84d5e76ee69796dd571Alon Albert 76871cfa486dd576841a6333b761530c82b296a1c0cYu Ping Hu @Override 76971cfa486dd576841a6333b761530c82b296a1c0cYu Ping Hu public boolean parse() throws IOException, CommandStatusException { 77071cfa486dd576841a6333b761530c82b296a1c0cYu Ping Hu final boolean result = super.parse(); 77171cfa486dd576841a6333b761530c82b296a1c0cYu Ping Hu return result || fetchNeeded(); 77271cfa486dd576841a6333b761530c82b296a1c0cYu Ping Hu } 77371cfa486dd576841a6333b761530c82b296a1c0cYu Ping Hu 77436e181376b06c8905af2d22bf494a2423f50aa65Alon Albert /** 77536e181376b06c8905af2d22bf494a2423f50aa65Alon Albert * Commit all changes. This results in a Binder IPC call which has constraint on the size of 77636e181376b06c8905af2d22bf494a2423f50aa65Alon Albert * the data, the docs say it currently 1MB. We set a limit to the size of the message we fetch 77736e181376b06c8905af2d22bf494a2423f50aa65Alon Albert * with {@link Eas#EAS12_TRUNCATION_SIZE} & {@link Eas#EAS12_TRUNCATION_SIZE} which are at 200k 77836e181376b06c8905af2d22bf494a2423f50aa65Alon Albert * or bellow. As long as these limits are bellow 500k, we should be able to apply a single 77936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert * message (the transaction size is about double the message size because Java strings are 16 78036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert * bit. 78136e181376b06c8905af2d22bf494a2423f50aa65Alon Albert * <b/> 78236e181376b06c8905af2d22bf494a2423f50aa65Alon Albert * We first try to apply the changes in normal chunk size {@link #MAX_OPS_PER_BATCH}. If we get 78336e181376b06c8905af2d22bf494a2423f50aa65Alon Albert * a {@link TransactionTooLargeException} we try again with but this time, we apply each change 78436e181376b06c8905af2d22bf494a2423f50aa65Alon Albert * immediately. 78536e181376b06c8905af2d22bf494a2423f50aa65Alon Albert */ 786c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu @Override 78736e181376b06c8905af2d22bf494a2423f50aa65Alon Albert public void commit() throws RemoteException, OperationApplicationException { 78836e181376b06c8905af2d22bf494a2423f50aa65Alon Albert try { 78936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert commitImpl(MAX_OPS_PER_BATCH); 79036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert } catch (TransactionTooLargeException e) { 79136e181376b06c8905af2d22bf494a2423f50aa65Alon Albert // Try again but apply batch after every message. The max message size defined in 79236e181376b06c8905af2d22bf494a2423f50aa65Alon Albert // Eas.EAS12_TRUNCATION_SIZE or Eas.EAS2_5_TRUNCATION_SIZE is small enough to fit 79336e181376b06c8905af2d22bf494a2423f50aa65Alon Albert // in a single Binder call. 79436e181376b06c8905af2d22bf494a2423f50aa65Alon Albert LogUtils.w(TAG, "Transaction too large, retrying in single mode", e); 79536e181376b06c8905af2d22bf494a2423f50aa65Alon Albert commitImpl(1); 79636e181376b06c8905af2d22bf494a2423f50aa65Alon Albert } 797c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 798c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 79936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert public void commitImpl(int maxOpsPerBatch) 80036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert throws RemoteException, OperationApplicationException { 801c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // Use a batch operation to handle the changes 802c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 803c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 804c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // Maximum size of message text per fetch 805c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu int numFetched = fetchedEmails.size(); 80636e181376b06c8905af2d22bf494a2423f50aa65Alon Albert LogUtils.d(TAG, "commitImpl: maxOpsPerBatch=%d numFetched=%d numNew=%d " 80736e181376b06c8905af2d22bf494a2423f50aa65Alon Albert + "numDeleted=%d numChanged=%d", 80836e181376b06c8905af2d22bf494a2423f50aa65Alon Albert maxOpsPerBatch, 80936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert numFetched, 81036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert newEmails.size(), 81136e181376b06c8905af2d22bf494a2423f50aa65Alon Albert deletedEmails.size(), 81236e181376b06c8905af2d22bf494a2423f50aa65Alon Albert changedEmails.size()); 813c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu for (EmailContent.Message msg: fetchedEmails) { 814c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // Find the original message's id (by serverId and mailbox) 815c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu Cursor c = getServerIdCursor(msg.mServerId, EmailContent.ID_PROJECTION); 816c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String id = null; 817c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu try { 818c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (c.moveToFirst()) { 819c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu id = c.getString(EmailContent.ID_PROJECTION_COLUMN); 820c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu while (c.moveToNext()) { 821c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // This shouldn't happen, but clean up if it does 822c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu Long dupId = 823c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu Long.parseLong(c.getString(EmailContent.ID_PROJECTION_COLUMN)); 824c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu userLog("Delete duplicate with id: " + dupId); 825c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu deletedEmails.add(dupId); 826c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 827c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 828c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } finally { 829c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu c.close(); 830c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 831c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 832c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // If we find one, we do two things atomically: 1) set the body text for the 833c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // message, and 2) mark the message loaded (i.e. completely loaded) 834c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (id != null) { 83571cfa486dd576841a6333b761530c82b296a1c0cYu Ping Hu LogUtils.i(TAG, "Fetched body successfully for %s", id); 836c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final String[] bindArgument = new String[] {id}; 837c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ops.add(ContentProviderOperation.newUpdate(EmailContent.Body.CONTENT_URI) 83871cfa486dd576841a6333b761530c82b296a1c0cYu Ping Hu .withSelection(EmailContent.Body.SELECTION_BY_MESSAGE_KEY, bindArgument) 839e544c545c9cca446538fcf402863d271f85052c2Tony Mantler .withValue(EmailContent.BodyColumns.TEXT_CONTENT, msg.mText) 840c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu .build()); 841c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ops.add(ContentProviderOperation.newUpdate(EmailContent.Message.CONTENT_URI) 842e544c545c9cca446538fcf402863d271f85052c2Tony Mantler .withSelection(MessageColumns._ID + "=?", bindArgument) 843e544c545c9cca446538fcf402863d271f85052c2Tony Mantler .withValue(MessageColumns.FLAG_LOADED, 844c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu EmailContent.Message.FLAG_LOADED_COMPLETE) 845c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu .build()); 846c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 84736e181376b06c8905af2d22bf494a2423f50aa65Alon Albert applyBatchIfNeeded(ops, maxOpsPerBatch, false); 848c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 849c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 850c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu for (EmailContent.Message msg: newEmails) { 851c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu msg.addSaveOps(ops); 85236e181376b06c8905af2d22bf494a2423f50aa65Alon Albert applyBatchIfNeeded(ops, maxOpsPerBatch, false); 853c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 854c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 855c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu for (Long id : deletedEmails) { 856c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ops.add(ContentProviderOperation.newDelete( 857c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ContentUris.withAppendedId(EmailContent.Message.CONTENT_URI, id)).build()); 858c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu AttachmentUtilities.deleteAllAttachmentFiles(mContext, mAccount.mId, id); 85936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert applyBatchIfNeeded(ops, maxOpsPerBatch, false); 860c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 861c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 862c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (!changedEmails.isEmpty()) { 863c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // Server wins in a conflict... 864c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu for (ServerChange change : changedEmails) { 865c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ContentValues cv = new ContentValues(); 866c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (change.read != null) { 867c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu cv.put(EmailContent.MessageColumns.FLAG_READ, change.read); 868c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 869c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (change.flag != null) { 870c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu cv.put(EmailContent.MessageColumns.FLAG_FAVORITE, change.flag); 871c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 872c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (change.flags != null) { 873c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu cv.put(EmailContent.MessageColumns.FLAGS, change.flags); 874c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 875c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ops.add(ContentProviderOperation.newUpdate( 876c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ContentUris.withAppendedId(EmailContent.Message.CONTENT_URI, change.id)) 877c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu .withValues(cv) 878c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu .build()); 879c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 88036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert applyBatchIfNeeded(ops, maxOpsPerBatch, false); 881c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 882c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 883c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // We only want to update the sync key here 884c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ContentValues mailboxValues = new ContentValues(); 885c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu mailboxValues.put(Mailbox.SYNC_KEY, mMailbox.mSyncKey); 886c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ops.add(ContentProviderOperation.newUpdate( 887c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailbox.mId)) 888c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu .withValues(mailboxValues).build()); 889c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 89036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert applyBatchIfNeeded(ops, maxOpsPerBatch, true); 89136e181376b06c8905af2d22bf494a2423f50aa65Alon Albert userLog(mMailbox.mDisplayName, " SyncKey saved as: ", mMailbox.mSyncKey); 89236e181376b06c8905af2d22bf494a2423f50aa65Alon Albert } 89336e181376b06c8905af2d22bf494a2423f50aa65Alon Albert 89436e181376b06c8905af2d22bf494a2423f50aa65Alon Albert // Check if there at least MAX_OPS_PER_BATCH ops in queue and flush if there are. 89536e181376b06c8905af2d22bf494a2423f50aa65Alon Albert // If force is true, flush regardless of size. 89636e181376b06c8905af2d22bf494a2423f50aa65Alon Albert private void applyBatchIfNeeded(ArrayList<ContentProviderOperation> ops, int maxOpsPerBatch, 89736e181376b06c8905af2d22bf494a2423f50aa65Alon Albert boolean force) 89836e181376b06c8905af2d22bf494a2423f50aa65Alon Albert throws RemoteException, OperationApplicationException { 89936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert if (force || ops.size() >= maxOpsPerBatch) { 90036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert // STOPSHIP Remove calculating size of data before ship 90136e181376b06c8905af2d22bf494a2423f50aa65Alon Albert if (LogUtils.isLoggable(TAG, Log.DEBUG)) { 90236e181376b06c8905af2d22bf494a2423f50aa65Alon Albert final Parcel parcel = Parcel.obtain(); 90336e181376b06c8905af2d22bf494a2423f50aa65Alon Albert for (ContentProviderOperation op : ops) { 90436e181376b06c8905af2d22bf494a2423f50aa65Alon Albert op.writeToParcel(parcel, 0); 90536e181376b06c8905af2d22bf494a2423f50aa65Alon Albert } 90636e181376b06c8905af2d22bf494a2423f50aa65Alon Albert Log.d(TAG, String.format("Committing %d ops total size=%d", 90736e181376b06c8905af2d22bf494a2423f50aa65Alon Albert ops.size(), parcel.dataSize())); 90836e181376b06c8905af2d22bf494a2423f50aa65Alon Albert parcel.recycle(); 90936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert } 910c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu mContentResolver.applyBatch(EmailContent.AUTHORITY, ops); 91136e181376b06c8905af2d22bf494a2423f50aa65Alon Albert ops.clear(); 912c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 913c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 9140756a9ff8e69b8cc52374fa68efb2e81971ee5c4Martin Hibdon} 915