Home | History | Annotate | Download | only in service
      1 package com.android.exchange.service;
      2 
      3 import android.content.ContentResolver;
      4 import android.content.Context;
      5 import android.content.SyncResult;
      6 import android.database.Cursor;
      7 import android.os.Bundle;
      8 
      9 import com.android.emailcommon.TrafficFlags;
     10 import com.android.emailcommon.provider.Account;
     11 import com.android.emailcommon.provider.EmailContent.Message;
     12 import com.android.emailcommon.provider.EmailContent.MessageColumns;
     13 import com.android.emailcommon.provider.EmailContent.SyncColumns;
     14 import com.android.emailcommon.provider.Mailbox;
     15 import com.android.emailcommon.service.SyncWindow;
     16 import com.android.exchange.Eas;
     17 import com.android.exchange.adapter.AbstractSyncParser;
     18 import com.android.exchange.adapter.EmailSyncParser;
     19 import com.android.exchange.adapter.Serializer;
     20 import com.android.exchange.adapter.Tags;
     21 
     22 import java.io.IOException;
     23 import java.io.InputStream;
     24 import java.util.ArrayList;
     25 
     26 /**
     27  * Performs an Exchange mailbox sync for "normal" mailboxes.
     28  */
     29 public class EasMailboxSyncHandler extends EasSyncHandler {
     30     /**
     31      * The projection used for building the fetch request list.
     32      */
     33     private static final String[] FETCH_REQUEST_PROJECTION = { SyncColumns.SERVER_ID };
     34     private static final int FETCH_REQUEST_SERVER_ID = 0;
     35 
     36     private static final int EMAIL_WINDOW_SIZE = 10;
     37 
     38     /**
     39      * List of server ids for messages to fetch from the server.
     40      */
     41     private final ArrayList<String> mMessagesToFetch = new ArrayList<String>();
     42 
     43     public EasMailboxSyncHandler(final Context context, final ContentResolver contentResolver,
     44             final Account account, final Mailbox mailbox, final Bundle syncExtras,
     45             final SyncResult syncResult) {
     46         super(context, contentResolver, account, mailbox, syncExtras, syncResult);
     47     }
     48 
     49     private String getEmailFilter() {
     50         final int syncLookback = mMailbox.mSyncLookback == SyncWindow.SYNC_WINDOW_ACCOUNT
     51                 ? mAccount.mSyncLookback : mMailbox.mSyncLookback;
     52         switch (syncLookback) {
     53             case SyncWindow.SYNC_WINDOW_1_DAY:
     54                 return Eas.FILTER_1_DAY;
     55             case SyncWindow.SYNC_WINDOW_3_DAYS:
     56                 return Eas.FILTER_3_DAYS;
     57             case SyncWindow.SYNC_WINDOW_1_WEEK:
     58                 return Eas.FILTER_1_WEEK;
     59             case SyncWindow.SYNC_WINDOW_2_WEEKS:
     60                 return Eas.FILTER_2_WEEKS;
     61             case SyncWindow.SYNC_WINDOW_1_MONTH:
     62                 return Eas.FILTER_1_MONTH;
     63             case SyncWindow.SYNC_WINDOW_ALL:
     64                 return Eas.FILTER_ALL;
     65             default:
     66                 // Auto window is deprecated and will also use the default.
     67                 return Eas.FILTER_1_WEEK;
     68         }
     69     }
     70 
     71     /**
     72      * Find partially loaded messages and add their server ids to {@link #mMessagesToFetch}.
     73      */
     74     private void addToFetchRequestList() {
     75         final Cursor c = mContentResolver.query(Message.CONTENT_URI, FETCH_REQUEST_PROJECTION,
     76                 MessageColumns.FLAG_LOADED + "=" + Message.FLAG_LOADED_PARTIAL + " AND " +
     77                 MessageColumns.MAILBOX_KEY + "=?", new String[] {Long.toString(mMailbox.mId)},
     78                 null);
     79         if (c != null) {
     80             try {
     81                 while (c.moveToNext()) {
     82                     mMessagesToFetch.add(c.getString(FETCH_REQUEST_SERVER_ID));
     83                 }
     84             } finally {
     85                 c.close();
     86             }
     87         }
     88     }
     89 
     90     @Override
     91     protected int getTrafficFlag() {
     92         return TrafficFlags.DATA_EMAIL;
     93     }
     94 
     95     @Override
     96     protected String getFolderClassName() {
     97         return "Email";
     98     }
     99 
    100     @Override
    101     protected AbstractSyncParser getParser(final InputStream is) throws IOException {
    102         return new EmailSyncParser(mContext, mContentResolver, is, mMailbox, mAccount);
    103     }
    104 
    105     @Override
    106     protected void setInitialSyncOptions(final Serializer s) {
    107         // No-op.
    108     }
    109 
    110     @Override
    111     protected void setNonInitialSyncOptions(final Serializer s, int numWindows) throws IOException {
    112         // Check for messages that aren't fully loaded.
    113         addToFetchRequestList();
    114         // The "empty" case is typical; we send a request for changes, and also specify a sync
    115         // window, body preference type (HTML for EAS 12.0 and later; MIME for EAS 2.5), and
    116         // truncation
    117         // If there are fetch requests, we only want the fetches (i.e. no changes from the server)
    118         // so we turn MIME support off.  Note that we are always using EAS 2.5 if there are fetch
    119         // requests
    120         if (mMessagesToFetch.isEmpty()) {
    121             // Permanently delete if in trash mailbox
    122             // In Exchange 2003, deletes-as-moves tag = true; no tag = false
    123             // In Exchange 2007 and up, deletes-as-moves tag is "0" (false) or "1" (true)
    124             final boolean isTrashMailbox = mMailbox.mType == Mailbox.TYPE_TRASH;
    125             if (getProtocolVersion() < Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) {
    126                 if (!isTrashMailbox) {
    127                     s.tag(Tags.SYNC_DELETES_AS_MOVES);
    128                 }
    129             } else {
    130                 s.data(Tags.SYNC_DELETES_AS_MOVES, isTrashMailbox ? "0" : "1");
    131             }
    132             s.tag(Tags.SYNC_GET_CHANGES);
    133 
    134             final int windowSize = numWindows * EMAIL_WINDOW_SIZE;
    135             if (windowSize > MAX_WINDOW_SIZE  + EMAIL_WINDOW_SIZE) {
    136                 throw new IOException("Max window size reached and still no data");
    137             }
    138             s.data(Tags.SYNC_WINDOW_SIZE,
    139                     String.valueOf(windowSize < MAX_WINDOW_SIZE ? windowSize : MAX_WINDOW_SIZE));
    140             s.start(Tags.SYNC_OPTIONS);
    141             // Set the lookback appropriately (EAS calls this a "filter")
    142             s.data(Tags.SYNC_FILTER_TYPE, getEmailFilter());
    143             // Set the truncation amount for all classes
    144             if (getProtocolVersion() >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) {
    145                 s.start(Tags.BASE_BODY_PREFERENCE);
    146                 // HTML for email
    147                 s.data(Tags.BASE_TYPE, Eas.BODY_PREFERENCE_HTML);
    148                 s.data(Tags.BASE_TRUNCATION_SIZE, Eas.EAS12_TRUNCATION_SIZE);
    149                 s.end();
    150             } else {
    151                 // Use MIME data for EAS 2.5
    152                 s.data(Tags.SYNC_MIME_SUPPORT, Eas.MIME_BODY_PREFERENCE_MIME);
    153                 s.data(Tags.SYNC_MIME_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE);
    154             }
    155             s.end();
    156         } else {
    157             // If we have any messages that are not fully loaded, ask for plain text rather than
    158             // MIME, to guarantee we'll get usable text body. This also means we should NOT ask for
    159             // new messages -- we only want data for the message explicitly fetched.
    160             s.start(Tags.SYNC_OPTIONS);
    161             s.data(Tags.SYNC_MIME_SUPPORT, Eas.MIME_BODY_PREFERENCE_TEXT);
    162             s.data(Tags.SYNC_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE);
    163             s.end();
    164         }
    165     }
    166 
    167     /**
    168      * Add FETCH commands for messages that need a body (i.e. we didn't find it during our earlier
    169      * sync; this happens only in EAS 2.5 where the body couldn't be found after parsing the
    170      * message's MIME data).
    171      * @param s The {@link Serializer} for this sync request.
    172      * @throws IOException
    173      */
    174     private void addFetchCommands(final Serializer s) throws IOException {
    175         if (!mMessagesToFetch.isEmpty()) {
    176             s.start(Tags.SYNC_COMMANDS);
    177             for (final String serverId : mMessagesToFetch) {
    178                 s.start(Tags.SYNC_FETCH).data(Tags.SYNC_SERVER_ID, serverId).end();
    179             }
    180             s.end();
    181         }
    182     }
    183 
    184     @Override
    185     protected void setUpsyncCommands(final Serializer s) throws IOException {
    186         addFetchCommands(s);
    187     }
    188 
    189     @Override
    190     protected void cleanup(final int syncResult) {
    191         if (syncResult == SYNC_RESULT_MORE_AVAILABLE) {
    192             // Prepare our member variables for another sync request.
    193             mMessagesToFetch.clear();
    194         }
    195     }
    196 }
    197