• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.exchange.eas;
2 
3 import android.content.Context;
4 import android.net.TrafficStats;
5 import android.text.format.DateUtils;
6 
7 import com.android.emailcommon.TrafficFlags;
8 import com.android.emailcommon.provider.Account;
9 import com.android.emailcommon.provider.EmailContent;
10 import com.android.emailcommon.provider.Mailbox;
11 import com.android.exchange.CommandStatusException;
12 import com.android.exchange.Eas;
13 import com.android.exchange.EasResponse;
14 import com.android.exchange.adapter.AbstractSyncParser;
15 import com.android.exchange.adapter.Parser;
16 import com.android.exchange.adapter.Serializer;
17 import com.android.exchange.adapter.Tags;
18 import com.android.mail.utils.LogUtils;
19 
20 import org.apache.http.HttpEntity;
21 
22 import java.io.IOException;
23 
24 /**
25  * Performs an EAS sync operation for one folder (excluding mail upsync).
26  * TODO: Merge with {@link EasSync}, which currently handles mail upsync.
27  */
28 public class EasSyncBase extends EasOperation {
29 
30     private static final String TAG = Eas.LOG_TAG;
31 
32     public static final int RESULT_DONE = 0;
33     public static final int RESULT_MORE_AVAILABLE = 1;
34 
35     private boolean mInitialSync;
36     private final Mailbox mMailbox;
37     private EasSyncCollectionTypeBase mCollectionTypeHandler;
38 
39     private int mNumWindows;
40 
41     // TODO: Convert to accountId when ready to convert to EasService.
EasSyncBase(final Context context, final Account account, final Mailbox mailbox)42     public EasSyncBase(final Context context, final Account account, final Mailbox mailbox) {
43         super(context, account);
44         mMailbox = mailbox;
45     }
46 
47     /**
48      * Get the sync key for this mailbox.
49      * @return The sync key for the object being synced. "0" means this is the first sync. If
50      *      there is an error in getting the sync key, this function returns null.
51      */
getSyncKey()52     protected String getSyncKey() {
53         if (mMailbox == null) {
54             return null;
55         }
56         if (mMailbox.mSyncKey == null) {
57             mMailbox.mSyncKey = "0";
58         }
59         return mMailbox.mSyncKey;
60     }
61 
62     @Override
getCommand()63     protected String getCommand() {
64         return "Sync";
65     }
66 
67     @Override
init(final boolean allowReload)68     public boolean init(final boolean allowReload) {
69         final boolean result = super.init(allowReload);
70         if (result) {
71             mCollectionTypeHandler = getCollectionTypeHandler(mMailbox.mType);
72             if (mCollectionTypeHandler == null) {
73                 return false;
74             }
75             // Set up traffic stats bookkeeping.
76             final int trafficFlags = TrafficFlags.getSyncFlags(mContext, mAccount);
77             TrafficStats.setThreadStatsTag(trafficFlags | mCollectionTypeHandler.getTrafficFlag());
78         }
79         return result;
80     }
81 
82     @Override
getRequestEntity()83     protected HttpEntity getRequestEntity() throws IOException {
84         final String className = Eas.getFolderClass(mMailbox.mType);
85         final String syncKey = getSyncKey();
86         LogUtils.d(TAG, "Syncing account %d mailbox %d (class %s) with syncKey %s", mAccount.mId,
87                 mMailbox.mId, className, syncKey);
88         mInitialSync = EmailContent.isInitialSyncKey(syncKey);
89         final Serializer s = new Serializer();
90         s.start(Tags.SYNC_SYNC);
91         s.start(Tags.SYNC_COLLECTIONS);
92         s.start(Tags.SYNC_COLLECTION);
93         // The "Class" element is removed in EAS 12.1 and later versions
94         if (getProtocolVersion() < Eas.SUPPORTED_PROTOCOL_EX2007_SP1_DOUBLE) {
95             s.data(Tags.SYNC_CLASS, className);
96         }
97         s.data(Tags.SYNC_SYNC_KEY, syncKey);
98         s.data(Tags.SYNC_COLLECTION_ID, mMailbox.mServerId);
99         mCollectionTypeHandler.setSyncOptions(mContext, s, getProtocolVersion(), mAccount, mMailbox,
100                 mInitialSync, mNumWindows);
101         s.end().end().end().done();
102 
103         return makeEntity(s);
104     }
105 
106     @Override
handleResponse(final EasResponse response)107     protected int handleResponse(final EasResponse response)
108             throws IOException, CommandStatusException {
109         try {
110             final AbstractSyncParser parser = mCollectionTypeHandler.getParser(mContext, mAccount,
111                     mMailbox, response.getInputStream());
112             final boolean moreAvailable = parser.parse();
113             if (moreAvailable) {
114                 return RESULT_MORE_AVAILABLE;
115             }
116         } catch (final Parser.EmptyStreamException e) {
117             // This indicates a compressed response which was empty, which is OK.
118         }
119         return RESULT_DONE;
120     }
121 
122     @Override
performOperation()123     public int performOperation() {
124         int result = RESULT_MORE_AVAILABLE;
125         mNumWindows = 1;
126         final String key = getSyncKey();
127         while (result == RESULT_MORE_AVAILABLE) {
128             result = super.performOperation();
129             if (result == RESULT_MORE_AVAILABLE || result == RESULT_DONE) {
130                 mCollectionTypeHandler.cleanup(mContext, mAccount);
131             }
132             // TODO: Clear pending request queue.
133             final String newKey = getSyncKey();
134             if (result == RESULT_MORE_AVAILABLE && key.equals(newKey)) {
135                 LogUtils.e(TAG,
136                         "Server has more data but we have the same key: %s numWindows: %d",
137                         key, mNumWindows);
138                 mNumWindows++;
139             } else {
140                 mNumWindows = 1;
141             }
142         }
143         return result;
144     }
145 
146     @Override
getTimeout()147     protected long getTimeout() {
148         if (mInitialSync) {
149             return 120 * DateUtils.SECOND_IN_MILLIS;
150         }
151         return super.getTimeout();
152     }
153 
154     /**
155      * Get an instance of the correct {@link EasSyncCollectionTypeBase} for a specific collection
156      * type.
157      * @param type The type of the {@link Mailbox} that we're trying to sync.
158      * @return An {@link EasSyncCollectionTypeBase} appropriate for this type.
159      */
getCollectionTypeHandler(final int type)160     private EasSyncCollectionTypeBase getCollectionTypeHandler(final int type) {
161         switch (type) {
162             case Mailbox.TYPE_MAIL:
163             case Mailbox.TYPE_INBOX:
164             case Mailbox.TYPE_DRAFTS:
165             case Mailbox.TYPE_SENT:
166             case Mailbox.TYPE_TRASH:
167             case Mailbox.TYPE_JUNK:
168                 return new EasSyncMail();
169             case Mailbox.TYPE_CALENDAR: {
170                 return new EasSyncCalendar(mContext, mAccount, mMailbox);
171             }
172             case Mailbox.TYPE_CONTACTS:
173                 return new EasSyncContacts(mAccount.mEmailAddress);
174             default:
175                 LogUtils.e(LOG_TAG, "unexpected collectiontype %d", type);
176                 return null;
177         }
178     }
179 }
180