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(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