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