• 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()68     public boolean init() {
69         mCollectionTypeHandler = getCollectionTypeHandler(mMailbox.mType);
70         if (mCollectionTypeHandler == null) {
71             return false;
72         }
73         // Set up traffic stats bookkeeping.
74         final int trafficFlags = TrafficFlags.getSyncFlags(mContext, mAccount);
75         TrafficStats.setThreadStatsTag(trafficFlags | mCollectionTypeHandler.getTrafficFlag());
76         return true;
77     }
78 
79     @Override
getRequestEntity()80     protected HttpEntity getRequestEntity() throws IOException {
81         final String className = Eas.getFolderClass(mMailbox.mType);
82         final String syncKey = getSyncKey();
83         LogUtils.d(TAG, "Syncing account %d mailbox %d (class %s) with syncKey %s", mAccount.mId,
84                 mMailbox.mId, className, syncKey);
85         mInitialSync = EmailContent.isInitialSyncKey(syncKey);
86         final Serializer s = new Serializer();
87         s.start(Tags.SYNC_SYNC);
88         s.start(Tags.SYNC_COLLECTIONS);
89         s.start(Tags.SYNC_COLLECTION);
90         // The "Class" element is removed in EAS 12.1 and later versions
91         if (getProtocolVersion() < Eas.SUPPORTED_PROTOCOL_EX2007_SP1_DOUBLE) {
92             s.data(Tags.SYNC_CLASS, className);
93         }
94         s.data(Tags.SYNC_SYNC_KEY, syncKey);
95         s.data(Tags.SYNC_COLLECTION_ID, mMailbox.mServerId);
96         mCollectionTypeHandler.setSyncOptions(mContext, s, getProtocolVersion(), mAccount, mMailbox,
97                 mInitialSync, mNumWindows);
98         s.end().end().end().done();
99 
100         return makeEntity(s);
101     }
102 
103     @Override
handleResponse(final EasResponse response)104     protected int handleResponse(final EasResponse response)
105             throws IOException, CommandStatusException {
106         try {
107             final AbstractSyncParser parser = mCollectionTypeHandler.getParser(mContext, mAccount,
108                     mMailbox, response.getInputStream());
109             final boolean moreAvailable = parser.parse();
110             if (moreAvailable) {
111                 return RESULT_MORE_AVAILABLE;
112             }
113         } catch (final Parser.EmptyStreamException e) {
114             // This indicates a compressed response which was empty, which is OK.
115         }
116         return RESULT_DONE;
117     }
118 
119     @Override
performOperation()120     public int performOperation() {
121         int result = RESULT_MORE_AVAILABLE;
122         mNumWindows = 1;
123         final String key = getSyncKey();
124         while (result == RESULT_MORE_AVAILABLE) {
125             result = super.performOperation();
126             if (result == RESULT_MORE_AVAILABLE || result == RESULT_DONE) {
127                 mCollectionTypeHandler.cleanup(mContext, mAccount);
128             }
129             // TODO: Clear pending request queue.
130             final String newKey = getSyncKey();
131             if (result == RESULT_MORE_AVAILABLE && key.equals(newKey)) {
132                 LogUtils.e(TAG,
133                         "Server has more data but we have the same key: %s numWindows: %d",
134                         key, mNumWindows);
135                 mNumWindows++;
136             } else {
137                 mNumWindows = 1;
138             }
139         }
140         return result;
141     }
142 
143     @Override
getTimeout()144     protected long getTimeout() {
145         if (mInitialSync) {
146             return 120 * DateUtils.SECOND_IN_MILLIS;
147         }
148         return super.getTimeout();
149     }
150 
151     /**
152      * Get an instance of the correct {@link EasSyncCollectionTypeBase} for a specific collection
153      * type.
154      * @param type The type of the {@link Mailbox} that we're trying to sync.
155      * @return An {@link EasSyncCollectionTypeBase} appropriate for this type.
156      */
getCollectionTypeHandler(final int type)157     private EasSyncCollectionTypeBase getCollectionTypeHandler(final int type) {
158         switch (type) {
159             case Mailbox.TYPE_MAIL:
160             case Mailbox.TYPE_INBOX:
161             case Mailbox.TYPE_DRAFTS:
162             case Mailbox.TYPE_SENT:
163             case Mailbox.TYPE_TRASH:
164             case Mailbox.TYPE_JUNK:
165                 return new EasSyncMail();
166             case Mailbox.TYPE_CALENDAR: {
167                 return new EasSyncCalendar(mContext, mAccount, mMailbox);
168             }
169             case Mailbox.TYPE_CONTACTS:
170                 return new EasSyncContacts(mAccount.mEmailAddress);
171             default:
172                 LogUtils.e(LOG_TAG, "unexpected collectiontype %d", type);
173                 return null;
174         }
175     }
176 }
177