1ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank/* 2ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Copyright (C) 2008-2009 Marc Blank 3ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Licensed to The Android Open Source Project. 4ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 5ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Licensed under the Apache License, Version 2.0 (the "License"); 6ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * you may not use this file except in compliance with the License. 7ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * You may obtain a copy of the License at 8ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 9ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * http://www.apache.org/licenses/LICENSE-2.0 10ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 11ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Unless required by applicable law or agreed to in writing, software 12ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * distributed under the License is distributed on an "AS IS" BASIS, 13ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * See the License for the specific language governing permissions and 15ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * limitations under the License. 16ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 17ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 18ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankpackage com.android.exchange.adapter; 19ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 20680e59f477fe2221ffa6d5bb6b3c8fe64c1f5a74Marc Blankimport android.content.ContentResolver; 21680e59f477fe2221ffa6d5bb6b3c8fe64c1f5a74Marc Blankimport android.content.ContentValues; 22680e59f477fe2221ffa6d5bb6b3c8fe64c1f5a74Marc Blankimport android.content.Context; 2336e181376b06c8905af2d22bf494a2423f50aa65Alon Albertimport android.content.OperationApplicationException; 2436e181376b06c8905af2d22bf494a2423f50aa65Alon Albertimport android.os.RemoteException; 25680e59f477fe2221ffa6d5bb6b3c8fe64c1f5a74Marc Blank 267372782488977df778a33d990401ce9e397f646bMarc Blankimport com.android.emailcommon.provider.Account; 27c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.MailboxColumns; 284d8774462ace9a45154b2df418b9f2fe7a9c685dBen Komaloimport com.android.emailcommon.provider.Mailbox; 2977186bb1a174432ef272584374942d8b9228e39cMarc Blankimport com.android.exchange.CommandStatusException; 3077186bb1a174432ef272584374942d8b9228e39cMarc Blankimport com.android.exchange.CommandStatusException.CommandStatus; 31110837ebff288a75f9bda067c38e2c46797d99b5Alon Albertimport com.android.exchange.Eas; 3236e181376b06c8905af2d22bf494a2423f50aa65Alon Albertimport com.android.mail.utils.LogUtils; 33ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 3467698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport java.io.IOException; 3567698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport java.io.InputStream; 36ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 37ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank/** 38ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Base class for the Email and PIM sync parsers 39ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Handles the basic flow of syncKeys, looping to get more data, handling errors, etc. 40ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Each subclass must implement a handful of methods that relate specifically to the data type 41ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 42ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 43368adeb5779fed5d64770d2131125dd93e43ab78Marc Blankpublic abstract class AbstractSyncParser extends Parser { 44110837ebff288a75f9bda067c38e2c46797d99b5Alon Albert private static final String TAG = Eas.LOG_TAG; 4536e181376b06c8905af2d22bf494a2423f50aa65Alon Albert 461b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank protected Mailbox mMailbox; 471b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank protected Account mAccount; 481b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank protected Context mContext; 491b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank protected ContentResolver mContentResolver; 50ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 518efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank private boolean mLooping; 528efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank 53c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu public AbstractSyncParser(final Context context, final ContentResolver resolver, 54c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu final InputStream in, final Mailbox mailbox, final Account account) throws IOException { 55c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu super(in); 56c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu init(context, resolver, mailbox, account); 57c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu } 58c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu 5948af7392c82262d17700e3fbdccf3a582809d449Marc Blank public AbstractSyncParser(InputStream in, AbstractSyncAdapter adapter) throws IOException { 60ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank super(in); 6126d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank init(adapter); 6226d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 6326d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 6426d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank public AbstractSyncParser(Parser p, AbstractSyncAdapter adapter) throws IOException { 6526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank super(p); 6626d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank init(adapter); 6726d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 6826d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 69c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu private void init(final AbstractSyncAdapter adapter) { 70c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu init(adapter.mContext, adapter.mContext.getContentResolver(), adapter.mMailbox, 71c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu adapter.mAccount); 72c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu } 73c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu 74c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu private void init(final Context context, final ContentResolver resolver, final Mailbox mailbox, 75c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu final Account account) { 76c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu mContext = context; 77c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu mContentResolver = resolver; 78c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu mMailbox = mailbox; 79c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu mAccount = account; 80ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 81ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 82ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 83ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Read, parse, and act on incoming commands from the Exchange server 84ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException if the connection is broken 8577186bb1a174432ef272584374942d8b9228e39cMarc Blank * @throws CommandStatusException 86ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 8777186bb1a174432ef272584374942d8b9228e39cMarc Blank public abstract void commandsParser() throws IOException, CommandStatusException; 88ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 89ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 90ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Read, parse, and act on server responses 91ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 92ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 9348af7392c82262d17700e3fbdccf3a582809d449Marc Blank public abstract void responsesParser() throws IOException; 9448af7392c82262d17700e3fbdccf3a582809d449Marc Blank 9548af7392c82262d17700e3fbdccf3a582809d449Marc Blank /** 9648af7392c82262d17700e3fbdccf3a582809d449Marc Blank * Commit any changes found during parsing 9748af7392c82262d17700e3fbdccf3a582809d449Marc Blank * @throws IOException 9848af7392c82262d17700e3fbdccf3a582809d449Marc Blank */ 9936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert public abstract void commit() throws IOException, RemoteException, 10036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert OperationApplicationException; 101ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1028efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank public boolean isLooping() { 1038efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank return mLooping; 1048efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank } 1058efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank 106ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 10777186bb1a174432ef272584374942d8b9228e39cMarc Blank * Skip through tags until we reach the specified end tag 10877186bb1a174432ef272584374942d8b9228e39cMarc Blank * @param endTag the tag we end with 10977186bb1a174432ef272584374942d8b9228e39cMarc Blank * @throws IOException 11077186bb1a174432ef272584374942d8b9228e39cMarc Blank */ 11177186bb1a174432ef272584374942d8b9228e39cMarc Blank public void skipParser(int endTag) throws IOException { 11277186bb1a174432ef272584374942d8b9228e39cMarc Blank while (nextTag(endTag) != END) { 11377186bb1a174432ef272584374942d8b9228e39cMarc Blank skipTag(); 11477186bb1a174432ef272584374942d8b9228e39cMarc Blank } 11577186bb1a174432ef272584374942d8b9228e39cMarc Blank } 11677186bb1a174432ef272584374942d8b9228e39cMarc Blank 11777186bb1a174432ef272584374942d8b9228e39cMarc Blank /** 118ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Loop through the top-level structure coming from the Exchange server 119ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Sync keys and the more available flag are handled here, whereas specific data parsing 120ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * is handled by abstract methods implemented for each data class (e.g. Email, Contacts, etc.) 12177186bb1a174432ef272584374942d8b9228e39cMarc Blank * @throws CommandStatusException 122ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 1237c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank @Override 12477186bb1a174432ef272584374942d8b9228e39cMarc Blank public boolean parse() throws IOException, CommandStatusException { 125ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int status; 126ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank boolean moreAvailable = false; 127da7f22d3a76dca40eaa95243288cea576852864cMarc Blank boolean newSyncKey = false; 1288efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank mLooping = false; 129ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // If we're not at the top of the xml tree, throw an exception 1307c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank if (nextTag(START_DOCUMENT) != Tags.SYNC_SYNC) { 1311431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank throw new EasParserException(); 132ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 133a05c26d8d2cce3faa152096cb8116fce375c6d81Marc Blank 134a05c26d8d2cce3faa152096cb8116fce375c6d81Marc Blank boolean mailboxUpdated = false; 135a05c26d8d2cce3faa152096cb8116fce375c6d81Marc Blank ContentValues cv = new ContentValues(); 136a05c26d8d2cce3faa152096cb8116fce375c6d81Marc Blank 137ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Loop here through the remaining xml 138ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (nextTag(START_DOCUMENT) != END_DOCUMENT) { 1397c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank if (tag == Tags.SYNC_COLLECTION || tag == Tags.SYNC_COLLECTIONS) { 140ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Ignore these tags, since we've only got one collection syncing in this loop 1417c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank } else if (tag == Tags.SYNC_STATUS) { 142ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Status = 1 is success; everything else is a failure 143ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank status = getValueInt(); 144ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (status != 1) { 14577186bb1a174432ef272584374942d8b9228e39cMarc Blank if (status == 3 || CommandStatus.isBadSyncKey(status)) { 146ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Must delete all of the data and start over with syncKey of "0" 147c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu mMailbox.mSyncKey = "0"; 1485cc7ea3e24f1c05f71a3223ac6fa8b69d211735cYu Ping Hu newSyncKey = true; 149722006c7b116240d9383c84d5e76ee69796dd571Alon Albert wipe(); 150ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Indicate there's more so that we'll start syncing again 151ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank moreAvailable = true; 152f7b0fd783fa0eec52473e0d0b832bc5adee1d69bMarc Blank } else if (status == 16 || status == 5) { 153f7b0fd783fa0eec52473e0d0b832bc5adee1d69bMarc Blank // Status 16 indicates a transient server error (indeterminate state) 154f7b0fd783fa0eec52473e0d0b832bc5adee1d69bMarc Blank // Status 5 indicates "server error"; this tends to loop for a while so 155f7b0fd783fa0eec52473e0d0b832bc5adee1d69bMarc Blank // throwing IOException will at least provide backoff behavior 1565ec3d0587c18603167f7a751c9b8974ba7a19758Marc Blank throw new IOException(); 157680e59f477fe2221ffa6d5bb6b3c8fe64c1f5a74Marc Blank } else if (status == 8 || status == 12) { 158680e59f477fe2221ffa6d5bb6b3c8fe64c1f5a74Marc Blank // Status 8 is Bad; it means the server doesn't recognize the serverId it 159680e59f477fe2221ffa6d5bb6b3c8fe64c1f5a74Marc Blank // sent us. 12 means that we're being asked to refresh the folder list. 160680e59f477fe2221ffa6d5bb6b3c8fe64c1f5a74Marc Blank // We'll do that with 8 also... 161c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu // TODO: reloadFolderList simply sets all mailboxes to hold. 162c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu //ExchangeService.reloadFolderList(mContext, mAccount.mId, true); 163680e59f477fe2221ffa6d5bb6b3c8fe64c1f5a74Marc Blank // We don't have any provision for telling the user "wait a minute while 164680e59f477fe2221ffa6d5bb6b3c8fe64c1f5a74Marc Blank // we sync folders"... 165680e59f477fe2221ffa6d5bb6b3c8fe64c1f5a74Marc Blank throw new IOException(); 166376260021db980af284f824f0c918b203e965407Marc Blank } else if (status == 7) { 1677d35f61afc910b8f21d3225762c251df10dc76eaYu Ping Hu // TODO: Fix this. The handling here used to be pretty bogus, and it's not 1687d35f61afc910b8f21d3225762c251df10dc76eaYu Ping Hu // obvious that simply forcing another resync makes sense here. 16961c48abf43082f3094f21fd4c645ad9cf4dfbd74Marc Blank moreAvailable = true; 17077186bb1a174432ef272584374942d8b9228e39cMarc Blank } else { 1719cdb8c8219349c654101ee5fc71e2986a6cebc3bAlon Albert LogUtils.e(LogUtils.TAG, "Sync: Unknown status: " + status); 17277186bb1a174432ef272584374942d8b9228e39cMarc Blank // Access, provisioning, transient, etc. 17377186bb1a174432ef272584374942d8b9228e39cMarc Blank throw new CommandStatusException(status); 174ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 175ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1767c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank } else if (tag == Tags.SYNC_COMMANDS) { 177ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank commandsParser(); 1787c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank } else if (tag == Tags.SYNC_RESPONSES) { 179ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank responsesParser(); 1807c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank } else if (tag == Tags.SYNC_MORE_AVAILABLE) { 181ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank moreAvailable = true; 1827c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank } else if (tag == Tags.SYNC_SYNC_KEY) { 183c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu if (mMailbox.mSyncKey.equals("0")) { 184ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank moreAvailable = true; 1859d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank } 186ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String newKey = getValue(); 1870a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank userLog("Parsed key for ", mMailbox.mDisplayName, ": ", newKey); 188a05c26d8d2cce3faa152096cb8116fce375c6d81Marc Blank if (!newKey.equals(mMailbox.mSyncKey)) { 189c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu mMailbox.mSyncKey = newKey; 190a05c26d8d2cce3faa152096cb8116fce375c6d81Marc Blank cv.put(MailboxColumns.SYNC_KEY, newKey); 191a05c26d8d2cce3faa152096cb8116fce375c6d81Marc Blank mailboxUpdated = true; 192da7f22d3a76dca40eaa95243288cea576852864cMarc Blank newSyncKey = true; 193da7f22d3a76dca40eaa95243288cea576852864cMarc Blank } 194ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else { 195ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 196ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 197ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 198ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 199da7f22d3a76dca40eaa95243288cea576852864cMarc Blank // If we don't have a new sync key, ignore moreAvailable (or we'll loop) 200da7f22d3a76dca40eaa95243288cea576852864cMarc Blank if (moreAvailable && !newSyncKey) { 20138ebc3d89a3c82589cf7bb413dc26c1a70b1a802Alon Albert LogUtils.e(TAG, "Looping detected"); 2028efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank mLooping = true; 203da7f22d3a76dca40eaa95243288cea576852864cMarc Blank } 204da7f22d3a76dca40eaa95243288cea576852864cMarc Blank 20548af7392c82262d17700e3fbdccf3a582809d449Marc Blank // Commit any changes 20636e181376b06c8905af2d22bf494a2423f50aa65Alon Albert try { 20736e181376b06c8905af2d22bf494a2423f50aa65Alon Albert commit(); 20836e181376b06c8905af2d22bf494a2423f50aa65Alon Albert if (mailboxUpdated) { 20936e181376b06c8905af2d22bf494a2423f50aa65Alon Albert mMailbox.update(mContext, cv); 21036e181376b06c8905af2d22bf494a2423f50aa65Alon Albert } 21136e181376b06c8905af2d22bf494a2423f50aa65Alon Albert } catch (RemoteException e) { 21236e181376b06c8905af2d22bf494a2423f50aa65Alon Albert LogUtils.e(TAG, "Failed to commit changes", e); 21336e181376b06c8905af2d22bf494a2423f50aa65Alon Albert } catch (OperationApplicationException e) { 21436e181376b06c8905af2d22bf494a2423f50aa65Alon Albert LogUtils.e(TAG, "Failed to commit changes", e); 21527cf341571fac3d8dbe866f503c34fc31e02bf85Marc Blank } 216ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Let the caller know that there's more to do 2170638c5ede5fc97b32e4b48d40c431c007860d0f6Marc Blank if (moreAvailable) { 2180638c5ede5fc97b32e4b48d40c431c007860d0f6Marc Blank userLog("MoreAvailable"); 2190638c5ede5fc97b32e4b48d40c431c007860d0f6Marc Blank } 220ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return moreAvailable; 221ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 222ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 22328f3556f92b20b2efb800c1d95c29b04a3bbbb4fMartin Hibdon abstract protected void wipe(); 224722006c7b116240d9383c84d5e76ee69796dd571Alon Albert 2250a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank void userLog(String ...strings) { 226c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu // TODO: Convert to other logging types? 227c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu //mService.userLog(strings); 2280a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank } 229ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 2300a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank void userLog(String string, int num, String string2) { 231c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu // TODO: Convert to other logging types? 232c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu //mService.userLog(string, num, string2); 2330a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank } 234ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank} 235