Home | History | Annotate | Download | only in eas
      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.
     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() {
     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
     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
    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
    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
    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      */
    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