EasSyncBase.java revision 6506c8ec2c09be76ee70bba69fcd70d64a11982f
1package com.android.exchange.eas;
2
3import android.content.Context;
4import android.net.TrafficStats;
5import android.text.format.DateUtils;
6
7import com.android.emailcommon.TrafficFlags;
8import com.android.emailcommon.provider.Account;
9import com.android.emailcommon.provider.EmailContent;
10import com.android.emailcommon.provider.Mailbox;
11import com.android.exchange.CommandStatusException;
12import com.android.exchange.Eas;
13import com.android.exchange.EasResponse;
14import com.android.exchange.adapter.AbstractSyncParser;
15import com.android.exchange.adapter.Parser;
16import com.android.exchange.adapter.Serializer;
17import com.android.exchange.adapter.Tags;
18import com.android.mail.utils.LogUtils;
19
20import org.apache.http.HttpEntity;
21
22import 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 */
28public 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.
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     */
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
63    protected String getCommand() {
64        return "Sync";
65    }
66
67    @Override
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
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
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
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
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     */
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