AbstractSyncParser.java revision 1431215b5fc40d0d6498b0fe602ad4d1b8a66ff3
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
18package com.android.exchange.adapter;
19
20import com.android.email.provider.EmailContent.Account;
21import com.android.email.provider.EmailContent.Mailbox;
22import com.android.email.provider.EmailContent.MailboxColumns;
23import com.android.exchange.EasSyncService;
24import com.android.exchange.SyncManager;
25
26import android.content.ContentResolver;
27import android.content.ContentValues;
28import android.content.Context;
29
30import java.io.IOException;
31import 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 */
39public 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
47    public AbstractSyncParser(InputStream in, EasSyncService _service) throws IOException {
48        super(in);
49        mService = _service;
50        mContext = mService.mContext;
51        mContentResolver = mContext.getContentResolver();
52        mMailbox = mService.mMailbox;
53        mAccount = mService.mAccount;
54    }
55
56    /**
57     * Read, parse, and act on incoming commands from the Exchange server
58     * @throws IOException if the connection is broken
59     */
60    public abstract void commandsParser() throws IOException;
61
62    /**
63     * Read, parse, and act on server responses
64     * Email doesn't have any, so this isn't yet implemented anywhere.  It will become abstract,
65     * in the near future, however.
66     * @throws IOException
67     */
68    public void responsesParser() throws IOException {
69        // Placeholder until needed; will become an abstract method
70    }
71
72    /**
73     * Delete all records of this class in this account
74     */
75    public abstract void wipe();
76
77    /**
78     * Loop through the top-level structure coming from the Exchange server
79     * Sync keys and the more available flag are handled here, whereas specific data parsing
80     * is handled by abstract methods implemented for each data class (e.g. Email, Contacts, etc.)
81     */
82    @Override
83    public boolean parse() throws IOException {
84        int status;
85        boolean moreAvailable = false;
86        int interval = mMailbox.mSyncInterval;
87
88        // If we're not at the top of the xml tree, throw an exception
89        if (nextTag(START_DOCUMENT) != Tags.SYNC_SYNC) {
90            throw new EasParserException();
91        }
92        // Loop here through the remaining xml
93        while (nextTag(START_DOCUMENT) != END_DOCUMENT) {
94            if (tag == Tags.SYNC_COLLECTION || tag == Tags.SYNC_COLLECTIONS) {
95                // Ignore these tags, since we've only got one collection syncing in this loop
96            } else if (tag == Tags.SYNC_STATUS) {
97                // Status = 1 is success; everything else is a failure
98                status = getValueInt();
99                if (status != 1) {
100                    mService.errorLog("Sync failed: " + status);
101                    // Status = 3 means invalid sync key
102                    if (status == 3) {
103                        // Must delete all of the data and start over with syncKey of "0"
104                        mMailbox.mSyncKey = "0";
105                        // Make this a push box through the first sync
106                        // TODO Make frequency conditional on user settings!
107                        mMailbox.mSyncInterval = Mailbox.CHECK_INTERVAL_PUSH;
108                        mService.errorLog("Bad sync key; RESET and delete data");
109                        wipe();
110                        // Indicate there's more so that we'll start syncing again
111                        moreAvailable = true;
112                    } else if (status == 8) {
113                        // This is Bad; it means the server doesn't recognize the serverId it
114                        // sent us.  What's needed is a refresh of the folder list.
115                        SyncManager.reloadFolderList(mContext, mAccount.mId, true);
116                    }
117                    // TODO Look at other error codes and consider what's to be done
118                }
119            } else if (tag == Tags.SYNC_COMMANDS) {
120                commandsParser();
121            } else if (tag == Tags.SYNC_RESPONSES) {
122                responsesParser();
123            } else if (tag == Tags.SYNC_MORE_AVAILABLE) {
124                moreAvailable = true;
125            } else if (tag == Tags.SYNC_SYNC_KEY) {
126                if (mMailbox.mSyncKey.equals("0")) {
127                    moreAvailable = true;
128                }
129                String newKey = getValue();
130                userLog("Parsed key for ", mMailbox.mDisplayName, ": ", newKey);
131                mMailbox.mSyncKey = newKey;
132                // If we were pushing (i.e. auto-start), now we'll become ping-triggered
133                if (mMailbox.mSyncInterval == Mailbox.CHECK_INTERVAL_PUSH) {
134                    mMailbox.mSyncInterval = Mailbox.CHECK_INTERVAL_PING;
135                }
136           } else {
137                skipTag();
138           }
139        }
140
141        // If the sync interval has changed, or if no commands were parsed save the change
142        if (mMailbox.mSyncInterval != interval || mService.mChangeCount == 0) {
143            synchronized (mService.getSynchronizer()) {
144                if (!mService.isStopped()) {
145                    // Make sure we save away the new syncFrequency
146                    ContentValues cv = new ContentValues();
147                    if (mService.mChangeCount == 0) {
148                        cv.put(MailboxColumns.SYNC_KEY, mMailbox.mSyncKey);
149                    }
150                    cv.put(MailboxColumns.SYNC_INTERVAL, mMailbox.mSyncInterval);
151                    mMailbox.update(mContext, cv);
152                }
153            }
154        // If this box has backed off of push, and there were changes, try to change back to
155        // ping; it seems to help at times
156        } else if (mService.mChangeCount > 0 &&
157                mAccount.mSyncInterval == Account.CHECK_INTERVAL_PUSH &&
158                mMailbox.mSyncInterval > 0) {
159            synchronized (mService.getSynchronizer()) {
160                if (!mService.isStopped()) {
161                    ContentValues cv = new ContentValues();
162                    cv.put(MailboxColumns.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PING);
163                    mMailbox.update(mContext, cv);
164                    userLog("Changes found to ping loop mailbox ", mMailbox.mDisplayName,
165                            ": switch back to ping.");
166                }
167            }
168        }
169
170        // Let the caller know that there's more to do
171        return moreAvailable;
172    }
173
174    void userLog(String ...strings) {
175        mService.userLog(strings);
176    }
177
178    void userLog(String string, int num, String string2) {
179        mService.userLog(string, num, string2);
180    }
181}
182