1b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank/*
2b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank * Copyright (C) 2008-2009 Marc Blank
3b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank * Licensed to The Android Open Source Project.
4b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank *
5b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank * Licensed under the Apache License, Version 2.0 (the "License");
6b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank * you may not use this file except in compliance with the License.
7b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank * You may obtain a copy of the License at
8b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank *
9b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank *      http://www.apache.org/licenses/LICENSE-2.0
10b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank *
11b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank * Unless required by applicable law or agreed to in writing, software
12b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank * distributed under the License is distributed on an "AS IS" BASIS,
13b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank * See the License for the specific language governing permissions and
15b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank * limitations under the License.
16b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank */
17b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank
18b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blankpackage com.android.exchange.adapter;
19b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank
207375d950e4c71a3ab2c8c726b7641c825fb92efcMarc Blankimport com.android.email.provider.EmailContent.Account;
217375d950e4c71a3ab2c8c726b7641c825fb92efcMarc Blankimport com.android.email.provider.EmailContent.Mailbox;
227375d950e4c71a3ab2c8c726b7641c825fb92efcMarc Blankimport com.android.email.provider.EmailContent.MailboxColumns;
237375d950e4c71a3ab2c8c726b7641c825fb92efcMarc Blankimport com.android.exchange.EasSyncService;
24b2bd8a8095f7cdfd4ea2a2c560cbb7a86ce3effaMarc Blankimport com.android.exchange.SyncManager;
25b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank
26b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blankimport android.content.ContentResolver;
279e2c6bd5f21f2d19eef7ebfe30e6fdf94ede0857Andrew Stadlerimport android.content.ContentValues;
28b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blankimport android.content.Context;
29b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank
307375d950e4c71a3ab2c8c726b7641c825fb92efcMarc Blankimport java.io.IOException;
317375d950e4c71a3ab2c8c726b7641c825fb92efcMarc Blankimport java.io.InputStream;
32b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank
33b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank/**
34b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank * Base class for the Email and PIM sync parsers
35b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank * Handles the basic flow of syncKeys, looping to get more data, handling errors, etc.
36b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank * Each subclass must implement a handful of methods that relate specifically to the data type
37b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank *
38b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank */
39b2bd8a8095f7cdfd4ea2a2c560cbb7a86ce3effaMarc Blankpublic abstract class AbstractSyncParser extends Parser {
40b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank
416b52af214c9961d8d11220a851a24928a5b91553Marc Blank    protected EasSyncService mService;
426b52af214c9961d8d11220a851a24928a5b91553Marc Blank    protected Mailbox mMailbox;
436b52af214c9961d8d11220a851a24928a5b91553Marc Blank    protected Account mAccount;
446b52af214c9961d8d11220a851a24928a5b91553Marc Blank    protected Context mContext;
456b52af214c9961d8d11220a851a24928a5b91553Marc Blank    protected ContentResolver mContentResolver;
46aa69a174774699d452233f8ffece021f9851011eMarc Blank    protected AbstractSyncAdapter mAdapter;
47b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank
48274492db09d464879903debf6645443b9be9a957Marc Blank    private boolean mLooping;
49274492db09d464879903debf6645443b9be9a957Marc Blank
50aa69a174774699d452233f8ffece021f9851011eMarc Blank    public AbstractSyncParser(InputStream in, AbstractSyncAdapter adapter) throws IOException {
51b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank        super(in);
52aa69a174774699d452233f8ffece021f9851011eMarc Blank        mAdapter = adapter;
53aa69a174774699d452233f8ffece021f9851011eMarc Blank        mService = adapter.mService;
54b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank        mContext = mService.mContext;
55b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank        mContentResolver = mContext.getContentResolver();
56b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank        mMailbox = mService.mMailbox;
57b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank        mAccount = mService.mAccount;
58b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank    }
59b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank
60b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank    /**
61b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank     * Read, parse, and act on incoming commands from the Exchange server
62b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank     * @throws IOException if the connection is broken
63b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank     */
64b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank    public abstract void commandsParser() throws IOException;
65b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank
66b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank    /**
67b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank     * Read, parse, and act on server responses
68b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank     * @throws IOException
69b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank     */
70aa69a174774699d452233f8ffece021f9851011eMarc Blank    public abstract void responsesParser() throws IOException;
71aa69a174774699d452233f8ffece021f9851011eMarc Blank
72aa69a174774699d452233f8ffece021f9851011eMarc Blank    /**
73aa69a174774699d452233f8ffece021f9851011eMarc Blank     * Commit any changes found during parsing
74aa69a174774699d452233f8ffece021f9851011eMarc Blank     * @throws IOException
75aa69a174774699d452233f8ffece021f9851011eMarc Blank     */
76aa69a174774699d452233f8ffece021f9851011eMarc Blank    public abstract void commit() throws IOException;
77b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank
78b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank    /**
79b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank     * Delete all records of this class in this account
80b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank     */
81b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank    public abstract void wipe();
82b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank
83274492db09d464879903debf6645443b9be9a957Marc Blank    public boolean isLooping() {
84274492db09d464879903debf6645443b9be9a957Marc Blank        return mLooping;
85274492db09d464879903debf6645443b9be9a957Marc Blank    }
86274492db09d464879903debf6645443b9be9a957Marc Blank
87b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank    /**
88b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank     * Loop through the top-level structure coming from the Exchange server
89b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank     * Sync keys and the more available flag are handled here, whereas specific data parsing
90b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank     * is handled by abstract methods implemented for each data class (e.g. Email, Contacts, etc.)
91b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank     */
92f00dccd32125c727cc18d837b59c15c95f5d78bcMarc Blank    @Override
93b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank    public boolean parse() throws IOException {
94b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank        int status;
95b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank        boolean moreAvailable = false;
96d676ab6a982ad76bb29beef5894823e5c6f18a77Marc Blank        boolean newSyncKey = false;
97fc579d4477ed3632de0a04678ea490875de85effMarc Blank        int interval = mMailbox.mSyncInterval;
98274492db09d464879903debf6645443b9be9a957Marc Blank        mLooping = false;
99b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank        // If we're not at the top of the xml tree, throw an exception
100f00dccd32125c727cc18d837b59c15c95f5d78bcMarc Blank        if (nextTag(START_DOCUMENT) != Tags.SYNC_SYNC) {
1010cbdba8d62c297c3aad7c833f5f5716eabc2c501Marc Blank            throw new EasParserException();
102b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank        }
103317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank
104317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank        boolean mailboxUpdated = false;
105317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank        ContentValues cv = new ContentValues();
106317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank
107b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank        // Loop here through the remaining xml
108b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank        while (nextTag(START_DOCUMENT) != END_DOCUMENT) {
109f00dccd32125c727cc18d837b59c15c95f5d78bcMarc Blank            if (tag == Tags.SYNC_COLLECTION || tag == Tags.SYNC_COLLECTIONS) {
110b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                // Ignore these tags, since we've only got one collection syncing in this loop
111f00dccd32125c727cc18d837b59c15c95f5d78bcMarc Blank            } else if (tag == Tags.SYNC_STATUS) {
112b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                // Status = 1 is success; everything else is a failure
113b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                status = getValueInt();
114b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                if (status != 1) {
115b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                    mService.errorLog("Sync failed: " + status);
116b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                    // Status = 3 means invalid sync key
117b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                    if (status == 3) {
118b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                        // Must delete all of the data and start over with syncKey of "0"
119aa69a174774699d452233f8ffece021f9851011eMarc Blank                        mAdapter.setSyncKey("0", false);
120b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                        // Make this a push box through the first sync
121b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                        // TODO Make frequency conditional on user settings!
1228db0babf726afc4018a721aa589a5ab461630948Marc Blank                        mMailbox.mSyncInterval = Mailbox.CHECK_INTERVAL_PUSH;
123f2113c1dcfa42b8ab8271ac0ea69c62698acdb61Marc Blank                        mService.errorLog("Bad sync key; RESET and delete data");
124b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                        wipe();
125b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                        // Indicate there's more so that we'll start syncing again
126b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                        moreAvailable = true;
127b2bd8a8095f7cdfd4ea2a2c560cbb7a86ce3effaMarc Blank                    } else if (status == 8) {
128b2bd8a8095f7cdfd4ea2a2c560cbb7a86ce3effaMarc Blank                        // This is Bad; it means the server doesn't recognize the serverId it
129b2bd8a8095f7cdfd4ea2a2c560cbb7a86ce3effaMarc Blank                        // sent us.  What's needed is a refresh of the folder list.
130fc579d4477ed3632de0a04678ea490875de85effMarc Blank                        SyncManager.reloadFolderList(mContext, mAccount.mId, true);
131b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                    }
132b2bd8a8095f7cdfd4ea2a2c560cbb7a86ce3effaMarc Blank                    // TODO Look at other error codes and consider what's to be done
133b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                }
134f00dccd32125c727cc18d837b59c15c95f5d78bcMarc Blank            } else if (tag == Tags.SYNC_COMMANDS) {
135b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                commandsParser();
136f00dccd32125c727cc18d837b59c15c95f5d78bcMarc Blank            } else if (tag == Tags.SYNC_RESPONSES) {
137b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                responsesParser();
138f00dccd32125c727cc18d837b59c15c95f5d78bcMarc Blank            } else if (tag == Tags.SYNC_MORE_AVAILABLE) {
139b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                moreAvailable = true;
140f00dccd32125c727cc18d837b59c15c95f5d78bcMarc Blank            } else if (tag == Tags.SYNC_SYNC_KEY) {
141aa69a174774699d452233f8ffece021f9851011eMarc Blank                if (mAdapter.getSyncKey().equals("0")) {
142b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                    moreAvailable = true;
143fc579d4477ed3632de0a04678ea490875de85effMarc Blank                }
144b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                String newKey = getValue();
1451ba0fd37a158c1477025aa530206fefdc4b3fe5bMarc Blank                userLog("Parsed key for ", mMailbox.mDisplayName, ": ", newKey);
146317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank                if (!newKey.equals(mMailbox.mSyncKey)) {
147317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank                    mAdapter.setSyncKey(newKey, true);
148317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank                    cv.put(MailboxColumns.SYNC_KEY, newKey);
149317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank                    mailboxUpdated = true;
150d676ab6a982ad76bb29beef5894823e5c6f18a77Marc Blank                    newSyncKey = true;
151d676ab6a982ad76bb29beef5894823e5c6f18a77Marc Blank                }
152b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                // If we were pushing (i.e. auto-start), now we'll become ping-triggered
1538db0babf726afc4018a721aa589a5ab461630948Marc Blank                if (mMailbox.mSyncInterval == Mailbox.CHECK_INTERVAL_PUSH) {
1548db0babf726afc4018a721aa589a5ab461630948Marc Blank                    mMailbox.mSyncInterval = Mailbox.CHECK_INTERVAL_PING;
155b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                }
156b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank           } else {
157b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank                skipTag();
158b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank           }
159b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank        }
160b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank
161d676ab6a982ad76bb29beef5894823e5c6f18a77Marc Blank        // If we don't have a new sync key, ignore moreAvailable (or we'll loop)
162d676ab6a982ad76bb29beef5894823e5c6f18a77Marc Blank        if (moreAvailable && !newSyncKey) {
163274492db09d464879903debf6645443b9be9a957Marc Blank            mLooping = true;
164d676ab6a982ad76bb29beef5894823e5c6f18a77Marc Blank        }
165d676ab6a982ad76bb29beef5894823e5c6f18a77Marc Blank
166aa69a174774699d452233f8ffece021f9851011eMarc Blank        // Commit any changes
167aa69a174774699d452233f8ffece021f9851011eMarc Blank        commit();
168aa69a174774699d452233f8ffece021f9851011eMarc Blank
1693fef6f4a47c0bf4874d901a9b0e95e14ff7fb33aMarc Blank        boolean abortSyncs = false;
1703fef6f4a47c0bf4874d901a9b0e95e14ff7fb33aMarc Blank
171317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank        // If the sync interval has changed, we need to save it
172aa69a174774699d452233f8ffece021f9851011eMarc Blank        if (mMailbox.mSyncInterval != interval) {
173317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank            cv.put(MailboxColumns.SYNC_INTERVAL, mMailbox.mSyncInterval);
174317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank            mailboxUpdated = true;
175317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank        // If there are changes, and we were bounced from push/ping, try again
17684cb2bcd59b421a3b515985c0115900ef16ac323Marc Blank        } else if (mService.mChangeCount > 0 &&
17784cb2bcd59b421a3b515985c0115900ef16ac323Marc Blank                mAccount.mSyncInterval == Account.CHECK_INTERVAL_PUSH &&
17884cb2bcd59b421a3b515985c0115900ef16ac323Marc Blank                mMailbox.mSyncInterval > 0) {
179317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank            userLog("Changes found to ping loop mailbox ", mMailbox.mDisplayName, ": will ping.");
180317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank            cv.put(MailboxColumns.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PING);
181317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank            mailboxUpdated = true;
1823fef6f4a47c0bf4874d901a9b0e95e14ff7fb33aMarc Blank            abortSyncs = true;
183317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank        }
184317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank
185317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank        if (mailboxUpdated) {
186317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank             synchronized (mService.getSynchronizer()) {
18784cb2bcd59b421a3b515985c0115900ef16ac323Marc Blank                if (!mService.isStopped()) {
188317a92f51f178cfa19cc3f92f03f54640ac9ad88Marc Blank                     mMailbox.update(mContext, cv);
18984cb2bcd59b421a3b515985c0115900ef16ac323Marc Blank                }
19084cb2bcd59b421a3b515985c0115900ef16ac323Marc Blank            }
191b9d6ba11c729d3ae6a59ceae780d9bd54032d6ddMarc Blank        }
192fc579d4477ed3632de0a04678ea490875de85effMarc Blank
1933fef6f4a47c0bf4874d901a9b0e95e14ff7fb33aMarc Blank        if (abortSyncs) {
1943fef6f4a47c0bf4874d901a9b0e95e14ff7fb33aMarc Blank            userLog("Aborting account syncs due to mailbox change to ping...");
1953fef6f4a47c0bf4874d901a9b0e95e14ff7fb33aMarc Blank            SyncManager.stopAccountSyncs(mAccount.mId);
1963fef6f4a47c0bf4874d901a9b0e95e14ff7fb33aMarc Blank        }
1973fef6f4a47c0bf4874d901a9b0e95e14ff7fb33aMarc Blank
198b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank        // Let the caller know that there's more to do
19936842abe9f5f022685bff1a7dd72f4664aba9326Marc Blank        userLog("Returning moreAvailable = " + moreAvailable);
200b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank        return moreAvailable;
201b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank    }
202b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank
2031ba0fd37a158c1477025aa530206fefdc4b3fe5bMarc Blank    void userLog(String ...strings) {
2041ba0fd37a158c1477025aa530206fefdc4b3fe5bMarc Blank        mService.userLog(strings);
2051ba0fd37a158c1477025aa530206fefdc4b3fe5bMarc Blank    }
206b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank
2071ba0fd37a158c1477025aa530206fefdc4b3fe5bMarc Blank    void userLog(String string, int num, String string2) {
2081ba0fd37a158c1477025aa530206fefdc4b3fe5bMarc Blank        mService.userLog(string, num, string2);
2091ba0fd37a158c1477025aa530206fefdc4b3fe5bMarc Blank    }
210b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank}
211