blob: 422c50a24895196cb3a0c60707dd6bb1ca074aa9 [file] [log] [blame]
/*
* Copyright (C) 2008-2009 Marc Blank
* Licensed to The Android Open Source Project.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.exchange.adapter;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.AccountColumns;
import com.android.emailcommon.provider.EmailContent.MailboxColumns;
import com.android.emailcommon.provider.Mailbox;
import com.android.emailcommon.provider.MailboxUtilities;
import com.android.emailcommon.service.SyncWindow;
import com.android.emailcommon.utility.AttachmentUtilities;
import com.android.exchange.CommandStatusException;
import com.android.exchange.CommandStatusException.CommandStatus;
import com.android.exchange.Eas;
import com.android.exchange.ExchangeService;
import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
/**
* Parse the result of a FolderSync command
*
* Handles the addition, deletion, and changes to folders in the user's Exchange account.
**/
public class FolderSyncParser extends AbstractSyncParser {
public static final String TAG = "FolderSyncParser";
// These are defined by the EAS protocol
public static final int USER_GENERIC_TYPE = 1;
public static final int INBOX_TYPE = 2;
public static final int DRAFTS_TYPE = 3;
public static final int DELETED_TYPE = 4;
public static final int SENT_TYPE = 5;
public static final int OUTBOX_TYPE = 6;
public static final int TASKS_TYPE = 7;
public static final int CALENDAR_TYPE = 8;
public static final int CONTACTS_TYPE = 9;
public static final int NOTES_TYPE = 10;
public static final int JOURNAL_TYPE = 11;
public static final int USER_MAILBOX_TYPE = 12;
// Chunk size for our mailbox commits
private final static int MAILBOX_COMMIT_SIZE = 20;
// Max mailboxes per account
private final static int MAX_MAILBOXES_PER_ACCOUNT = 1000000;
// EAS types that we are willing to consider valid folders for EAS sync
private static final List<Integer> VALID_EAS_FOLDER_TYPES = Arrays.asList(INBOX_TYPE,
DRAFTS_TYPE, DELETED_TYPE, SENT_TYPE, OUTBOX_TYPE, USER_MAILBOX_TYPE, CALENDAR_TYPE,
CONTACTS_TYPE, USER_GENERIC_TYPE);
public static final String ALL_BUT_ACCOUNT_MAILBOX = MailboxColumns.ACCOUNT_KEY + "=? and " +
MailboxColumns.TYPE + "!=" + Mailbox.TYPE_EAS_ACCOUNT_MAILBOX;
private static final String WHERE_SERVER_ID_AND_ACCOUNT = MailboxColumns.SERVER_ID + "=? and " +
MailboxColumns.ACCOUNT_KEY + "=?";
private static final String WHERE_DISPLAY_NAME_AND_ACCOUNT = MailboxColumns.DISPLAY_NAME +
"=? and " + MailboxColumns.ACCOUNT_KEY + "=?";
private static final String WHERE_PARENT_SERVER_ID_AND_ACCOUNT =
MailboxColumns.PARENT_SERVER_ID +"=? and " + MailboxColumns.ACCOUNT_KEY + "=?";
private static final String[] MAILBOX_ID_COLUMNS_PROJECTION =
new String[] {MailboxColumns.ID, MailboxColumns.SERVER_ID, MailboxColumns.PARENT_SERVER_ID};
private static final int MAILBOX_ID_COLUMNS_ID = 0;
private static final int MAILBOX_ID_COLUMNS_SERVER_ID = 1;
private static final int MAILBOX_ID_COLUMNS_PARENT_SERVER_ID = 2;
@VisibleForTesting
long mAccountId;
@VisibleForTesting
String mAccountIdAsString;
@VisibleForTesting
boolean mInUnitTest = false;
private String[] mBindArguments = new String[2];
private ArrayList<ContentProviderOperation> mOperations =
new ArrayList<ContentProviderOperation>();
private boolean mInitialSync;
private ArrayList<String> mParentFixupsNeeded = new ArrayList<String>();
private boolean mFixupUninitializedNeeded = false;
// If true, we only care about status (this is true when validating an account) and ignore
// other data
private final boolean mStatusOnly;
private static final ContentValues UNINITIALIZED_PARENT_KEY = new ContentValues();
{
UNINITIALIZED_PARENT_KEY.put(MailboxColumns.PARENT_KEY, Mailbox.PARENT_KEY_UNINITIALIZED);
}
public FolderSyncParser(final Context context, final ContentResolver resolver,
final InputStream in, final Account account, final boolean statusOnly)
throws IOException {
super(context, resolver, in, null, account);
mAccountId = mAccount.mId;
mAccountIdAsString = Long.toString(mAccountId);
mStatusOnly = statusOnly;
}
public FolderSyncParser(InputStream in, AbstractSyncAdapter adapter) throws IOException {
this(in, adapter, false);
}
public FolderSyncParser(InputStream in, AbstractSyncAdapter adapter, boolean statusOnly)
throws IOException {
super(in, adapter);
mAccountId = mAccount.mId;
mAccountIdAsString = Long.toString(mAccountId);
mStatusOnly = statusOnly;
}
@Override
public boolean parse() throws IOException, CommandStatusException {
int status;
boolean res = false;
boolean resetFolders = false;
// Since we're now (potentially) committing mailboxes in chunks, ensure that we start with
// only the account mailbox
String key = mAccount.mSyncKey;
mInitialSync = (key == null) || "0".equals(key);
if (mInitialSync) {
mContentResolver.delete(Mailbox.CONTENT_URI, ALL_BUT_ACCOUNT_MAILBOX,
new String[] {Long.toString(mAccountId)});
}
if (nextTag(START_DOCUMENT) != Tags.FOLDER_FOLDER_SYNC)
throw new EasParserException();
while (nextTag(START_DOCUMENT) != END_DOCUMENT) {
if (tag == Tags.FOLDER_STATUS) {
status = getValueInt();
// Do a sanity check on the account here; if we have any duplicated folders, we'll
// act as though we have a bad folder sync key (wipe/reload mailboxes)
// Note: The ContentValues isn't used, but no point creating a new one
int dupes = 0;
if (mAccountId > 0) {
dupes = mContentResolver.update(
ContentUris.withAppendedId(EmailContent.ACCOUNT_CHECK_URI, mAccountId),
UNINITIALIZED_PARENT_KEY, null, null);
}
if (dupes > 0) {
String e = "Duplicate mailboxes found for account " + mAccountId + ": " + dupes;
// For verbose logging, make sure this is in emaillog.txt
userLog(e);
// Worthy of logging, regardless
Log.w(Logging.LOG_TAG, e);
status = Eas.FOLDER_STATUS_INVALID_KEY;
}
if (status != Eas.FOLDER_STATUS_OK) {
// If the account hasn't been saved, this is a validation attempt, so we don't
// try reloading the folder list...
if (CommandStatus.isDeniedAccess(status) ||
CommandStatus.isNeedsProvisioning(status) ||
(mAccount.mId == Account.NOT_SAVED)) {
throw new CommandStatusException(status);
// Note that we need to catch both old-style (Eas.FOLDER_STATUS_INVALID_KEY)
// and EAS 14 style command status
} else if (status == Eas.FOLDER_STATUS_INVALID_KEY ||
CommandStatus.isBadSyncKey(status)) {
// Delete PIM data
ExchangeService.deleteAccountPIMData(mContext, mAccountId);
// Save away any mailbox sync information that is NOT default
saveMailboxSyncOptions();
// And only then, delete mailboxes
mContentResolver.delete(Mailbox.CONTENT_URI,
MailboxColumns.ACCOUNT_KEY + "=?",
new String[] {Long.toString(mAccountId)});
// Reconstruct _main
res = true;
resetFolders = true;
// Reset the sync key and save (this should trigger the AccountObserver
// in ExchangeService, which will recreate the account mailbox, which
// will then start syncing folders, etc.)
mAccount.mSyncKey = "0";
ContentValues cv = new ContentValues();
cv.put(AccountColumns.SYNC_KEY, mAccount.mSyncKey);
mContentResolver.update(ContentUris.withAppendedId(Account.CONTENT_URI,
mAccount.mId), cv, null, null);
} else {
// Other errors are at the server, so let's throw an error that will
// cause this sync to be retried at a later time
throw new EasParserException("Folder status error");
}
}
} else if (tag == Tags.FOLDER_SYNC_KEY) {
String newKey = getValue();
if (!resetFolders) {
mAccount.mSyncKey = newKey;
}
} else if (tag == Tags.FOLDER_CHANGES) {
if (mStatusOnly) return res;
changesParser(mOperations, mInitialSync);
} else
skipTag();
}
if (!mStatusOnly) {
commit();
}
return res;
}
private Cursor getServerIdCursor(String serverId) {
mBindArguments[0] = serverId;
mBindArguments[1] = mAccountIdAsString;
return mContentResolver.query(Mailbox.CONTENT_URI, MAILBOX_ID_COLUMNS_PROJECTION,
WHERE_SERVER_ID_AND_ACCOUNT, mBindArguments, null);
}
private void deleteParser(ArrayList<ContentProviderOperation> ops) throws IOException {
while (nextTag(Tags.FOLDER_DELETE) != END) {
switch (tag) {
case Tags.FOLDER_SERVER_ID:
final String serverId = getValue();
// Find the mailbox in this account with the given serverId
final Cursor c = getServerIdCursor(serverId);
try {
if (c.moveToFirst()) {
userLog("Deleting ", serverId);
final long mailboxId = c.getLong(MAILBOX_ID_COLUMNS_ID);
ops.add(ContentProviderOperation.newDelete(
ContentUris.withAppendedId(Mailbox.CONTENT_URI,
mailboxId)).build());
AttachmentUtilities.deleteAllMailboxAttachmentFiles(mContext,
mAccountId, mailboxId);
if (!mInitialSync) {
String parentId = c.getString(MAILBOX_ID_COLUMNS_PARENT_SERVER_ID);
if (!TextUtils.isEmpty(parentId)) {
mParentFixupsNeeded.add(parentId);
}
}
}
} finally {
c.close();
}
break;
default:
skipTag();
}
}
}
private static class SyncOptions {
private final int mInterval;
private final int mLookback;
private SyncOptions(int interval, int lookback) {
mInterval = interval;
mLookback = lookback;
}
}
private static final String MAILBOX_STATE_SELECTION =
MailboxColumns.ACCOUNT_KEY + "=? AND (" + MailboxColumns.SYNC_INTERVAL + "!=" +
Account.CHECK_INTERVAL_NEVER + " OR " + Mailbox.SYNC_LOOKBACK + "!=" +
SyncWindow.SYNC_WINDOW_UNKNOWN + ")";
private static final String[] MAILBOX_STATE_PROJECTION = new String[] {
MailboxColumns.SERVER_ID, MailboxColumns.SYNC_INTERVAL, MailboxColumns.SYNC_LOOKBACK};
private static final int MAILBOX_STATE_SERVER_ID = 0;
private static final int MAILBOX_STATE_INTERVAL = 1;
private static final int MAILBOX_STATE_LOOKBACK = 2;
@VisibleForTesting
final HashMap<String, SyncOptions> mSyncOptionsMap = new HashMap<String, SyncOptions>();
/**
* For every mailbox in this account that has a non-default interval or lookback, save those
* values.
*/
@VisibleForTesting
void saveMailboxSyncOptions() {
// Shouldn't be necessary, but...
mSyncOptionsMap.clear();
Cursor c = mContentResolver.query(Mailbox.CONTENT_URI, MAILBOX_STATE_PROJECTION,
MAILBOX_STATE_SELECTION, new String[] {mAccountIdAsString}, null);
if (c != null) {
try {
while (c.moveToNext()) {
mSyncOptionsMap.put(c.getString(MAILBOX_STATE_SERVER_ID),
new SyncOptions(c.getInt(MAILBOX_STATE_INTERVAL),
c.getInt(MAILBOX_STATE_LOOKBACK)));
}
} finally {
c.close();
}
}
}
/**
* For every set of saved mailbox sync options, try to find and restore those values
*/
@VisibleForTesting
void restoreMailboxSyncOptions() {
try {
ContentValues cv = new ContentValues();
mBindArguments[1] = mAccountIdAsString;
for (String serverId: mSyncOptionsMap.keySet()) {
SyncOptions options = mSyncOptionsMap.get(serverId);
cv.put(MailboxColumns.SYNC_INTERVAL, options.mInterval);
cv.put(MailboxColumns.SYNC_LOOKBACK, options.mLookback);
mBindArguments[0] = serverId;
// If we match account and server id, set the sync options
mContentResolver.update(Mailbox.CONTENT_URI, cv, WHERE_SERVER_ID_AND_ACCOUNT,
mBindArguments);
}
} finally {
mSyncOptionsMap.clear();
}
}
private Mailbox addParser() throws IOException {
String name = null;
String serverId = null;
String parentId = null;
int type = 0;
while (nextTag(Tags.FOLDER_ADD) != END) {
switch (tag) {
case Tags.FOLDER_DISPLAY_NAME: {
name = getValue();
break;
}
case Tags.FOLDER_TYPE: {
type = getValueInt();
break;
}
case Tags.FOLDER_PARENT_ID: {
parentId = getValue();
break;
}
case Tags.FOLDER_SERVER_ID: {
serverId = getValue();
break;
}
default:
skipTag();
}
}
if (VALID_EAS_FOLDER_TYPES.contains(type)) {
Mailbox mailbox = new Mailbox();
mailbox.mDisplayName = name;
mailbox.mServerId = serverId;
mailbox.mAccountKey = mAccountId;
mailbox.mType = Mailbox.TYPE_MAIL;
// Note that all mailboxes default to checking "never" (i.e. manual sync only)
// We set specific intervals for inbox, contacts, and (eventually) calendar
mailbox.mSyncInterval = Mailbox.CHECK_INTERVAL_NEVER;
switch (type) {
case INBOX_TYPE:
mailbox.mType = Mailbox.TYPE_INBOX;
mailbox.mSyncInterval = mAccount.mSyncInterval;
break;
case CONTACTS_TYPE:
mailbox.mType = Mailbox.TYPE_CONTACTS;
mailbox.mSyncInterval = mAccount.mSyncInterval;
break;
case OUTBOX_TYPE:
// TYPE_OUTBOX mailboxes are known by ExchangeService to sync whenever they
// aren't empty. The value of mSyncFrequency is ignored for this kind of
// mailbox.
mailbox.mType = Mailbox.TYPE_OUTBOX;
break;
case SENT_TYPE:
mailbox.mType = Mailbox.TYPE_SENT;
break;
case DRAFTS_TYPE:
mailbox.mType = Mailbox.TYPE_DRAFTS;
break;
case DELETED_TYPE:
mailbox.mType = Mailbox.TYPE_TRASH;
break;
case CALENDAR_TYPE:
mailbox.mType = Mailbox.TYPE_CALENDAR;
mailbox.mSyncInterval = mAccount.mSyncInterval;
break;
}
// Make boxes like Contacts and Calendar invisible in the folder list
mailbox.mFlagVisible = (mailbox.mType < Mailbox.TYPE_NOT_EMAIL);
if (!parentId.equals("0")) {
mailbox.mParentServerId = parentId;
if (!mInitialSync) {
mParentFixupsNeeded.add(parentId);
}
}
// At the least, we'll need to set flags
mFixupUninitializedNeeded = true;
return mailbox;
}
return null;
}
private void updateParser(ArrayList<ContentProviderOperation> ops) throws IOException {
String serverId = null;
String displayName = null;
String parentId = null;
while (nextTag(Tags.FOLDER_UPDATE) != END) {
switch (tag) {
case Tags.FOLDER_SERVER_ID:
serverId = getValue();
break;
case Tags.FOLDER_DISPLAY_NAME:
displayName = getValue();
break;
case Tags.FOLDER_PARENT_ID:
parentId = getValue();
break;
default:
skipTag();
break;
}
}
// We'll make a change if one of parentId or displayName are specified
// serverId is required, but let's be careful just the same
if (serverId != null && (displayName != null || parentId != null)) {
Cursor c = getServerIdCursor(serverId);
try {
// If we find the mailbox (using serverId), make the change
if (c.moveToFirst()) {
userLog("Updating ", serverId);
// Fix up old and new parents, as needed
if (!TextUtils.isEmpty(parentId)) {
mParentFixupsNeeded.add(parentId);
}
String oldParentId = c.getString(MAILBOX_ID_COLUMNS_PARENT_SERVER_ID);
if (!TextUtils.isEmpty(oldParentId)) {
mParentFixupsNeeded.add(oldParentId);
}
// Set display name if we've got one
ContentValues cv = new ContentValues();
if (displayName != null) {
cv.put(Mailbox.DISPLAY_NAME, displayName);
}
// Save away the server id and uninitialize the parent key
cv.put(Mailbox.PARENT_SERVER_ID, parentId);
// Clear the parent key; it will be fixed up after the commit
cv.put(Mailbox.PARENT_KEY, Mailbox.PARENT_KEY_UNINITIALIZED);
ops.add(ContentProviderOperation.newUpdate(
ContentUris.withAppendedId(Mailbox.CONTENT_URI,
c.getLong(MAILBOX_ID_COLUMNS_ID))).withValues(cv).build());
// Say we need to fixup uninitialized mailboxes
mFixupUninitializedNeeded = true;
}
} finally {
c.close();
}
}
}
private boolean commitMailboxes(ArrayList<ContentProviderOperation> ops) {
// Commit the mailboxes
userLog("Applying ", mOperations.size(), " mailbox operations.");
// Execute the batch; throw IOExceptions if this fails, hoping the issue isn't repeatable
// If it IS repeatable, there's no good result, since the folder list will be invalid
try {
mContentResolver.applyBatch(EmailContent.AUTHORITY, mOperations);
return true;
} catch (RemoteException e) {
userLog("RemoteException in commitMailboxes");
return false;
} catch (OperationApplicationException e) {
userLog("OperationApplicationException in commitMailboxes");
return false;
}
}
private void changesParser(final ArrayList<ContentProviderOperation> ops,
final boolean initialSync) throws IOException {
// Array of added mailboxes
final ArrayList<Mailbox> addMailboxes = new ArrayList<Mailbox>();
// Indicate start of (potential) mailbox changes
MailboxUtilities.startMailboxChanges(mContext, mAccount.mId);
while (nextTag(Tags.FOLDER_CHANGES) != END) {
if (tag == Tags.FOLDER_ADD) {
final Mailbox mailbox = addParser();
if (mailbox != null) {
addMailboxes.add(mailbox);
}
} else if (tag == Tags.FOLDER_DELETE) {
deleteParser(ops);
} else if (tag == Tags.FOLDER_UPDATE) {
updateParser(ops);
} else if (tag == Tags.FOLDER_COUNT) {
getValueInt();
} else
skipTag();
}
// Map folder serverId to mailbox (used to validate user mailboxes)
final HashMap<String, Mailbox> mailboxMap = new HashMap<String, Mailbox>();
for (final Mailbox mailbox : addMailboxes) {
mailboxMap.put(mailbox.mServerId, mailbox);
}
userLog("Total of " + addMailboxes.size() + " mailboxes parsed");
// Synchronize on the parser to prevent this being run concurrently
// (an extremely unlikely event, but nonetheless possible)
if (mInitialSync) {
synchronized (FolderSyncParser.this) {
// Assign unique sequential ids and set appropriate mailbox flags; it's safe to
// assume that there won't be more than one million mailboxes defined for an
// account. I use millions for ease in debugging (i.e. associating an int in
// the debugger with an account)
long mailboxId = (mAccount.mId * MAX_MAILBOXES_PER_ACCOUNT) + 1;
// Set basic flags
for (Mailbox mailbox : addMailboxes) {
int type = mailbox.mType;
if (type <= Mailbox.TYPE_NOT_EMAIL) {
mailbox.mFlags |= Mailbox.FLAG_HOLDS_MAIL + Mailbox.FLAG_SUPPORTS_SETTINGS;
}
// Outbox, Drafts, and Sent don't allow mail to be moved to them
if (type == Mailbox.TYPE_MAIL || type == Mailbox.TYPE_TRASH ||
type == Mailbox.TYPE_JUNK || type == Mailbox.TYPE_INBOX) {
mailbox.mFlags |= Mailbox.FLAG_ACCEPTS_MOVED_MAIL;
}
mailbox.mId = mailboxId++;
}
// Set parent mailbox key and hierarchical name; set parent flags on parents
for (Mailbox mailbox: addMailboxes) {
String parentServerId = mailbox.mParentServerId;
if (parentServerId == null || parentServerId.equals("0")) {
mailbox.mParentKey = Mailbox.NO_MAILBOX;
} else {
Mailbox parentMailbox = mailboxMap.get(parentServerId);
if (parentMailbox != null) {
mailbox.mParentKey = parentMailbox.mId;
parentMailbox.mFlags |=
Mailbox.FLAG_HAS_CHILDREN + Mailbox.FLAG_CHILDREN_VISIBLE;
String hierarchicalName = mailbox.mDisplayName;
while (parentMailbox != null) {
hierarchicalName = parentMailbox.mDisplayName + "/" +
hierarchicalName;
if (parentMailbox.mParentServerId != null &&
!parentMailbox.mParentServerId.equals("0")) {
parentMailbox = mailboxMap.get(parentMailbox.mParentServerId);
} else {
break;
}
}
mailbox.mHierarchicalName = hierarchicalName;
} else {
userLog("Parent not found with serverId = " + parentServerId);
}
}
}
}
// Save all the new mailboxes away in groups of 20
int batchCount = 0;
for (Mailbox mailbox: addMailboxes) {
if (mailbox.mId == Mailbox.NO_MAILBOX) {
userLog("Skipping mailbox: ", mailbox.mDisplayName);
continue;
}
if (++batchCount == MAILBOX_COMMIT_SIZE) {
if (!commitMailboxes(ops)) {
//mService.stop();
return;
}
ops.clear();
batchCount = 0;
}
userLog("Adding mailbox: ", mailbox.mDisplayName);
ContentValues initialValues = mailbox.toContentValues();
// We already have an id if this is the initial sync
if (mInitialSync) {
initialValues.put(MailboxColumns.ID, mailbox.mId);
}
ops.add(ContentProviderOperation.newInsert(
Mailbox.CONTENT_URI).withValues(initialValues).build());
}
// Save away the new sync key with the last batch
ContentValues cv = new ContentValues();
cv.put(AccountColumns.SYNC_KEY, mAccount.mSyncKey);
ops.add(ContentProviderOperation
.newUpdate(
ContentUris.withAppendedId(Account.CONTENT_URI,
mAccount.mId))
.withValues(cv).build());
if (!commitMailboxes(ops)) {
//mService.stop();
return;
}
}
// If this isn't the initial sync, we need to fix up the hierarchy
if (!mInitialSync) {
String accountSelector = Mailbox.ACCOUNT_KEY + "=" + mAccount.mId;
// For new boxes, setup the parent key and flags
if (mFixupUninitializedNeeded) {
MailboxUtilities.fixupUninitializedParentKeys(mContext,
accountSelector);
}
// For modified parents, reset the flags (and children's parent key)
for (String parentServerId: mParentFixupsNeeded) {
Cursor c = mContentResolver.query(Mailbox.CONTENT_URI,
Mailbox.CONTENT_PROJECTION, Mailbox.PARENT_SERVER_ID + "=?",
new String[] {parentServerId}, null);
try {
if (c.moveToFirst()) {
MailboxUtilities.setFlagsAndChildrensParentKey(mContext, c,
accountSelector);
}
} finally {
c.close();
}
}
MailboxUtilities.setupHierarchicalNames(mContext, mAccount.mId);
}
// Signal completion of mailbox changes
MailboxUtilities.endMailboxChanges(mContext, mAccount.mId);
}
/**
* Not needed for FolderSync parsing; everything is done within changesParser
*/
@Override
public void commandsParser() throws IOException {
}
/**
* Clean up after sync
*/
@Override
public void commit() throws IOException {
// Look for sync issues and its children and delete them
// I'm not aware of any other way to deal with this properly
mBindArguments[0] = "Sync Issues";
mBindArguments[1] = mAccountIdAsString;
Cursor c = mContentResolver.query(Mailbox.CONTENT_URI,
MAILBOX_ID_COLUMNS_PROJECTION, WHERE_DISPLAY_NAME_AND_ACCOUNT,
mBindArguments, null);
String parentServerId = null;
long id = 0;
try {
if (c.moveToFirst()) {
id = c.getLong(MAILBOX_ID_COLUMNS_ID);
parentServerId = c.getString(MAILBOX_ID_COLUMNS_SERVER_ID);
}
} finally {
c.close();
}
if (parentServerId != null) {
mContentResolver.delete(ContentUris.withAppendedId(Mailbox.CONTENT_URI, id),
null, null);
mBindArguments[0] = parentServerId;
mContentResolver.delete(Mailbox.CONTENT_URI, WHERE_PARENT_SERVER_ID_AND_ACCOUNT,
mBindArguments);
}
// If we have saved options, restore them now
if (mInitialSync) {
restoreMailboxSyncOptions();
}
}
@Override
public void responsesParser() throws IOException {
}
}