Home | History | Annotate | Download | only in adapter
      1 /*
      2  * Copyright (C) 2008-2009 Marc Blank
      3  * Licensed to The Android Open Source Project.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package com.android.exchange.adapter;
     19 
     20 import com.android.email.provider.EmailContent.Account;
     21 import com.android.email.provider.EmailContent.Mailbox;
     22 import com.android.email.provider.EmailContent.MailboxColumns;
     23 import com.android.exchange.EasSyncService;
     24 import com.android.exchange.SyncManager;
     25 
     26 import android.content.ContentResolver;
     27 import android.content.ContentValues;
     28 import android.content.Context;
     29 
     30 import java.io.IOException;
     31 import java.io.InputStream;
     32 
     33 /**
     34  * Base class for the Email and PIM sync parsers
     35  * Handles the basic flow of syncKeys, looping to get more data, handling errors, etc.
     36  * Each subclass must implement a handful of methods that relate specifically to the data type
     37  *
     38  */
     39 public abstract class AbstractSyncParser extends Parser {
     40 
     41     protected EasSyncService mService;
     42     protected Mailbox mMailbox;
     43     protected Account mAccount;
     44     protected Context mContext;
     45     protected ContentResolver mContentResolver;
     46     protected AbstractSyncAdapter mAdapter;
     47 
     48     private boolean mLooping;
     49 
     50     public AbstractSyncParser(InputStream in, AbstractSyncAdapter adapter) throws IOException {
     51         super(in);
     52         mAdapter = adapter;
     53         mService = adapter.mService;
     54         mContext = mService.mContext;
     55         mContentResolver = mContext.getContentResolver();
     56         mMailbox = mService.mMailbox;
     57         mAccount = mService.mAccount;
     58     }
     59 
     60     /**
     61      * Read, parse, and act on incoming commands from the Exchange server
     62      * @throws IOException if the connection is broken
     63      */
     64     public abstract void commandsParser() throws IOException;
     65 
     66     /**
     67      * Read, parse, and act on server responses
     68      * @throws IOException
     69      */
     70     public abstract void responsesParser() throws IOException;
     71 
     72     /**
     73      * Commit any changes found during parsing
     74      * @throws IOException
     75      */
     76     public abstract void commit() throws IOException;
     77 
     78     /**
     79      * Delete all records of this class in this account
     80      */
     81     public abstract void wipe();
     82 
     83     public boolean isLooping() {
     84         return mLooping;
     85     }
     86 
     87     /**
     88      * Loop through the top-level structure coming from the Exchange server
     89      * Sync keys and the more available flag are handled here, whereas specific data parsing
     90      * is handled by abstract methods implemented for each data class (e.g. Email, Contacts, etc.)
     91      */
     92     @Override
     93     public boolean parse() throws IOException {
     94         int status;
     95         boolean moreAvailable = false;
     96         boolean newSyncKey = false;
     97         int interval = mMailbox.mSyncInterval;
     98         mLooping = false;
     99         // If we're not at the top of the xml tree, throw an exception
    100         if (nextTag(START_DOCUMENT) != Tags.SYNC_SYNC) {
    101             throw new EasParserException();
    102         }
    103 
    104         boolean mailboxUpdated = false;
    105         ContentValues cv = new ContentValues();
    106 
    107         // Loop here through the remaining xml
    108         while (nextTag(START_DOCUMENT) != END_DOCUMENT) {
    109             if (tag == Tags.SYNC_COLLECTION || tag == Tags.SYNC_COLLECTIONS) {
    110                 // Ignore these tags, since we've only got one collection syncing in this loop
    111             } else if (tag == Tags.SYNC_STATUS) {
    112                 // Status = 1 is success; everything else is a failure
    113                 status = getValueInt();
    114                 if (status != 1) {
    115                     mService.errorLog("Sync failed: " + status);
    116                     // Status = 3 means invalid sync key
    117                     if (status == 3) {
    118                         // Must delete all of the data and start over with syncKey of "0"
    119                         mAdapter.setSyncKey("0", false);
    120                         // Make this a push box through the first sync
    121                         // TODO Make frequency conditional on user settings!
    122                         mMailbox.mSyncInterval = Mailbox.CHECK_INTERVAL_PUSH;
    123                         mService.errorLog("Bad sync key; RESET and delete data");
    124                         wipe();
    125                         // Indicate there's more so that we'll start syncing again
    126                         moreAvailable = true;
    127                     } else if (status == 8) {
    128                         // This is Bad; it means the server doesn't recognize the serverId it
    129                         // sent us.  What's needed is a refresh of the folder list.
    130                         SyncManager.reloadFolderList(mContext, mAccount.mId, true);
    131                     }
    132                     // TODO Look at other error codes and consider what's to be done
    133                 }
    134             } else if (tag == Tags.SYNC_COMMANDS) {
    135                 commandsParser();
    136             } else if (tag == Tags.SYNC_RESPONSES) {
    137                 responsesParser();
    138             } else if (tag == Tags.SYNC_MORE_AVAILABLE) {
    139                 moreAvailable = true;
    140             } else if (tag == Tags.SYNC_SYNC_KEY) {
    141                 if (mAdapter.getSyncKey().equals("0")) {
    142                     moreAvailable = true;
    143                 }
    144                 String newKey = getValue();
    145                 userLog("Parsed key for ", mMailbox.mDisplayName, ": ", newKey);
    146                 if (!newKey.equals(mMailbox.mSyncKey)) {
    147                     mAdapter.setSyncKey(newKey, true);
    148                     cv.put(MailboxColumns.SYNC_KEY, newKey);
    149                     mailboxUpdated = true;
    150                     newSyncKey = true;
    151                 }
    152                 // If we were pushing (i.e. auto-start), now we'll become ping-triggered
    153                 if (mMailbox.mSyncInterval == Mailbox.CHECK_INTERVAL_PUSH) {
    154                     mMailbox.mSyncInterval = Mailbox.CHECK_INTERVAL_PING;
    155                 }
    156            } else {
    157                 skipTag();
    158            }
    159         }
    160 
    161         // If we don't have a new sync key, ignore moreAvailable (or we'll loop)
    162         if (moreAvailable && !newSyncKey) {
    163             mLooping = true;
    164         }
    165 
    166         // Commit any changes
    167         commit();
    168 
    169         boolean abortSyncs = false;
    170 
    171         // If the sync interval has changed, we need to save it
    172         if (mMailbox.mSyncInterval != interval) {
    173             cv.put(MailboxColumns.SYNC_INTERVAL, mMailbox.mSyncInterval);
    174             mailboxUpdated = true;
    175         // If there are changes, and we were bounced from push/ping, try again
    176         } else if (mService.mChangeCount > 0 &&
    177                 mAccount.mSyncInterval == Account.CHECK_INTERVAL_PUSH &&
    178                 mMailbox.mSyncInterval > 0) {
    179             userLog("Changes found to ping loop mailbox ", mMailbox.mDisplayName, ": will ping.");
    180             cv.put(MailboxColumns.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PING);
    181             mailboxUpdated = true;
    182             abortSyncs = true;
    183         }
    184 
    185         if (mailboxUpdated) {
    186              synchronized (mService.getSynchronizer()) {
    187                 if (!mService.isStopped()) {
    188                      mMailbox.update(mContext, cv);
    189                 }
    190             }
    191         }
    192 
    193         if (abortSyncs) {
    194             userLog("Aborting account syncs due to mailbox change to ping...");
    195             SyncManager.stopAccountSyncs(mAccount.mId);
    196         }
    197 
    198         // Let the caller know that there's more to do
    199         userLog("Returning moreAvailable = " + moreAvailable);
    200         return moreAvailable;
    201     }
    202 
    203     void userLog(String ...strings) {
    204         mService.userLog(strings);
    205     }
    206 
    207     void userLog(String string, int num, String string2) {
    208         mService.userLog(string, num, string2);
    209     }
    210 }
    211