1328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu/* 2328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * Copyright (C) 2013 The Android Open Source Project 3328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * 4328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * Licensed under the Apache License, Version 2.0 (the "License"); 5328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * you may not use this file except in compliance with the License. 6328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * You may obtain a copy of the License at 7328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * 8328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * http://www.apache.org/licenses/LICENSE-2.0 9328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * 10328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * Unless required by applicable law or agreed to in writing, software 11328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * distributed under the License is distributed on an "AS IS" BASIS, 12328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * See the License for the specific language governing permissions and 14328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * limitations under the License. 15328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu */ 16328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 17328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hupackage com.android.exchange.eas; 18328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 19c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.content.ContentResolver; 20c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.content.ContentUris; 21328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Huimport android.content.Context; 22c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.database.Cursor; 23c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport android.support.v4.util.LongSparseArray; 2494599580f738c4700174be250ecf7392768a432bTony Mantlerimport android.text.TextUtils; 25328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Huimport android.text.format.DateUtils; 26328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 27328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Huimport com.android.emailcommon.provider.Account; 28c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.provider.EmailContent; 29328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Huimport com.android.emailcommon.provider.Mailbox; 30c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.emailcommon.provider.MessageStateChange; 31c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.exchange.CommandStatusException; 32328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Huimport com.android.exchange.Eas; 33328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Huimport com.android.exchange.EasResponse; 34c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.exchange.adapter.EmailSyncParser; 35c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport com.android.exchange.adapter.Parser; 36328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Huimport com.android.exchange.adapter.Serializer; 37328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Huimport com.android.exchange.adapter.Tags; 3894599580f738c4700174be250ecf7392768a432bTony Mantlerimport com.android.mail.utils.LogUtils; 39328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 40328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Huimport org.apache.http.HttpEntity; 41328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 42328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Huimport java.io.IOException; 43c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.util.Calendar; 44c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.util.GregorianCalendar; 45c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.util.List; 46c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.util.Locale; 47c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.util.Map; 48c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Huimport java.util.TimeZone; 49328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 50328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu/** 51328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu * Performs an Exchange Sync operation for one {@link Mailbox}. 52c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * TODO: For now, only handles upsync. 53c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * TODO: Handle multiple folders in one request. Not sure if parser can handle it yet. 54328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu */ 55328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hupublic class EasSync extends EasOperation { 56328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 578c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu /** Result code indicating that the mailbox for an upsync is no longer present. */ 588c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu public final static int RESULT_NO_MAILBOX = 0; 598c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu public final static int RESULT_OK = 1; 608c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu 61c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // TODO: When we handle downsync, this will become relevant. 62c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private boolean mInitialSync; 63328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 64c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // State for the mailbox we're currently syncing. 65c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private long mMailboxId; 66c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private String mMailboxServerId; 67c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private String mMailboxSyncKey; 68c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private List<MessageStateChange> mStateChanges; 69c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private Map<String, Integer> mMessageUpdateStatus; 70c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 71c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu public EasSync(final Context context, final Account account) { 72328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu super(context, account); 73328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu mInitialSync = false; 74328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 75328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 76c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private long getMessageId(final String serverId) { 77c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // TODO: Improve this. 78c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu for (final MessageStateChange change : mStateChanges) { 79c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (change.getServerId().equals(serverId)) { 80c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu return change.getMessageId(); 81c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 82c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 83c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu return EmailContent.Message.NO_MESSAGE; 84c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 85c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 86c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private void handleMessageUpdateStatus(final Map<String, Integer> messageStatus, 87c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final long[][] messageIds, final int[] counts) { 88c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu for (final Map.Entry<String, Integer> entry : messageStatus.entrySet()) { 89c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final String serverId = entry.getKey(); 90c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final int status = entry.getValue(); 91c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final int index; 92c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (EmailSyncParser.shouldRetry(status)) { 93c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu index = 1; 94c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } else { 95c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu index = 0; 96c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 97c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final long messageId = getMessageId(serverId); 98c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (messageId != EmailContent.Message.NO_MESSAGE) { 99c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu messageIds[index][counts[index]] = messageId; 100c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu ++counts[index]; 101c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 102c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 103c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 104c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 105c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu /** 1068c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu * @return Number of messages successfully synced, or a negative response code from 1078c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu * {@link EasOperation} if we encountered any errors. 108c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu */ 1098c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu public final int upsync() { 1106c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu final List<MessageStateChange> changes = MessageStateChange.getChanges(mContext, 1116c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu getAccountId(), getProtocolVersion() < Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE); 112c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (changes == null) { 113c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu return 0; 114c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 115c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final LongSparseArray<List<MessageStateChange>> allData = 116c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu MessageStateChange.convertToChangesMap(changes); 117c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (allData == null) { 118c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu return 0; 119c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 120c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 121c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final long[][] messageIds = new long[2][changes.size()]; 122c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final int[] counts = new int[2]; 1238c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu int result = 0; 124c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 125c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu for (int i = 0; i < allData.size(); ++i) { 126c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu mMailboxId = allData.keyAt(i); 127c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu mStateChanges = allData.valueAt(i); 1288c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu boolean retryMailbox = true; 1298c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu // If we've already encountered a fatal error, don't even try to upsync subsequent 1308c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu // mailboxes. 1318c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu if (result >= 0) { 1328c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu final Cursor mailboxCursor = mContext.getContentResolver().query( 1338c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailboxId), 1348c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu Mailbox.ProjectionSyncData.PROJECTION, null, null, null); 1358c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu if (mailboxCursor != null) { 1368c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu try { 1378c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu if (mailboxCursor.moveToFirst()) { 1388c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu mMailboxServerId = mailboxCursor.getString( 1398c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu Mailbox.ProjectionSyncData.COLUMN_SERVER_ID); 1408c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu mMailboxSyncKey = mailboxCursor.getString( 1418c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu Mailbox.ProjectionSyncData.COLUMN_SYNC_KEY); 1428c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu if (TextUtils.isEmpty(mMailboxSyncKey) || mMailboxSyncKey.equals("0")) { 1438c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu // For some reason we can get here without a valid mailbox sync key 1448c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu // b/10797675 1458c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu // TODO: figure out why and clean this up 1468c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu LogUtils.d(LOG_TAG, 1478c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu "Tried to sync mailbox %d with invalid mailbox sync key", 1488c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu mMailboxId); 1498c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu } else { 1508c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu result = performOperation(); 1518c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu if (result >= 0) { 1528c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu // Our request gave us back a legitimate answer; this is the 1538c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu // only case in which we don't retry this mailbox. 1548c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu retryMailbox = false; 1558c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu if (result == RESULT_OK) { 1568c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu handleMessageUpdateStatus(mMessageUpdateStatus, messageIds, 1578c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu counts); 1588c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu } else if (result == RESULT_NO_MAILBOX) { 1598c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu // A retry here is pointless -- the message's mailbox (and 1608c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu // therefore the message) is gone, so mark as success so 1618c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu // that these entries get wiped from the change list. 1628c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu for (final MessageStateChange msc : mStateChanges) { 1638c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu messageIds[0][counts[0]] = msc.getMessageId(); 1648c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu ++counts[0]; 1658c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu } 1668c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu } else { 1678c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu LogUtils.wtf(LOG_TAG, "Unrecognized result code: %d", 1688c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu result); 1698c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu } 1708c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu } 17194599580f738c4700174be250ecf7392768a432bTony Mantler } 172c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 1738c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu } finally { 1748c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu mailboxCursor.close(); 175c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 1768c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu } 1778c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu } 1788c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu if (retryMailbox) { 1798c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu for (final MessageStateChange msc : mStateChanges) { 1808c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu messageIds[1][counts[1]] = msc.getMessageId(); 1818c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu ++counts[1]; 182c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 183c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 184c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 185c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 186c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final ContentResolver cr = mContext.getContentResolver(); 187c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu MessageStateChange.upsyncSuccessful(cr, messageIds[0], counts[0]); 188c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu MessageStateChange.upsyncRetry(cr, messageIds[1], counts[1]); 189c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 1908c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu if (result < 0) { 1918c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu return result; 1928c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu } 1938c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu return counts[0]; 194c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 195328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 196328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu @Override 197328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu protected String getCommand() { 198328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu return "Sync"; 199328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 200328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 201328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu @Override 202328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu protected HttpEntity getRequestEntity() throws IOException { 2036f0cf29be849d3a95a9168cb6fa09de12635e818Yu Ping Hu final Serializer s = new Serializer(); 2046f0cf29be849d3a95a9168cb6fa09de12635e818Yu Ping Hu s.start(Tags.SYNC_SYNC); 2056f0cf29be849d3a95a9168cb6fa09de12635e818Yu Ping Hu s.start(Tags.SYNC_COLLECTIONS); 206c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu addOneCollectionToRequest(s, Mailbox.TYPE_MAIL, mMailboxServerId, mMailboxSyncKey, 207c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu mStateChanges); 2086f0cf29be849d3a95a9168cb6fa09de12635e818Yu Ping Hu s.end().end().done(); 2096f0cf29be849d3a95a9168cb6fa09de12635e818Yu Ping Hu return makeEntity(s); 210328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 211328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 212328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu @Override 2138c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu protected int handleResponse(final EasResponse response) 2147f0b7b0d66b40ec3004fbc329dbd34a801608b92Yu Ping Hu throws IOException, CommandStatusException { 215c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, mMailboxId); 216c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (mailbox == null) { 2178c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu return RESULT_NO_MAILBOX; 218c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 219c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final EmailSyncParser parser = new EmailSyncParser(mContext, mContext.getContentResolver(), 2206c4254903d2f42836c5b74a934e54441ccee6dbeYu Ping Hu response.getInputStream(), mailbox, mAccount); 221c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu try { 222c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu parser.parse(); 223c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu mMessageUpdateStatus = parser.getMessageStatuses(); 224c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } catch (final Parser.EmptyStreamException e) { 225c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // This indicates a compressed response which was empty, which is OK. 226c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 2278c95506ad96f5990d5e4d4ca86b05ec95dd4b1d6Yu Ping Hu return RESULT_OK; 228328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 229328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 230328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu @Override 231328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu protected long getTimeout() { 232328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu if (mInitialSync) { 233328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu return 120 * DateUtils.SECOND_IN_MILLIS; 234328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 235328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu return super.getTimeout(); 236328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 237328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 238c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu /** 239c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * Create date/time in RFC8601 format. Oddly enough, for calendar date/time, Microsoft uses 240c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * a different format that excludes the punctuation (this is why I'm not putting this in a 241c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu * parent class) 242c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu */ 243c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu private static String formatDateTime(final Calendar calendar) { 244c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final StringBuilder sb = new StringBuilder(); 245c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu //YYYY-MM-DDTHH:MM:SS.MSSZ 246c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu sb.append(calendar.get(Calendar.YEAR)); 247c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu sb.append('-'); 248c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu sb.append(String.format(Locale.US, "%02d", calendar.get(Calendar.MONTH) + 1)); 249c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu sb.append('-'); 250c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu sb.append(String.format(Locale.US, "%02d", calendar.get(Calendar.DAY_OF_MONTH))); 251c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu sb.append('T'); 252c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu sb.append(String.format(Locale.US, "%02d", calendar.get(Calendar.HOUR_OF_DAY))); 253c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu sb.append(':'); 254c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu sb.append(String.format(Locale.US, "%02d", calendar.get(Calendar.MINUTE))); 255c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu sb.append(':'); 256c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu sb.append(String.format(Locale.US, "%02d", calendar.get(Calendar.SECOND))); 257c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu sb.append(".000Z"); 258c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu return sb.toString(); 259328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 260328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 261ca8ae4efb836bee643396f1fde52e0c87a8c40c1Tony Mantler private void addOneCollectionToRequest(final Serializer s, final int collectionType, 262c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final String mailboxServerId, final String mailboxSyncKey, 263c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final List<MessageStateChange> stateChanges) throws IOException { 264328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu 265c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu s.start(Tags.SYNC_COLLECTION); 266c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (getProtocolVersion() < Eas.SUPPORTED_PROTOCOL_EX2007_SP1_DOUBLE) { 267c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu s.data(Tags.SYNC_CLASS, Eas.getFolderClass(collectionType)); 268c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 269c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu s.data(Tags.SYNC_SYNC_KEY, mailboxSyncKey); 270c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu s.data(Tags.SYNC_COLLECTION_ID, mailboxServerId); 271ca8ae4efb836bee643396f1fde52e0c87a8c40c1Tony Mantler if (getProtocolVersion() >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) { 272ca8ae4efb836bee643396f1fde52e0c87a8c40c1Tony Mantler // Exchange 2003 doesn't understand the concept of setting this flag to false. The 273ca8ae4efb836bee643396f1fde52e0c87a8c40c1Tony Mantler // documentation indicates that its presence alone, with no value, requests a two-way 274ca8ae4efb836bee643396f1fde52e0c87a8c40c1Tony Mantler // sync. 275ca8ae4efb836bee643396f1fde52e0c87a8c40c1Tony Mantler // TODO: handle downsync here so we don't need this at all 276ca8ae4efb836bee643396f1fde52e0c87a8c40c1Tony Mantler s.data(Tags.SYNC_GET_CHANGES, "0"); 277ca8ae4efb836bee643396f1fde52e0c87a8c40c1Tony Mantler } 278c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu s.start(Tags.SYNC_COMMANDS); 279c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu for (final MessageStateChange change : stateChanges) { 280c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu s.start(Tags.SYNC_CHANGE); 281c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu s.data(Tags.SYNC_SERVER_ID, change.getServerId()); 282c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu s.start(Tags.SYNC_APPLICATION_DATA); 283c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final int newFlagRead = change.getNewFlagRead(); 284c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (newFlagRead != MessageStateChange.VALUE_UNCHANGED) { 285c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu s.data(Tags.EMAIL_READ, Integer.toString(newFlagRead)); 286c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 287c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final int newFlagFavorite = change.getNewFlagFavorite(); 288c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (newFlagFavorite != MessageStateChange.VALUE_UNCHANGED) { 289c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // "Flag" is a relatively complex concept in EAS 12.0 and above. It is not only 290c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // the boolean "favorite" that we think of in Gmail, but it also represents a 291c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // follow up action, which can include a subject, start and due dates, and even 292c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // recurrences. We don't support any of this as yet, but EAS 12.0 and higher 293c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // require that a flag contain a status, a type, and four date fields, two each 294c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // for start date and end (due) date. 295c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu if (newFlagFavorite != 0) { 296c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // Status 2 = set flag 297c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu s.start(Tags.EMAIL_FLAG).data(Tags.EMAIL_FLAG_STATUS, "2"); 298c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // "FollowUp" is the standard type 299c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu s.data(Tags.EMAIL_FLAG_TYPE, "FollowUp"); 300c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final long now = System.currentTimeMillis(); 301c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu final Calendar calendar = 302c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT")); 303c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu calendar.setTimeInMillis(now); 304c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // Flags are required to have a start date and end date (duplicated) 305c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // First, we'll set the current date/time in GMT as the start time 306c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu String utc = formatDateTime(calendar); 307c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu s.data(Tags.TASK_START_DATE, utc).data(Tags.TASK_UTC_START_DATE, utc); 308c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu // And then we'll use one week from today for completion date 309c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu calendar.setTimeInMillis(now + DateUtils.WEEK_IN_MILLIS); 310c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu utc = formatDateTime(calendar); 311c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu s.data(Tags.TASK_DUE_DATE, utc).data(Tags.TASK_UTC_DUE_DATE, utc); 312c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu s.end(); 313c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } else { 314c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu s.tag(Tags.EMAIL_FLAG); 315c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 316c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu } 317c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu s.end().end(); // SYNC_APPLICATION_DATA, SYNC_CHANGE 318328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 319c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu s.end().end(); // SYNC_COMMANDS, SYNC_COLLECTION 320328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu } 321328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu} 322