Remove Exchange1 sources

* Prevents confusion

Change-Id: If698dadca64ea1c68433e03d9a37bcdcdf6d2c29
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
deleted file mode 100644
index 15ac88f..0000000
--- a/AndroidManifest.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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.
--->
-
-<manifest
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.exchange"
-    android:versionCode="410000"
-    android:versionName="4.1"
-     >
-
-    <uses-permission
-        android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
-    <uses-permission
-        android:name="android.permission.ACCESS_NETWORK_STATE"/>
-    <uses-permission
-        android:name="android.permission.INTERNET"/>
-    <uses-permission
-        android:name="android.permission.VIBRATE"/>
-    <uses-permission
-        android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <uses-permission
-        android:name="android.permission.GET_ACCOUNTS" />
-    <uses-permission
-        android:name="android.permission.MANAGE_ACCOUNTS" />
-    <uses-permission
-        android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
-    <uses-permission
-        android:name="android.permission.READ_SYNC_SETTINGS" />
-    <uses-permission
-        android:name="android.permission.WRITE_SYNC_SETTINGS" />
-
-    <uses-permission
-        android:name="android.permission.READ_CONTACTS"/>
-    <uses-permission
-        android:name="android.permission.WRITE_CONTACTS"/>
-    <uses-permission
-        android:name="android.permission.READ_CALENDAR"/>
-    <uses-permission
-        android:name="android.permission.WRITE_CALENDAR"/>
-    <uses-permission
-        android:name="android.permission.USE_CREDENTIALS"/>
-
-    <!-- Only required if a store implements push mail and needs to keep network open -->
-    <uses-permission
-        android:name="android.permission.WAKE_LOCK"/>
-    <uses-permission
-        android:name="android.permission.READ_PHONE_STATE"/>
-
-    <uses-permission
-        android:name="com.android.email.permission.READ_ATTACHMENT"/>
-    <uses-permission
-        android:name="com.android.email.permission.ACCESS_PROVIDER"/>
-
-    <application
-        android:icon="@mipmap/icon"
-        android:label="@string/app_name"
-        android:name="Exchange"
-        android:theme="@android:style/Theme.Holo.Light"
-        >
-
-        <receiver
-            android:name="com.android.exchange.EmailSyncAlarmReceiver"/>
-        <receiver
-            android:name="com.android.exchange.MailboxAlarmReceiver"/>
-
-        <receiver
-            android:name=".service.ExchangeBroadcastReceiver"
-            android:enabled="true">
-            <intent-filter>
-                <action
-                    android:name="android.intent.action.BOOT_COMPLETED" />
-                <action
-                    android:name="android.intent.action.DEVICE_STORAGE_LOW" />
-                <action
-                    android:name="android.intent.action.DEVICE_STORAGE_OK" />
-                <action
-                    android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED" />
-            </intent-filter>
-        </receiver>
-
-        <service
-            android:name=".service.ExchangeBroadcastProcessorService" />
-
-        <!--Required stanza to register the EAS EmailSyncAdapterService with SyncManager -->
-        <service
-            android:name="com.android.exchange.EmailSyncAdapterService"
-            android:exported="true">
-            <intent-filter>
-                <action
-                    android:name="android.content.SyncAdapter" />
-            </intent-filter>
-            <meta-data android:name="android.content.SyncAdapter"
-                       android:resource="@xml/syncadapter_email" />
-        </service>
-
-        <!--Required stanza to register the EAS ContactsSyncAdapterService with SyncManager -->
-        <service
-            android:name="com.android.exchange.ContactsSyncAdapterService"
-            android:exported="true">
-            <intent-filter>
-                <action
-                    android:name="android.content.SyncAdapter" />
-            </intent-filter>
-            <meta-data android:name="android.content.SyncAdapter"
-                       android:resource="@xml/syncadapter_contacts" />
-        </service>
-
-        <!--Required stanza to register the EAS CalendarSyncAdapterService with SyncManager -->
-        <service
-            android:name="com.android.exchange.CalendarSyncAdapterService"
-            android:exported="true">
-            <intent-filter>
-                <action
-                    android:name="android.content.SyncAdapter" />
-            </intent-filter>
-            <meta-data android:name="android.content.SyncAdapter"
-                       android:resource="@xml/syncadapter_calendar" />
-        </service>
-
-        <!-- Add android:process=":remote" below to enable ExchangeService as a separate process -->
-        <service
-            android:name="com.android.exchange.ExchangeService"
-            android:enabled="true"
-            android:permission="com.android.email.permission.ACCESS_PROVIDER"
-            >
-            <intent-filter>
-                <action
-                    android:name="com.android.email.EXCHANGE_INTENT" />
-            </intent-filter>
-        </service>
-
-        <provider
-            android:name="com.android.exchange.provider.ExchangeDirectoryProvider"
-            android:authorities="com.android.exchange.directory.provider"
-            android:readPermission="android.permission.READ_CONTACTS"
-            android:multiprocess="false"
-            >
-          <meta-data
-              android:name="android.content.ContactDirectory"
-              android:value="true"/>
-        </provider>
-
-        <activity android:name=".EasCertificateRequestor">
-            <intent-filter>
-                <action android:name="com.android.emailcommon.REQUEST_CERT" />
-                <data android:scheme="eas" android:path="/certrequest" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </activity>
-        <activity android:name=".SettingsRedirector">
-            <intent-filter>
-                <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/proguard.flags b/proguard.flags
deleted file mode 100644
index 0b7da4b..0000000
--- a/proguard.flags
+++ /dev/null
@@ -1,23 +0,0 @@
-# keep names that are used by reflection.
-
-# Keep names that are used only by unit tests
--keep class ** {
-  *** *ForTest(...);
-}
-
--keepclasseswithmembers class com.android.exchange.adapter.Parser {
-  *** resetInput(java.io.InputStream);
-}
-
--keepclasseswithmembers class com.android.exchange.provider.GalResult {
-  *** addGalData(com.android.exchange.provider.GalResult$GalData);
-  *** addGalData(long, java.lang.String, java.lang.String);
-}
-
--keepclasseswithmembers class com.android.exchange.CalendarSyncEnabler {
-  public <init>(android.content.Context);
-}
-
--keepclasseswithmembers class com.android.exchange.provider.MailboxUtilities {
-  *** setFlagsAndChildrensParentKey(android.content.Context, java.lang.String, java.lang.String);
-}
diff --git a/src/com/android/exchange/AbstractSyncService.java b/src/com/android/exchange/AbstractSyncService.java
deleted file mode 100644
index 6ccc7c3..0000000
--- a/src/com/android/exchange/AbstractSyncService.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * 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;
-
-import com.android.emailcommon.provider.Account;
-import com.android.emailcommon.provider.HostAuth;
-import com.android.emailcommon.provider.Mailbox;
-import com.android.exchange.utility.FileLogger;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
-import android.os.Bundle;
-import android.util.Log;
-
-import java.util.concurrent.LinkedBlockingQueue;
-
-/**
- * Base class for all protocol services SyncManager (extends Service, implements
- * Runnable) instantiates subclasses to run a sync (either timed, or push, or
- * mail placed in outbox, etc.) EasSyncService is currently implemented; my goal
- * would be to move IMAP to this structure when it comes time to introduce push
- * functionality.
- */
-public abstract class AbstractSyncService implements Runnable {
-
-    public String TAG = "AbstractSyncService";
-
-    public static final int SECONDS = 1000;
-    public static final int MINUTES = 60*SECONDS;
-    public static final int HOURS = 60*MINUTES;
-    public static final int DAYS = 24*HOURS;
-
-    public static final int CONNECT_TIMEOUT = 30*SECONDS;
-    public static final int NETWORK_WAIT = 15*SECONDS;
-
-    public static final String EAS_PROTOCOL = "eas";
-    public static final int EXIT_DONE = 0;
-    public static final int EXIT_IO_ERROR = 1;
-    public static final int EXIT_LOGIN_FAILURE = 2;
-    public static final int EXIT_EXCEPTION = 3;
-    public static final int EXIT_SECURITY_FAILURE = 4;
-    public static final int EXIT_ACCESS_DENIED = 5;
-
-    public Mailbox mMailbox;
-    protected long mMailboxId;
-    protected int mExitStatus = EXIT_EXCEPTION;
-    protected String mMailboxName;
-    public Account mAccount;
-    public Context mContext;
-    public int mChangeCount = 0;
-    public volatile int mSyncReason = 0;
-    protected volatile boolean mStop = false;
-    protected volatile Thread mThread;
-    protected final Object mSynchronizer = new Object();
-
-    protected volatile long mRequestTime = 0;
-    protected LinkedBlockingQueue<Request> mRequestQueue = new LinkedBlockingQueue<Request>();
-
-    /**
-     * Sent by SyncManager to request that the service stop itself cleanly
-     */
-    public abstract void stop();
-
-    /**
-     * Sent by SyncManager to indicate that an alarm has fired for this service, and that its
-     * pending (network) operation has timed out. The service is NOT automatically stopped,
-     * although the behavior is service dependent.
-     *
-     * @return true if the operation was stopped normally; false if the thread needed to be
-     * interrupted.
-     */
-    public abstract boolean alarm();
-
-    /**
-     * Sent by SyncManager to request that the service reset itself cleanly; the meaning of this
-     * operation is service dependent.
-     */
-    public abstract void reset();
-
-    /**
-     * Called to validate an account; abstract to allow each protocol to do what
-     * is necessary. For consistency with the Email app's original
-     * functionality, success is indicated by a failure to throw an Exception
-     * (ugh). Parameters are self-explanatory
-     *
-     * @param hostAuth
-     * @return a Bundle containing a result code and, depending on the result, a PolicySet or an
-     * error message
-     */
-    public abstract Bundle validateAccount(HostAuth hostAuth, Context context);
-
-    public AbstractSyncService(Context _context, Mailbox _mailbox) {
-        mContext = _context;
-        mMailbox = _mailbox;
-        mMailboxId = _mailbox.mId;
-        mMailboxName = _mailbox.mServerId;
-        mAccount = Account.restoreAccountWithId(_context, _mailbox.mAccountKey);
-    }
-
-    // Will be required when subclasses are instantiated by name
-    public AbstractSyncService(String prefix) {
-    }
-
-    /**
-     * The UI can call this static method to perform account validation.  This method wraps each
-     * protocol's validateAccount method.   Arguments are self-explanatory, except where noted.
-     *
-     * @param klass the protocol class (EasSyncService.class for example)
-     * @param hostAuth
-     * @param context
-     * @return a Bundle containing a result code and, depending on the result, a PolicySet or an
-     * error message
-     */
-    public static Bundle validate(Class<? extends AbstractSyncService> klass,
-            HostAuth hostAuth, Context context) {
-        AbstractSyncService svc;
-        try {
-            svc = klass.newInstance();
-            return svc.validateAccount(hostAuth, context);
-        } catch (IllegalAccessException e) {
-        } catch (InstantiationException e) {
-        }
-        return null;
-    }
-
-    public static class ValidationResult {
-        static final int NO_FAILURE = 0;
-        static final int CONNECTION_FAILURE = 1;
-        static final int VALIDATION_FAILURE = 2;
-        static final int EXCEPTION = 3;
-
-        static final ValidationResult succeeded = new ValidationResult(true, NO_FAILURE, null);
-        boolean success;
-        int failure = NO_FAILURE;
-        String reason = null;
-        Exception exception = null;
-
-        ValidationResult(boolean _success, int _failure, String _reason) {
-            success = _success;
-            failure = _failure;
-            reason = _reason;
-        }
-
-        ValidationResult(boolean _success) {
-            success = _success;
-        }
-
-        ValidationResult(Exception e) {
-            success = false;
-            failure = EXCEPTION;
-            exception = e;
-        }
-
-        public boolean isSuccess() {
-            return success;
-        }
-
-        public String getReason() {
-            return reason;
-        }
-    }
-
-    public boolean isStopped() {
-        return mStop;
-    }
-
-    public Object getSynchronizer() {
-        return mSynchronizer;
-    }
-
-    /**
-     * Convenience methods to do user logging (i.e. connection activity).  Saves a bunch of
-     * repetitive code.
-     */
-    public void userLog(String string, int code, String string2) {
-        if (Eas.USER_LOG) {
-            userLog(string + code + string2);
-        }
-    }
-
-    public void userLog(String string, int code) {
-        if (Eas.USER_LOG) {
-            userLog(string + code);
-        }
-    }
-
-    public void userLog(String str, Exception e) {
-        if (Eas.USER_LOG) {
-            Log.e(TAG, str, e);
-        } else {
-            Log.e(TAG, str + e);
-        }
-        if (Eas.FILE_LOG) {
-            FileLogger.log(e);
-        }
-    }
-
-    /**
-     * Standard logging for EAS.
-     * If user logging is active, we concatenate any arguments and log them using Log.d
-     * We also check for file logging, and log appropriately
-     * @param strings strings to concatenate and log
-     */
-    public void userLog(String ...strings) {
-        if (Eas.USER_LOG) {
-            String logText;
-            if (strings.length == 1) {
-                logText = strings[0];
-            } else {
-                StringBuilder sb = new StringBuilder(64);
-                for (String string: strings) {
-                    sb.append(string);
-                }
-                logText = sb.toString();
-            }
-            Log.d(TAG, logText);
-            if (Eas.FILE_LOG) {
-                FileLogger.log(TAG, logText);
-            }
-        }
-    }
-
-    /**
-     * Error log is used for serious issues that should always be logged
-     * @param str the string to log
-     */
-    public void errorLog(String str) {
-        Log.e(TAG, str);
-        if (Eas.FILE_LOG) {
-            FileLogger.log(TAG, str);
-        }
-    }
-
-    /**
-     * Waits for up to 10 seconds for network connectivity; returns whether or not there is
-     * network connectivity.
-     *
-     * @return whether there is network connectivity
-     */
-    public boolean hasConnectivity() {
-        ConnectivityManager cm =
-                (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-        int tries = 0;
-        while (tries++ < 1) {
-            // Use the same test as in ExchangeService#waitForConnectivity
-            // TODO: Create common code for this test in emailcommon
-            NetworkInfo info = cm.getActiveNetworkInfo();
-            if (info != null) {
-                return true;
-            }
-            try {
-                Thread.sleep(10*SECONDS);
-            } catch (InterruptedException e) {
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Request handling (common functionality)
-     * Can be overridden if desired
-     */
-
-    public void addRequest(Request req) {
-        mRequestQueue.offer(req);
-    }
-
-    public void removeRequest(Request req) {
-        mRequestQueue.remove(req);
-    }
-
-    public boolean hasPendingRequests() {
-        return !mRequestQueue.isEmpty();
-    }
-
-    public void clearRequests() {
-        mRequestQueue.clear();
-    }
-}
diff --git a/src/com/android/exchange/CalendarSyncAdapterService.java b/src/com/android/exchange/CalendarSyncAdapterService.java
deleted file mode 100644
index 5ff03b3..0000000
--- a/src/com/android/exchange/CalendarSyncAdapterService.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-import android.accounts.Account;
-import android.accounts.OperationCanceledException;
-import android.app.Service;
-import android.content.AbstractThreadedSyncAdapter;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SyncResult;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.provider.CalendarContract.Events;
-import android.util.Log;
-
-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;
-
-public class CalendarSyncAdapterService extends Service {
-    private static final String TAG = "EAS CalendarSyncAdapterService";
-    private static SyncAdapterImpl sSyncAdapter = null;
-    private static final Object sSyncAdapterLock = new Object();
-
-    private static final String ACCOUNT_AND_TYPE_CALENDAR =
-        MailboxColumns.ACCOUNT_KEY + "=? AND " + MailboxColumns.TYPE + '=' + Mailbox.TYPE_CALENDAR;
-    private static final String DIRTY_IN_ACCOUNT =
-        Events.DIRTY + "=1 AND " + Events.ACCOUNT_NAME + "=?";
-    private static final String[] ID_SYNC_KEY_PROJECTION =
-        new String[] {MailboxColumns.ID, MailboxColumns.SYNC_KEY};
-    private static final int ID_SYNC_KEY_MAILBOX_ID = 0;
-    private static final int ID_SYNC_KEY_SYNC_KEY = 1;
-
-    public CalendarSyncAdapterService() {
-        super();
-    }
-
-    private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
-        private Context mContext;
-
-        public SyncAdapterImpl(Context context) {
-            super(context, true /* autoInitialize */);
-            mContext = context;
-        }
-
-        @Override
-        public void onPerformSync(Account account, Bundle extras,
-                String authority, ContentProviderClient provider, SyncResult syncResult) {
-            try {
-                CalendarSyncAdapterService.performSync(mContext, account, extras,
-                        authority, provider, syncResult);
-            } catch (OperationCanceledException e) {
-            }
-        }
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        synchronized (sSyncAdapterLock) {
-            if (sSyncAdapter == null) {
-                sSyncAdapter = new SyncAdapterImpl(getApplicationContext());
-            }
-        }
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return sSyncAdapter.getSyncAdapterBinder();
-    }
-
-    /**
-     * Partial integration with system SyncManager; we tell our EAS ExchangeService to start a
-     * calendar sync when we get the signal from SyncManager.
-     * The missing piece at this point is integration with the push/ping mechanism in EAS; this will
-     * be put in place at a later time.
-     */
-    private static void performSync(Context context, Account account, Bundle extras,
-            String authority, ContentProviderClient provider, SyncResult syncResult)
-            throws OperationCanceledException {
-        ContentResolver cr = context.getContentResolver();
-        boolean logging = Eas.USER_LOG;
-        if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD)) {
-            Cursor c = cr.query(Events.CONTENT_URI,
-                    new String[] {Events._ID}, DIRTY_IN_ACCOUNT, new String[] {account.name}, null);
-            try {
-                if (!c.moveToFirst()) {
-                    if (logging) {
-                        Log.d(TAG, "No changes for " + account.name);
-                    }
-                    return;
-                }
-            } finally {
-                c.close();
-            }
-        }
-
-        // Find the (EmailProvider) account associated with this email address
-        Cursor accountCursor =
-            cr.query(com.android.emailcommon.provider.Account.CONTENT_URI,
-                    EmailContent.ID_PROJECTION, AccountColumns.EMAIL_ADDRESS + "=?",
-                    new String[] {account.name}, null);
-        try {
-            if (accountCursor.moveToFirst()) {
-                long accountId = accountCursor.getLong(0);
-                // Now, find the calendar mailbox associated with the account
-                Cursor mailboxCursor = cr.query(Mailbox.CONTENT_URI, ID_SYNC_KEY_PROJECTION,
-                        ACCOUNT_AND_TYPE_CALENDAR, new String[] {Long.toString(accountId)}, null);
-                try {
-                     if (mailboxCursor.moveToFirst()) {
-                        if (logging) {
-                            Log.d(TAG, "Upload sync requested for " + account.name);
-                        }
-                        String syncKey = mailboxCursor.getString(ID_SYNC_KEY_SYNC_KEY);
-                        if ((syncKey == null) || (syncKey.equals("0"))) {
-                            if (logging) {
-                                Log.d(TAG, "Can't sync; mailbox in initial state");
-                            }
-                            return;
-                        }
-                        // Ask for a sync from our sync manager
-                        ExchangeService.serviceRequest(mailboxCursor.getLong(
-                                ID_SYNC_KEY_MAILBOX_ID), ExchangeService.SYNC_UPSYNC);
-                    }
-                } finally {
-                    mailboxCursor.close();
-                }
-            }
-        } finally {
-            accountCursor.close();
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/exchange/CalendarSyncEnabler.java b/src/com/android/exchange/CalendarSyncEnabler.java
deleted file mode 100644
index 5989614..0000000
--- a/src/com/android/exchange/CalendarSyncEnabler.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-import com.android.emailcommon.Logging;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.provider.CalendarContract;
-import android.util.Log;
-
-/**
- * Utility class to enable Exchange calendar sync for all existing Exchange accounts.
- *
- * <p>Exchange calendar was first supported on Froyo.  It wasn't supported on Eclair, which
- * was the first version that supported Exchange email.
- *
- * <p>This class is used only once when the devices is upgraded to Froyo (or later) from Eclair,
- * to enable calendar sync for all the existing Exchange accounts.
- */
-public class CalendarSyncEnabler {
-    private final Context mContext;
-
-    public CalendarSyncEnabler(Context context) {
-        this.mContext = context;
-    }
-
-    /**
-     * Enable calendar sync for all the existing exchange accounts, and post a notification if any.
-     */
-    public final void enableEasCalendarSync() {
-        String emailAddresses = enableEasCalendarSyncInternalForTest();
-        if (emailAddresses.length() > 0) {
-            // Exchange account(s) found.
-            showNotificationForTest(emailAddresses.toString());
-        }
-    }
-
-    /**
-     * Enable calendar sync for all the existing exchange accounts
-     *
-     * @return email addresses of the Exchange accounts joined with spaces as delimiters,
-     *     or the empty string if there's no Exchange accounts.
-     */
-    /* package for testing */ final String enableEasCalendarSyncInternalForTest() {
-        StringBuilder emailAddresses = new StringBuilder();
-
-        Account[] exchangeAccounts = AccountManager.get(mContext)
-                .getAccountsByType(Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE);
-        for (Account account : exchangeAccounts) {
-            final String emailAddress = account.name;
-            Log.i(Logging.LOG_TAG, "Enabling Exchange calendar sync for " + emailAddress);
-
-            ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 1);
-            ContentResolver.setSyncAutomatically(account, CalendarContract.AUTHORITY, true);
-
-            // Accumulate addresses for notification.
-            if (emailAddresses.length() > 0) {
-                emailAddresses.append(' ');
-            }
-            emailAddresses.append(emailAddress);
-        }
-        return emailAddresses.toString();
-    }
-
-    // *** Taken from NotificationController
-    public static final int NOTIFICATION_ID_EXCHANGE_CALENDAR_ADDED = 2;
-
-    /**
-     * Show the "Exchange calendar added" notification.
-     *
-     * @param emailAddresses space delimited list of email addresses of Exchange accounts.  It'll
-     *     be shown on the notification.
-     */
-    /* package for testing */ void showNotificationForTest(String emailAddresses) {
-        // Launch Calendar app when clicked.
-        PendingIntent launchCalendarPendingIntent = PendingIntent.getActivity(mContext, 0,
-                createLaunchCalendarIntent(), 0);
-
-        String tickerText = mContext.getString(R.string.notification_exchange_calendar_added);
-        Notification n = new Notification(R.drawable.stat_notify_calendar,
-                tickerText, System.currentTimeMillis());
-        n.setLatestEventInfo(mContext, tickerText, emailAddresses, launchCalendarPendingIntent);
-        n.flags = Notification.FLAG_AUTO_CANCEL;
-
-        NotificationManager nm =
-                (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        nm.notify(NOTIFICATION_ID_EXCHANGE_CALENDAR_ADDED, n);
-    }
-
-    /** @return {@link Intent} to launch the Calendar app. */
-    private Intent createLaunchCalendarIntent() {
-        return new Intent(Intent.ACTION_VIEW, Uri.parse("content://com.android.calendar/time"));
-    }
-}
diff --git a/src/com/android/exchange/CommandStatusException.java b/src/com/android/exchange/CommandStatusException.java
deleted file mode 100644
index 80b405a..0000000
--- a/src/com/android/exchange/CommandStatusException.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-/**
- * ActiveSync command error status definitions (EAS 14.0 and later); these are in addition to the
- * command-specific errors defined for earlier protocol versions
- */
-public class CommandStatusException extends EasException {
-    private static final long serialVersionUID = 1L;
-
-    // A status response to an EAS account. Responses < 16 correspond to command-specific errors as
-    // reported by EAS versions < 14.0; responses > 100 correspond to generic errors as reported
-    // by EAS versions 14.0 and greater
-    public final int mStatus;
-    // If the error refers to a specific data item, that item's id (as provided by the server) is
-    // stored here
-    public final String mItemId;
-
-    public static class CommandStatus {
-        private static final long serialVersionUID = 1L;
-
-        // Fatal user/provisioning issues (put on security hold)
-        public static final int USER_DISABLED_FOR_SYNC = 126;
-        public static final int USERS_DISABLED_FOR_SYNC = 127;
-        public static final int USER_ON_LEGACY_SERVER_CANT_SYNC = 128;
-        public static final int DEVICE_QUARANTINED = 129;
-        public static final int ACCESS_DENIED = 130;
-        public static final int USER_ACCOUNT_DISABLED = 131;
-        public static final int NOT_PROVISIONABLE_PARTIAL = 139;
-        public static final int NOT_PROVISIONABLE_LEGACY_DEVICE = 141;
-        public static final int TOO_MANY_PARTNERSHIPS = 177;
-
-        // Sync state problems (bad key, multiple client conflict, etc.)
-        public static final int SYNC_STATE_LOCKED = 133;
-        public static final int SYNC_STATE_CORRUPT = 134;
-        public static final int SYNC_STATE_EXISTS = 135;
-        public static final int SYNC_STATE_INVALID = 136;
-
-        // Soft provisioning errors, we need to send Provision command
-        public static final int NEEDS_PROVISIONING_WIPE = 140;
-        public static final int NEEDS_PROVISIONING = 142;
-        public static final int NEEDS_PROVISIONING_REFRESH = 143;
-        public static final int NEEDS_PROVISIONING_INVALID = 144;
-
-        // WTF issues (really shouldn't happen in our implementation)
-        public static final int WTF_INVALID_COMMAND = 137;
-        public static final int WTF_INVALID_PROTOCOL = 138;
-        public static final int WTF_DEVICE_CLAIMS_EXTERNAL_MANAGEMENT = 145;
-        public static final int WTF_UNKNOWN_ITEM_TYPE = 147;
-        public static final int WTF_REQUIRES_PROXY_WITHOUT_SSL = 148;
-
-        // For SmartReply/SmartForward
-        public static final int ITEM_NOT_FOUND = 150;
-
-        // Transient or possibly transient errors
-        public static final int SERVER_ERROR_RETRY = 111;
-        public static final int SYNC_STATE_NOT_FOUND = 132;
-
-        // String version of error status codes (for logging only)
-        private static final int STATUS_TEXT_START = 101;
-        private static final int STATUS_TEXT_END = 150;
-        private static final String[] STATUS_TEXT = {
-            "InvalidContent", "InvalidWBXML", "InvalidXML", "InvalidDateTime", "InvalidIDCombo",
-            "InvalidIDs", "InvalidMIME", "DeviceIdError", "DeviceTypeError", "ServerError",
-            "ServerErrorRetry", "ADAccessDenied", "Quota", "ServerOffline", "SendQuota",
-            "RecipientUnresolved", "ReplyNotAllowed", "SentPreviously", "NoRecipient", "SendFailed",
-            "ReplyFailed", "AttsTooLarge", "NoMailbox", "CantBeAnonymous", "UserNotFound",
-            "UserDisabled", "NewMailbox", "LegacyMailbox", "DeviceBlocked", "AccessDenied",
-            "AcctDisabled", "SyncStateNF", "SyncStateLocked", "SyncStateCorrupt", "SyncStateExists",
-            "SyncStateInvalid", "BadCommand", "BadVersion", "NotFullyProvisionable", "RemoteWipe",
-            "LegacyDevice", "NotProvisioned", "PolicyRefresh", "BadPolicyKey", "ExternallyManaged",
-            "NoRecurrence", "UnexpectedClass", "RemoteHasNoSSL", "InvalidRequest", "ItemNotFound"
-        };
-
-        public static boolean isNeedsProvisioning(int status) {
-            return (status == CommandStatus.NEEDS_PROVISIONING ||
-                    status == CommandStatus.NEEDS_PROVISIONING_REFRESH ||
-                    status == CommandStatus.NEEDS_PROVISIONING_INVALID ||
-                    status == CommandStatus.NEEDS_PROVISIONING_WIPE);
-        }
-
-        public static boolean isBadSyncKey(int status) {
-            return (status == CommandStatus.SYNC_STATE_CORRUPT ||
-                    status == CommandStatus.SYNC_STATE_INVALID);
-        }
-
-        public static boolean isDeniedAccess(int status) {
-            return (status == CommandStatus.USER_DISABLED_FOR_SYNC ||
-                    status == CommandStatus.USERS_DISABLED_FOR_SYNC ||
-                    status == CommandStatus.USER_ON_LEGACY_SERVER_CANT_SYNC ||
-                    status == CommandStatus.DEVICE_QUARANTINED ||
-                    status == CommandStatus.ACCESS_DENIED ||
-                    status == CommandStatus.USER_ACCOUNT_DISABLED ||
-                    status == CommandStatus.NOT_PROVISIONABLE_LEGACY_DEVICE ||
-                    status == CommandStatus.NOT_PROVISIONABLE_PARTIAL ||
-                    status == CommandStatus.TOO_MANY_PARTNERSHIPS);
-        }
-
-        public static boolean isTransientError(int status) {
-            return status == CommandStatus.SYNC_STATE_NOT_FOUND ||
-                status == CommandStatus.SERVER_ERROR_RETRY;
-        }
-
-        public static String toString(int status) {
-            StringBuilder sb = new StringBuilder();
-            sb.append(status);
-            sb.append(" (");
-            if (status < STATUS_TEXT_START || status > STATUS_TEXT_END) {
-                sb.append("unknown");
-            } else {
-                int offset = status - STATUS_TEXT_START;
-                if (offset <= STATUS_TEXT.length) {
-                    sb.append(STATUS_TEXT[offset]);
-                }
-            }
-            sb.append(")");
-            return sb.toString();
-        }
-    }
-
-    public CommandStatusException(int status) {
-        mStatus = status;
-        mItemId = null;
-    }
-
-    public CommandStatusException(int status, String itemId) {
-        mStatus = status;
-        mItemId = itemId;
-    }
-}
diff --git a/src/com/android/exchange/ContactsSyncAdapterService.java b/src/com/android/exchange/ContactsSyncAdapterService.java
deleted file mode 100644
index 97706e7..0000000
--- a/src/com/android/exchange/ContactsSyncAdapterService.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2009 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;
-
-import com.android.emailcommon.provider.EmailContent.AccountColumns;
-import com.android.emailcommon.provider.EmailContent.MailboxColumns;
-import com.android.emailcommon.provider.Mailbox;
-
-import android.accounts.Account;
-import android.accounts.OperationCanceledException;
-import android.app.Service;
-import android.content.AbstractThreadedSyncAdapter;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SyncResult;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.provider.ContactsContract.Groups;
-import android.provider.ContactsContract.RawContacts;
-import android.util.Log;
-
-public class ContactsSyncAdapterService extends Service {
-    private static final String TAG = "EAS ContactsSyncAdapterService";
-    private static SyncAdapterImpl sSyncAdapter = null;
-    private static final Object sSyncAdapterLock = new Object();
-
-    private static final String[] ID_PROJECTION = new String[] {"_id"};
-    private static final String ACCOUNT_AND_TYPE_CONTACTS =
-        MailboxColumns.ACCOUNT_KEY + "=? AND " + MailboxColumns.TYPE + '=' + Mailbox.TYPE_CONTACTS;
-
-    public ContactsSyncAdapterService() {
-        super();
-    }
-
-    private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
-        private Context mContext;
-
-        public SyncAdapterImpl(Context context) {
-            super(context, true /* autoInitialize */);
-            mContext = context;
-        }
-
-        @Override
-        public void onPerformSync(Account account, Bundle extras,
-                String authority, ContentProviderClient provider, SyncResult syncResult) {
-            try {
-                ContactsSyncAdapterService.performSync(mContext, account, extras,
-                        authority, provider, syncResult);
-            } catch (OperationCanceledException e) {
-            }
-        }
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        synchronized (sSyncAdapterLock) {
-            if (sSyncAdapter == null) {
-                sSyncAdapter = new SyncAdapterImpl(getApplicationContext());
-            }
-        }
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return sSyncAdapter.getSyncAdapterBinder();
-    }
-
-    private static boolean hasDirtyRows(ContentResolver resolver, Uri uri, String dirtyColumn) {
-        Cursor c = resolver.query(uri, ID_PROJECTION, dirtyColumn + "=1", null, null);
-        try {
-            return c.getCount() > 0;
-        } finally {
-            c.close();
-        }
-    }
-
-    /**
-     * Partial integration with system SyncManager; we tell our EAS ExchangeService to start a
-     * contacts sync when we get the signal from SyncManager.
-     * The missing piece at this point is integration with the push/ping mechanism in EAS; this will
-     * be put in place at a later time.
-     */
-    private static void performSync(Context context, Account account, Bundle extras,
-            String authority, ContentProviderClient provider, SyncResult syncResult)
-            throws OperationCanceledException {
-        ContentResolver cr = context.getContentResolver();
-
-        // If we've been asked to do an upload, make sure we've got work to do
-        if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD)) {
-            Uri uri = RawContacts.CONTENT_URI.buildUpon()
-                .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name)
-                .appendQueryParameter(RawContacts.ACCOUNT_TYPE, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE)
-                .build();
-            // See if we've got dirty contacts or dirty groups containing our contacts
-            boolean changed = hasDirtyRows(cr, uri, RawContacts.DIRTY);
-            if (!changed) {
-                uri = Groups.CONTENT_URI.buildUpon()
-                    .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name)
-                    .appendQueryParameter(RawContacts.ACCOUNT_TYPE,
-                            Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE)
-                    .build();
-                changed = hasDirtyRows(cr, uri, Groups.DIRTY);
-            }
-            if (!changed) {
-                Log.i(TAG, "Upload sync; no changes");
-                return;
-            }
-        }
-
-        // Find the (EmailProvider) account associated with this email address
-        Cursor accountCursor =
-            cr.query(com.android.emailcommon.provider.Account.CONTENT_URI, ID_PROJECTION,
-                AccountColumns.EMAIL_ADDRESS + "=?", new String[] {account.name}, null);
-        try {
-            if (accountCursor.moveToFirst()) {
-                long accountId = accountCursor.getLong(0);
-                // Now, find the contacts mailbox associated with the account
-                Cursor mailboxCursor = cr.query(Mailbox.CONTENT_URI, ID_PROJECTION,
-                        ACCOUNT_AND_TYPE_CONTACTS, new String[] {Long.toString(accountId)}, null);
-                try {
-                     if (mailboxCursor.moveToFirst()) {
-                        Log.i(TAG, "Contact sync requested for " + account.name);
-                        // Ask for a sync from our sync manager
-                        ExchangeService.serviceRequest(mailboxCursor.getLong(0),
-                                ExchangeService.SYNC_UPSYNC);
-                    }
-                } finally {
-                    mailboxCursor.close();
-                }
-            }
-        } finally {
-            accountCursor.close();
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/exchange/Eas.java b/src/com/android/exchange/Eas.java
deleted file mode 100644
index 9789f43..0000000
--- a/src/com/android/exchange/Eas.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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;
-
-import android.util.Log;
-
-import com.android.emailcommon.service.EmailServiceProxy;
-import com.android.emailcommon.service.SyncWindow;
-
-/**
- * Constants used throughout the EAS implementation are stored here.
- *
- */
-public class Eas {
-    // For debugging
-    public static boolean WAIT_DEBUG = false;   // DO NOT CHECK IN WITH THIS SET TO TRUE
-    public static boolean DEBUG = false;         // DO NOT CHECK IN WITH THIS SET TO TRUE
-
-    // The following two are for user logging (the second providing more detail)
-    public static boolean USER_LOG = false;     // DO NOT CHECK IN WITH THIS SET TO TRUE
-    public static boolean PARSER_LOG = false;   // DO NOT CHECK IN WITH THIS SET TO TRUE
-    public static boolean FILE_LOG = false;     // DO NOT CHECK IN WITH THIS SET TO TRUE
-
-    public static final String CLIENT_VERSION = "EAS-1.3";
-    public static final String ACCOUNT_MAILBOX_PREFIX = "__eas";
-
-    // Define our default protocol version as 2.5 (Exchange 2003)
-    public static final String SUPPORTED_PROTOCOL_EX2003 = "2.5";
-    public static final double SUPPORTED_PROTOCOL_EX2003_DOUBLE = 2.5;
-    public static final String SUPPORTED_PROTOCOL_EX2007 = "12.0";
-    public static final double SUPPORTED_PROTOCOL_EX2007_DOUBLE = 12.0;
-    public static final String SUPPORTED_PROTOCOL_EX2007_SP1 = "12.1";
-    public static final double SUPPORTED_PROTOCOL_EX2007_SP1_DOUBLE = 12.1;
-    public static final String SUPPORTED_PROTOCOL_EX2010 = "14.0";
-    public static final double SUPPORTED_PROTOCOL_EX2010_DOUBLE = 14.0;
-    public static final String SUPPORTED_PROTOCOL_EX2010_SP1 = "14.1";
-    public static final double SUPPORTED_PROTOCOL_EX2010_SP1_DOUBLE = 14.1;
-    public static final String DEFAULT_PROTOCOL_VERSION = SUPPORTED_PROTOCOL_EX2003;
-
-    public static final String EXCHANGE_ACCOUNT_MANAGER_TYPE = "com.android.exchange";
-
-    // From EAS spec
-    //                Mail Cal
-    // 0 No filter    Yes  Yes
-    // 1 1 day ago    Yes  No
-    // 2 3 days ago   Yes  No
-    // 3 1 week ago   Yes  No
-    // 4 2 weeks ago  Yes  Yes
-    // 5 1 month ago  Yes  Yes
-    // 6 3 months ago No   Yes
-    // 7 6 months ago No   Yes
-
-    public static final String FILTER_AUTO =  Integer.toString(SyncWindow.SYNC_WINDOW_AUTO);
-    // TODO Rationalize this with SYNC_WINDOW_ALL
-    public static final String FILTER_ALL = "0";
-    public static final String FILTER_1_DAY = Integer.toString(SyncWindow.SYNC_WINDOW_1_DAY);
-    public static final String FILTER_3_DAYS =  Integer.toString(SyncWindow.SYNC_WINDOW_3_DAYS);
-    public static final String FILTER_1_WEEK =  Integer.toString(SyncWindow.SYNC_WINDOW_1_WEEK);
-    public static final String FILTER_2_WEEKS =  Integer.toString(SyncWindow.SYNC_WINDOW_2_WEEKS);
-    public static final String FILTER_1_MONTH =  Integer.toString(SyncWindow.SYNC_WINDOW_1_MONTH);
-    public static final String FILTER_3_MONTHS = "6";
-    public static final String FILTER_6_MONTHS = "7";
-
-    public static final String BODY_PREFERENCE_TEXT = "1";
-    public static final String BODY_PREFERENCE_HTML = "2";
-
-    public static final String MIME_BODY_PREFERENCE_TEXT = "0";
-    public static final String MIME_BODY_PREFERENCE_MIME = "2";
-
-    // For EAS 12, we use HTML, so we want a larger size than in EAS 2.5
-    public static final String EAS12_TRUNCATION_SIZE = "200000";
-    // For EAS 2.5, truncation is a code; the largest is "7", which is 100k
-    public static final String EAS2_5_TRUNCATION_SIZE = "7";
-
-    public static final int FOLDER_STATUS_OK = 1;
-    public static final int FOLDER_STATUS_INVALID_KEY = 9;
-
-    public static final int EXCHANGE_ERROR_NOTIFICATION = 0x10;
-
-    public static void setUserDebug(int state) {
-        // DEBUG takes precedence and is never true in a user build
-        if (!DEBUG) {
-            USER_LOG = (state & EmailServiceProxy.DEBUG_BIT) != 0;
-            PARSER_LOG = (state & EmailServiceProxy.DEBUG_VERBOSE_BIT) != 0;
-            FILE_LOG = (state & EmailServiceProxy.DEBUG_FILE_BIT) != 0;
-            if (FILE_LOG || PARSER_LOG) {
-                USER_LOG = true;
-            }
-            Log.d("Eas Debug", "Logging: " + (USER_LOG ? "User " : "") +
-                    (PARSER_LOG ? "Parser " : "") + (FILE_LOG ? "File" : ""));
-        }
-    }
-
-    static public Double getProtocolVersionDouble(String version) {
-        if (SUPPORTED_PROTOCOL_EX2003.equals(version)) {
-            return SUPPORTED_PROTOCOL_EX2003_DOUBLE;
-        } else if (SUPPORTED_PROTOCOL_EX2007.equals(version)) {
-            return SUPPORTED_PROTOCOL_EX2007_DOUBLE;
-        } if (SUPPORTED_PROTOCOL_EX2007_SP1.equals(version)) {
-            return SUPPORTED_PROTOCOL_EX2007_SP1_DOUBLE;
-        } if (SUPPORTED_PROTOCOL_EX2010.equals(version)) {
-            return SUPPORTED_PROTOCOL_EX2010_DOUBLE;
-        } if (SUPPORTED_PROTOCOL_EX2010_SP1.equals(version)) {
-            return SUPPORTED_PROTOCOL_EX2010_SP1_DOUBLE;
-        }
-        throw new IllegalArgumentException("illegal protocol version");
-    }
-}
diff --git a/src/com/android/exchange/EasAuthenticationException.java b/src/com/android/exchange/EasAuthenticationException.java
deleted file mode 100644
index f5b14b9..0000000
--- a/src/com/android/exchange/EasAuthenticationException.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-import java.io.IOException;
-
-/**
- * Use this to be able to distinguish login (authentication) failures from other I/O
- * exceptions during a sync, as they are handled very differently.
- */
-public class EasAuthenticationException extends IOException {
-    private static final long serialVersionUID = 1L;
-
-    EasAuthenticationException() {
-        super();
-    }
-}
diff --git a/src/com/android/exchange/EasCertificateRequestor.java b/src/com/android/exchange/EasCertificateRequestor.java
deleted file mode 100644
index ebafe44..0000000
--- a/src/com/android/exchange/EasCertificateRequestor.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-import android.security.KeyChain;
-
-import com.android.emailcommon.utility.CertificateRequestor;
-
-/**
- * A subclass of the {@link CertificateRequestor} so that the Exchange process
- * can request access to a certificate.
- *
- * They {@link KeyChain} API works in such a way that the host
- * activity requesting the certificate must be running in the process with the UID of who will
- * actually use the certificate. Since the Exchange process needs to establish connections and use
- * certificates for EAS accounts, requests for certificates must be delegated by an Activity in this
- * process.
- */
-public class EasCertificateRequestor extends CertificateRequestor {
-    // Intentionally blank - no behavior overridden.
-}
diff --git a/src/com/android/exchange/EasException.java b/src/com/android/exchange/EasException.java
deleted file mode 100644
index e7613d7..0000000
--- a/src/com/android/exchange/EasException.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- *  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;
-
-public class EasException extends Exception {
-    private static final long serialVersionUID = 5894556952470989968L;
-}
diff --git a/src/com/android/exchange/EasOutboxService.java b/src/com/android/exchange/EasOutboxService.java
deleted file mode 100644
index 4466925..0000000
--- a/src/com/android/exchange/EasOutboxService.java
+++ /dev/null
@@ -1,543 +0,0 @@
-/*
- * 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;
-
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.TrafficStats;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.text.TextUtils;
-
-import com.android.emailcommon.TrafficFlags;
-import com.android.emailcommon.internet.Rfc822Output;
-import com.android.emailcommon.mail.MessagingException;
-import com.android.emailcommon.provider.Account;
-import com.android.emailcommon.provider.EmailContent.Body;
-import com.android.emailcommon.provider.EmailContent.BodyColumns;
-import com.android.emailcommon.provider.EmailContent.MailboxColumns;
-import com.android.emailcommon.provider.EmailContent.Message;
-import com.android.emailcommon.provider.EmailContent.MessageColumns;
-import com.android.emailcommon.provider.EmailContent.SyncColumns;
-import com.android.emailcommon.provider.Mailbox;
-import com.android.emailcommon.service.EmailServiceStatus;
-import com.android.emailcommon.utility.Utility;
-import com.android.exchange.CommandStatusException.CommandStatus;
-import com.android.exchange.adapter.Parser;
-import com.android.exchange.adapter.Parser.EmptyStreamException;
-import com.android.exchange.adapter.Serializer;
-import com.android.exchange.adapter.Tags;
-
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpStatus;
-import org.apache.http.entity.InputStreamEntity;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-public class EasOutboxService extends EasSyncService {
-
-    public static final int SEND_FAILED = 1;
-    public static final String MAILBOX_KEY_AND_NOT_SEND_FAILED =
-        MessageColumns.MAILBOX_KEY + "=? and (" + SyncColumns.SERVER_ID + " is null or " +
-        SyncColumns.SERVER_ID + "!=" + SEND_FAILED + ')';
-    public static final String[] BODY_SOURCE_PROJECTION =
-        new String[] {BodyColumns.SOURCE_MESSAGE_KEY};
-    public static final String WHERE_MESSAGE_KEY = Body.MESSAGE_KEY + "=?";
-
-    // This is a normal email (i.e. not one of the other types)
-    public static final int MODE_NORMAL = 0;
-    // This is a smart reply email
-    public static final int MODE_SMART_REPLY = 1;
-    // This is a smart forward email
-    public static final int MODE_SMART_FORWARD = 2;
-
-    // This needs to be long enough to send the longest reasonable message, without being so long
-    // as to effectively "hang" sending of mail.  The standard 30 second timeout isn't long enough
-    // for pictures and the like.  For now, we'll use 15 minutes, in the knowledge that any socket
-    // failure would probably generate an Exception before timing out anyway
-    public static final int SEND_MAIL_TIMEOUT = 15*MINUTES;
-
-    public EasOutboxService(Context _context, Mailbox _mailbox) {
-        super(_context, _mailbox);
-    }
-
-    /**
-     * Our own HttpEntity subclass that is able to insert opaque data (in this case the MIME
-     * representation of the message body as stored in a temporary file) into the serializer stream
-     */
-    private static class SendMailEntity extends InputStreamEntity {
-        private final Context mContext;
-        private final FileInputStream mFileStream;
-        private final long mFileLength;
-        private final int mSendTag;
-        private final Message mMessage;
-
-        private static final int[] MODE_TAGS =  new int[] {Tags.COMPOSE_SEND_MAIL,
-            Tags.COMPOSE_SMART_REPLY, Tags.COMPOSE_SMART_FORWARD};
-
-        public SendMailEntity(Context context, FileInputStream instream, long length, int tag,
-                Message message) {
-            super(instream, length);
-            mContext = context;
-            mFileStream = instream;
-            mFileLength = length;
-            mSendTag = tag;
-            mMessage = message;
-        }
-
-        /**
-         * We always return -1 because we don't know the actual length of the POST data (this
-         * causes HttpClient to send the data in "chunked" mode)
-         */
-        @Override
-        public long getContentLength() {
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            try {
-                // Calculate the overhead for the WBXML data
-                writeTo(baos, false);
-                // Return the actual size that will be sent
-                return baos.size() + mFileLength;
-            } catch (IOException e) {
-                // Just return -1 (unknown)
-            } finally {
-                try {
-                    baos.close();
-                } catch (IOException e) {
-                    // Ignore
-                }
-            }
-            return -1;
-        }
-
-        @Override
-        public void writeTo(OutputStream outstream) throws IOException {
-            writeTo(outstream, true);
-        }
-
-        /**
-         * Write the message to the output stream
-         * @param outstream the output stream to write
-         * @param withData whether or not the actual data is to be written; true when sending
-         *   mail; false when calculating size only
-         * @throws IOException
-         */
-        public void writeTo(OutputStream outstream, boolean withData) throws IOException {
-            // Not sure if this is possible; the check is taken from the superclass
-            if (outstream == null) {
-                throw new IllegalArgumentException("Output stream may not be null");
-            }
-
-            // We'll serialize directly into the output stream
-            Serializer s = new Serializer(outstream);
-            // Send the appropriate initial tag
-            s.start(mSendTag);
-            // The Message-Id for this message (note that we cannot use the messageId stored in
-            // the message, as EAS 14 limits the length to 40 chars and we use 70+)
-            s.data(Tags.COMPOSE_CLIENT_ID, "SendMail-" + System.nanoTime());
-            // We always save sent mail
-            s.tag(Tags.COMPOSE_SAVE_IN_SENT_ITEMS);
-
-            // If we're using smart reply/forward, we need info about the original message
-            if (mSendTag != Tags.COMPOSE_SEND_MAIL) {
-                OriginalMessageInfo info = getOriginalMessageInfo(mContext, mMessage.mId);
-                if (info != null) {
-                    s.start(Tags.COMPOSE_SOURCE);
-                    // For search results, use the long id (stored in mProtocolSearchInfo); else,
-                    // use folder id/item id combo
-                    if (mMessage.mProtocolSearchInfo != null) {
-                        s.data(Tags.COMPOSE_LONG_ID, mMessage.mProtocolSearchInfo);
-                    } else {
-                        s.data(Tags.COMPOSE_ITEM_ID, info.mItemId);
-                        s.data(Tags.COMPOSE_FOLDER_ID, info.mCollectionId);
-                    }
-                    s.end();  // Tags.COMPOSE_SOURCE
-                }
-            }
-
-            // Start the MIME tag; this is followed by "opaque" data (byte array)
-            s.start(Tags.COMPOSE_MIME);
-            // Send opaque data from the file stream
-            if (withData) {
-                s.opaque(mFileStream, (int)mFileLength);
-            } else {
-                s.opaqueWithoutData((int)mFileLength);
-            }
-            // And we're done
-            s.end().end().done();
-        }
-    }
-
-    private static class SendMailParser extends Parser {
-        private final int mStartTag;
-        private int mStatus;
-
-        public SendMailParser(InputStream in, int startTag) throws IOException {
-            super(in);
-            mStartTag = startTag;
-        }
-
-        public int getStatus() {
-            return mStatus;
-        }
-
-        /**
-         * The only useful info in the SendMail response is the status; we capture and save it
-         */
-        @Override
-        public boolean parse() throws IOException {
-            if (nextTag(START_DOCUMENT) != mStartTag) {
-                throw new IOException();
-            }
-            while (nextTag(START_DOCUMENT) != END_DOCUMENT) {
-                if (tag == Tags.COMPOSE_STATUS) {
-                    mStatus = getValueInt();
-                } else {
-                    skipTag();
-                }
-            }
-            return true;
-        }
-    }
-
-    /**
-     * For OriginalMessageInfo, we use the terminology of EAS for the serverId and mailboxId of the
-     * original message
-     */
-    protected static class OriginalMessageInfo {
-        final String mItemId;
-        final String mCollectionId;
-        final String mLongId;
-
-        OriginalMessageInfo(String itemId, String collectionId, String longId) {
-            mItemId = itemId;
-            mCollectionId = collectionId;
-            mLongId = longId;
-        }
-    }
-
-    private void sendCallback(long msgId, String subject, int status) {
-        try {
-            ExchangeService.callback().sendMessageStatus(msgId, subject, status, 0);
-        } catch (RemoteException e) {
-            // It's all good
-        }
-    }
-
-    /*package*/ String generateSmartSendCmd(boolean reply, OriginalMessageInfo info) {
-        StringBuilder sb = new StringBuilder();
-        sb.append(reply ? "SmartReply" : "SmartForward");
-        if (!TextUtils.isEmpty(info.mLongId)) {
-            sb.append("&LongId=");
-            sb.append(Uri.encode(info.mLongId, ":"));
-        } else {
-            sb.append("&ItemId=");
-            sb.append(Uri.encode(info.mItemId, ":"));
-            sb.append("&CollectionId=");
-            sb.append(Uri.encode(info.mCollectionId, ":"));
-        }
-        return sb.toString();
-    }
-
-    /**
-     * Get information about the original message that is referenced by the message to be sent; this
-     * information will exist for replies and forwards
-     *
-     * @param context the caller's context
-     * @param msgId the id of the message we're sending
-     * @return a data structure with the serverId and mailboxId of the original message, or null if
-     * either or both of those pieces of information can't be found
-     */
-    private static OriginalMessageInfo getOriginalMessageInfo(Context context, long msgId) {
-        // Note: itemId and collectionId are the terms used by EAS to refer to the serverId and
-        // mailboxId of a Message
-        String itemId = null;
-        String collectionId = null;
-        String longId = null;
-
-        // First, we need to get the id of the reply/forward message
-        String[] cols = Utility.getRowColumns(context, Body.CONTENT_URI,
-                BODY_SOURCE_PROJECTION, WHERE_MESSAGE_KEY,
-                new String[] {Long.toString(msgId)});
-        if (cols != null) {
-            long refId = Long.parseLong(cols[0]);
-            // Then, we need the serverId and mailboxKey of the message
-            cols = Utility.getRowColumns(context, Message.CONTENT_URI, refId,
-                    SyncColumns.SERVER_ID, MessageColumns.MAILBOX_KEY,
-                    MessageColumns.PROTOCOL_SEARCH_INFO);
-            if (cols != null) {
-                itemId = cols[0];
-                long boxId = Long.parseLong(cols[1]);
-                // Then, we need the serverId of the mailbox
-                cols = Utility.getRowColumns(context, Mailbox.CONTENT_URI, boxId,
-                        MailboxColumns.SERVER_ID);
-                if (cols != null) {
-                    collectionId = cols[0];
-                }
-            }
-        }
-        // We need either a longId or both itemId (serverId) and collectionId (mailboxId) to process
-        // a smart reply or a smart forward
-        if (longId != null || (itemId != null && collectionId != null)){
-            return new OriginalMessageInfo(itemId, collectionId, longId);
-        }
-        return null;
-    }
-
-    private void sendFailed(long msgId, int result) {
-        ContentValues cv = new ContentValues();
-        cv.put(SyncColumns.SERVER_ID, SEND_FAILED);
-        Message.update(mContext, Message.CONTENT_URI, msgId, cv);
-        sendCallback(msgId, null, result);
-    }
-
-    /**
-     * Send a single message via EAS
-     * Note that we mark messages SEND_FAILED when there is a permanent failure, rather than an
-     * IOException, which is handled by ExchangeService with retries, backoffs, etc.
-     *
-     * @param cacheDir the cache directory for this context
-     * @param msgId the _id of the message to send
-     * @throws IOException
-     */
-    int sendMessage(File cacheDir, long msgId) throws IOException, MessagingException {
-        // We always return SUCCESS unless the sending error is account-specific (security or
-        // authentication) rather than message-specific; returning anything else will terminate
-        // the Outbox sync! Message-specific errors are marked in the messages themselves.
-        int result = EmailServiceStatus.SUCCESS;
-        // Say we're starting to send this message
-        sendCallback(msgId, null, EmailServiceStatus.IN_PROGRESS);
-        // Create a temporary file (this will hold the outgoing message in RFC822 (MIME) format)
-        File tmpFile = File.createTempFile("eas_", "tmp", cacheDir);
-        try {
-            // Get the message and fail quickly if not found
-            Message msg = Message.restoreMessageWithId(mContext, msgId);
-            if (msg == null) return EmailServiceStatus.MESSAGE_NOT_FOUND;
-
-            // See what kind of outgoing messge this is
-            int flags = msg.mFlags;
-            boolean reply = (flags & Message.FLAG_TYPE_REPLY) != 0;
-            boolean forward = (flags & Message.FLAG_TYPE_FORWARD) != 0;
-            boolean includeQuotedText = (flags & Message.FLAG_NOT_INCLUDE_QUOTED_TEXT) == 0;
-
-            // The reference message and mailbox are called item and collection in EAS
-            OriginalMessageInfo referenceInfo = null;
-            // Respect the sense of the include quoted text flag
-            if (includeQuotedText && (reply || forward)) {
-                referenceInfo = getOriginalMessageInfo(mContext, msgId);
-            }
-            // Generally, we use SmartReply/SmartForward if we've got a good reference
-            boolean smartSend = referenceInfo != null;
-            // But we won't use SmartForward if the account isn't set up for it (currently, we only
-            // use SmartForward for EAS 12.0 or later to avoid creating eml files that are
-            // potentially difficult for the recipient to handle)
-            if (forward && ((mAccount.mFlags & Account.FLAGS_SUPPORTS_SMART_FORWARD) == 0)) {
-                smartSend = false;
-            }
-
-            // Write the message to the temporary file
-            FileOutputStream fileOutputStream = new FileOutputStream(tmpFile);
-            Rfc822Output.writeTo(mContext, msgId, fileOutputStream, smartSend, true);
-            fileOutputStream.close();
-
-            // Sending via EAS14 is a whole 'nother kettle of fish
-            boolean isEas14 = (Double.parseDouble(mAccount.mProtocolVersion) >=
-                Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE);
-
-            while (true) {
-                // Get an input stream to our temporary file and create an entity with it
-                FileInputStream fileStream = new FileInputStream(tmpFile);
-                long fileLength = tmpFile.length();
-
-                // The type of entity depends on whether we're using EAS 14
-                HttpEntity inputEntity;
-                // For EAS 14, we need to save the wbxml tag we're using
-                int modeTag = 0;
-                if (isEas14) {
-                    int mode =
-                        !smartSend ? MODE_NORMAL : reply ? MODE_SMART_REPLY : MODE_SMART_FORWARD;
-                    modeTag = SendMailEntity.MODE_TAGS[mode];
-                    inputEntity =
-                        new SendMailEntity(mContext, fileStream, fileLength, modeTag, msg);
-                } else {
-                    inputEntity = new InputStreamEntity(fileStream, fileLength);
-                }
-                // Create the appropriate command and POST it to the server
-                String cmd = "SendMail";
-                if (smartSend) {
-                    // In EAS 14, we don't send itemId and collectionId in the command
-                    if (isEas14) {
-                        cmd = reply ? "SmartReply" : "SmartForward";
-                    } else {
-                        cmd = generateSmartSendCmd(reply, referenceInfo);
-                    }
-                }
-
-                // If we're not EAS 14, add our save-in-sent setting here
-                if (!isEas14) {
-                    cmd += "&SaveInSent=T";
-                }
-                userLog("Send cmd: " + cmd);
-
-                // Finally, post SendMail to the server
-                EasResponse resp = sendHttpClientPost(cmd, inputEntity, SEND_MAIL_TIMEOUT);
-                try {
-                    fileStream.close();
-                    int code = resp.getStatus();
-                    if (code == HttpStatus.SC_OK) {
-                        // HTTP OK before EAS 14 is a thumbs up; in EAS 14, we've got to parse
-                        // the reply
-                        if (isEas14) {
-                            try {
-                                // Try to parse the result
-                                SendMailParser p =
-                                    new SendMailParser(resp.getInputStream(), modeTag);
-                                // If we get here, the SendMail failed; go figure
-                                p.parse();
-                                // The parser holds the status
-                                int status = p.getStatus();
-                                userLog("SendMail error, status: " + status);
-                                if (CommandStatus.isNeedsProvisioning(status)) {
-                                    result = EmailServiceStatus.SECURITY_FAILURE;
-                                } else if (status == CommandStatus.ITEM_NOT_FOUND && smartSend) {
-                                    // This is the retry case for EAS 14; we'll send without "smart"
-                                    // commands next time
-                                    resp.close();
-                                    smartSend = false;
-                                    continue;
-                                }
-                                sendFailed(msgId, result);
-                                return result;
-                            } catch (EmptyStreamException e) {
-                                // This is actually fine; an empty stream means SendMail succeeded
-                            }
-                        }
-
-                        // If we're here, the SendMail command succeeded
-                        userLog("Deleting message...");
-                        // Delete the message from the Outbox and send callback
-                        mContentResolver.delete(
-                                ContentUris.withAppendedId(Message.CONTENT_URI, msgId), null, null);
-                        sendCallback(-1, msg.mSubject, EmailServiceStatus.SUCCESS);
-                        break;
-                    } else if (code == EasSyncService.INTERNAL_SERVER_ERROR_CODE && smartSend) {
-                        // This is the retry case for EAS 12.1 and below; we'll send without "smart"
-                        // commands next time
-                        resp.close();
-                        smartSend = false;
-                    } else {
-                        userLog("Message sending failed, code: " + code);
-                        if (EasResponse.isAuthError(code)) {
-                            result = EmailServiceStatus.LOGIN_FAILED;
-                        } else if (EasResponse.isProvisionError(code)) {
-                            result = EmailServiceStatus.SECURITY_FAILURE;
-                        }
-                        sendFailed(msgId, result);
-                        break;
-                    }
-                } finally {
-                    resp.close();
-                }
-            }
-        } catch (IOException e) {
-            // We catch this just to send the callback
-            sendCallback(msgId, null, EmailServiceStatus.CONNECTION_ERROR);
-            throw e;
-        } finally {
-            // Clean up the temporary file
-            if (tmpFile.exists()) {
-                tmpFile.delete();
-            }
-        }
-        return result;
-    }
-
-    @Override
-    public void run() {
-        setupService();
-        // Use SMTP flags for sending mail
-        TrafficStats.setThreadStatsTag(TrafficFlags.getSmtpFlags(mContext, mAccount));
-        File cacheDir = mContext.getCacheDir();
-        try {
-            mDeviceId = ExchangeService.getDeviceId(mContext);
-            // Get a cursor to Outbox messages
-            Cursor c = mContext.getContentResolver().query(Message.CONTENT_URI,
-                    Message.ID_COLUMN_PROJECTION, MAILBOX_KEY_AND_NOT_SEND_FAILED,
-                    new String[] {Long.toString(mMailbox.mId)}, null);
-            try {
-                // Loop through the messages, sending each one
-                while (c.moveToNext()) {
-                    long msgId = c.getLong(Message.ID_COLUMNS_ID_COLUMN);
-                    if (msgId != 0) {
-                        if (Utility.hasUnloadedAttachments(mContext, msgId)) {
-                            // We'll just have to wait on this...
-                            continue;
-                        }
-                        int result = sendMessage(cacheDir, msgId);
-                        // If there's an error, it should stop the service; we will distinguish
-                        // at least between login failures and everything else
-                        if (result == EmailServiceStatus.LOGIN_FAILED) {
-                            mExitStatus = EXIT_LOGIN_FAILURE;
-                            return;
-                        } else if (result == EmailServiceStatus.SECURITY_FAILURE) {
-                            mExitStatus = EXIT_SECURITY_FAILURE;
-                            return;
-                        } else if (result == EmailServiceStatus.REMOTE_EXCEPTION) {
-                            mExitStatus = EXIT_EXCEPTION;
-                            return;
-                        }
-                    }
-                }
-            } finally {
-                c.close();
-            }
-            mExitStatus = EXIT_DONE;
-        } catch (IOException e) {
-            mExitStatus = EXIT_IO_ERROR;
-        } catch (Exception e) {
-            userLog("Exception caught in EasOutboxService", e);
-            mExitStatus = EXIT_EXCEPTION;
-        } finally {
-            userLog(mMailbox.mDisplayName, ": sync finished");
-            userLog("Outbox exited with status ", mExitStatus);
-            ExchangeService.done(this);
-        }
-    }
-
-    /**
-     * Convenience method for adding a Message to an account's outbox
-     * @param context the context of the caller
-     * @param accountId the accountId for the sending account
-     * @param msg the message to send
-     */
-    public static void sendMessage(Context context, long accountId, Message msg) {
-        Mailbox mailbox = Mailbox.restoreMailboxOfType(context, accountId, Mailbox.TYPE_OUTBOX);
-        if (mailbox != null) {
-            msg.mMailboxKey = mailbox.mId;
-            msg.mAccountKey = accountId;
-            msg.save(context);
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/exchange/EasResponse.java b/src/com/android/exchange/EasResponse.java
deleted file mode 100644
index 174989e..0000000
--- a/src/com/android/exchange/EasResponse.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * 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;
-
-import com.android.emailcommon.utility.EmailClientConnectionManager;
-
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpUriRequest;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.zip.GZIPInputStream;
-
-/**
- * Encapsulate a response to an HTTP POST
- */
-public class EasResponse {
-    // MSFT's custom HTTP result code indicating the need to provision
-    static private final int HTTP_NEED_PROVISIONING = 449;
-
-    final HttpResponse mResponse;
-    private final HttpEntity mEntity;
-    private final int mLength;
-    private InputStream mInputStream;
-    private boolean mClosed;
-
-    /**
-     * Whether or not a certificate was requested by the server and missing.
-     * If this is set, it is essentially a 403 whereby the failure was due
-     */
-    private boolean mClientCertRequested = false;
-
-    private EasResponse(HttpResponse response) {
-        mResponse = response;
-        mEntity = (response == null) ? null : mResponse.getEntity();
-        if (mEntity !=  null) {
-            mLength = (int) mEntity.getContentLength();
-        } else {
-            mLength = 0;
-        }
-    }
-
-    public static EasResponse fromHttpRequest(
-            EmailClientConnectionManager connManager, HttpClient client, HttpUriRequest request)
-            throws IOException {
-
-        long reqTime = System.currentTimeMillis();
-        HttpResponse response = client.execute(request);
-        EasResponse result = new EasResponse(response);
-        if (isAuthError(response.getStatusLine().getStatusCode())
-                && connManager.hasDetectedUnsatisfiedCertReq(reqTime)) {
-            result.mClientCertRequested = true;
-            result.mClosed = true;
-        }
-
-        return result;
-    }
-
-    /**
-     * Determine whether an HTTP code represents an authentication error
-     * @param code the HTTP code returned by the server
-     * @return whether or not the code represents an authentication error
-     */
-    public static boolean isAuthError(int code) {
-        return (code == HttpStatus.SC_UNAUTHORIZED) || (code == HttpStatus.SC_FORBIDDEN);
-    }
-
-    /**
-     * Determine whether an HTTP code represents a provisioning error
-     * @param code the HTTP code returned by the server
-     * @return whether or not the code represents an provisioning error
-     */
-    public static boolean isProvisionError(int code) {
-        return (code == HTTP_NEED_PROVISIONING) || (code == HttpStatus.SC_FORBIDDEN);
-    }
-
-    /**
-     * Return an appropriate input stream for the response, either a GZIPInputStream, for
-     * compressed data, or a generic InputStream otherwise
-     * @return the input stream for the response
-     */
-    public InputStream getInputStream() {
-        if (mInputStream != null || mClosed) {
-            throw new IllegalStateException("Can't reuse stream or get closed stream");
-        } else if (mEntity == null) {
-            throw new IllegalStateException("Can't get input stream without entity");
-        }
-        InputStream is = null;
-        try {
-            // Get the default input stream for the entity
-            is = mEntity.getContent();
-            Header ceHeader = mResponse.getFirstHeader("Content-Encoding");
-            if (ceHeader != null) {
-                String encoding = ceHeader.getValue();
-                // If we're gzip encoded, wrap appropriately
-                if (encoding.toLowerCase().equals("gzip")) {
-                    is = new GZIPInputStream(is);
-                }
-            }
-        } catch (IllegalStateException e1) {
-        } catch (IOException e1) {
-        }
-        mInputStream = is;
-        return is;
-    }
-
-    public boolean isEmpty() {
-        return mLength == 0;
-    }
-
-    public int getStatus() {
-        return mClientCertRequested
-                ? HttpStatus.SC_UNAUTHORIZED
-                : mResponse.getStatusLine().getStatusCode();
-    }
-
-    public boolean isMissingCertificate() {
-        return mClientCertRequested;
-    }
-
-    public Header getHeader(String name) {
-        return (mResponse == null) ? null : mResponse.getFirstHeader(name);
-    }
-
-    public int getLength() {
-        return mLength;
-    }
-
-    public void close() {
-        if (!mClosed) {
-            if (mEntity != null) {
-                try {
-                    mEntity.consumeContent();
-                } catch (IOException e) {
-                    // No harm, no foul
-                }
-            }
-            if (mInputStream instanceof GZIPInputStream) {
-                try {
-                    mInputStream.close();
-                } catch (IOException e) {
-                    // We tried
-                }
-            }
-            mClosed = true;
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/exchange/EasSyncService.java b/src/com/android/exchange/EasSyncService.java
deleted file mode 100644
index 148a348..0000000
--- a/src/com/android/exchange/EasSyncService.java
+++ /dev/null
@@ -1,2690 +0,0 @@
-/*
- * 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;
-
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Entity;
-import android.database.Cursor;
-import android.net.TrafficStats;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.provider.CalendarContract.Attendees;
-import android.provider.CalendarContract.Events;
-import android.text.TextUtils;
-import android.util.Base64;
-import android.util.Log;
-import android.util.Xml;
-
-import com.android.emailcommon.TrafficFlags;
-import com.android.emailcommon.mail.Address;
-import com.android.emailcommon.mail.MeetingInfo;
-import com.android.emailcommon.mail.MessagingException;
-import com.android.emailcommon.mail.PackedString;
-import com.android.emailcommon.provider.Account;
-import com.android.emailcommon.provider.EmailContent.AccountColumns;
-import com.android.emailcommon.provider.EmailContent.HostAuthColumns;
-import com.android.emailcommon.provider.EmailContent.MailboxColumns;
-import com.android.emailcommon.provider.EmailContent.Message;
-import com.android.emailcommon.provider.EmailContent.MessageColumns;
-import com.android.emailcommon.provider.EmailContent.SyncColumns;
-import com.android.emailcommon.provider.HostAuth;
-import com.android.emailcommon.provider.Mailbox;
-import com.android.emailcommon.provider.Policy;
-import com.android.emailcommon.provider.ProviderUnavailableException;
-import com.android.emailcommon.service.EmailServiceConstants;
-import com.android.emailcommon.service.EmailServiceProxy;
-import com.android.emailcommon.service.EmailServiceStatus;
-import com.android.emailcommon.utility.EmailClientConnectionManager;
-import com.android.emailcommon.utility.Utility;
-import com.android.exchange.CommandStatusException.CommandStatus;
-import com.android.exchange.adapter.AbstractSyncAdapter;
-import com.android.exchange.adapter.AccountSyncAdapter;
-import com.android.exchange.adapter.AttachmentLoader;
-import com.android.exchange.adapter.CalendarSyncAdapter;
-import com.android.exchange.adapter.ContactsSyncAdapter;
-import com.android.exchange.adapter.EmailSyncAdapter;
-import com.android.exchange.adapter.FolderSyncParser;
-import com.android.exchange.adapter.GalParser;
-import com.android.exchange.adapter.MeetingResponseParser;
-import com.android.exchange.adapter.MoveItemsParser;
-import com.android.exchange.adapter.Parser.EasParserException;
-import com.android.exchange.adapter.Parser.EmptyStreamException;
-import com.android.exchange.adapter.PingParser;
-import com.android.exchange.adapter.ProvisionParser;
-import com.android.exchange.adapter.Serializer;
-import com.android.exchange.adapter.SettingsParser;
-import com.android.exchange.adapter.Tags;
-import com.android.exchange.provider.GalResult;
-import com.android.exchange.provider.MailboxUtilities;
-import com.android.exchange.utility.CalendarUtilities;
-import com.google.common.annotations.VisibleForTesting;
-
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpOptions;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpRequestBase;
-import org.apache.http.entity.ByteArrayEntity;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.params.BasicHttpParams;
-import org.apache.http.params.HttpConnectionParams;
-import org.apache.http.params.HttpParams;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlPullParserFactory;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.Thread.State;
-import java.net.URI;
-import java.security.cert.CertificateException;
-import java.util.ArrayList;
-import java.util.HashMap;
-
-public class EasSyncService extends AbstractSyncService {
-    // DO NOT CHECK IN SET TO TRUE
-    public static final boolean DEBUG_GAL_SERVICE = false;
-
-    private static final String WHERE_ACCOUNT_KEY_AND_SERVER_ID =
-        MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SERVER_ID + "=?";
-    private static final String WHERE_ACCOUNT_AND_SYNC_INTERVAL_PING =
-        MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SYNC_INTERVAL +
-        '=' + Mailbox.CHECK_INTERVAL_PING;
-    private static final String AND_FREQUENCY_PING_PUSH_AND_NOT_ACCOUNT_MAILBOX = " AND " +
-        MailboxColumns.SYNC_INTERVAL + " IN (" + Mailbox.CHECK_INTERVAL_PING +
-        ',' + Mailbox.CHECK_INTERVAL_PUSH + ") AND " + MailboxColumns.TYPE + "!=\"" +
-        Mailbox.TYPE_EAS_ACCOUNT_MAILBOX + '\"';
-    private static final String WHERE_PUSH_HOLD_NOT_ACCOUNT_MAILBOX =
-        MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SYNC_INTERVAL +
-        '=' + Mailbox.CHECK_INTERVAL_PUSH_HOLD;
-
-    static private final String PING_COMMAND = "Ping";
-    // Command timeout is the the time allowed for reading data from an open connection before an
-    // IOException is thrown.  After a small added allowance, our watchdog alarm goes off (allowing
-    // us to detect a silently dropped connection).  The allowance is defined below.
-    static public final int COMMAND_TIMEOUT = 30*SECONDS;
-    // Connection timeout is the time given to connect to the server before reporting an IOException
-    static private final int CONNECTION_TIMEOUT = 20*SECONDS;
-    // The extra time allowed beyond the COMMAND_TIMEOUT before which our watchdog alarm triggers
-    static private final int WATCHDOG_TIMEOUT_ALLOWANCE = 30*SECONDS;
-
-    // The amount of time the account mailbox will sleep if there are no pingable mailboxes
-    // This could happen if the sync time is set to "never"; we always want to check in from time
-    // to time, however, for folder list/policy changes
-    static private final int ACCOUNT_MAILBOX_SLEEP_TIME = 20*MINUTES;
-    static private final String ACCOUNT_MAILBOX_SLEEP_TEXT =
-        "Account mailbox sleeping for " + (ACCOUNT_MAILBOX_SLEEP_TIME / MINUTES) + "m";
-
-    static private final String AUTO_DISCOVER_SCHEMA_PREFIX =
-        "http://schemas.microsoft.com/exchange/autodiscover/mobilesync/";
-    static private final String AUTO_DISCOVER_PAGE = "/autodiscover/autodiscover.xml";
-    static private final int EAS_REDIRECT_CODE = 451;
-
-    static public final int INTERNAL_SERVER_ERROR_CODE = 500;
-
-    static public final String EAS_12_POLICY_TYPE = "MS-EAS-Provisioning-WBXML";
-    static public final String EAS_2_POLICY_TYPE = "MS-WAP-Provisioning-XML";
-
-    static public final int MESSAGE_FLAG_MOVED_MESSAGE = 1 << Message.FLAG_SYNC_ADAPTER_SHIFT;
-
-    /**
-     * We start with an 8 minute timeout, and increase/decrease by 3 minutes at a time.  There's
-     * no point having a timeout shorter than 5 minutes, I think; at that point, we can just let
-     * the ping exception out.  The maximum I use is 17 minutes, which is really an empirical
-     * choice; too long and we risk silent connection loss and loss of push for that period.  Too
-     * short and we lose efficiency/battery life.
-     *
-     * If we ever have to drop the ping timeout, we'll never increase it again.  There's no point
-     * going into hysteresis; the NAT timeout isn't going to change without a change in connection,
-     * which will cause the sync service to be restarted at the starting heartbeat and going through
-     * the process again.
-     */
-    static private final int PING_MINUTES = 60; // in seconds
-    static private final int PING_FUDGE_LOW = 10;
-    static private final int PING_STARTING_HEARTBEAT = (8*PING_MINUTES)-PING_FUDGE_LOW;
-    static private final int PING_HEARTBEAT_INCREMENT = 3*PING_MINUTES;
-
-    // Maximum number of times we'll allow a sync to "loop" with MoreAvailable true before
-    // forcing it to stop.  This number has been determined empirically.
-    static private final int MAX_LOOPING_COUNT = 100;
-
-    static private final int PROTOCOL_PING_STATUS_COMPLETED = 1;
-
-    // The amount of time we allow for a thread to release its post lock after receiving an alert
-    static private final int POST_LOCK_TIMEOUT = 10*SECONDS;
-
-    // Fallbacks (in minutes) for ping loop failures
-    static private final int MAX_PING_FAILURES = 1;
-    static private final int PING_FALLBACK_INBOX = 5;
-    static private final int PING_FALLBACK_PIM = 25;
-
-    // The EAS protocol Provision status for "we implement all of the policies"
-    static private final String PROVISION_STATUS_OK = "1";
-    // The EAS protocol Provision status meaning "we partially implement the policies"
-    static private final String PROVISION_STATUS_PARTIAL = "2";
-
-    static /*package*/ final String DEVICE_TYPE = "Android";
-    static private final String USER_AGENT = DEVICE_TYPE + '/' + Build.VERSION.RELEASE + '-' +
-        Eas.CLIENT_VERSION;
-
-    // Reasonable default
-    public String mProtocolVersion = Eas.DEFAULT_PROTOCOL_VERSION;
-    public Double mProtocolVersionDouble;
-    protected String mDeviceId = null;
-    @VisibleForTesting
-    String mAuthString = null;
-    @VisibleForTesting
-    String mUserString = null;
-    @VisibleForTesting
-    String mBaseUriString = null;
-    public String mHostAddress;
-    public String mUserName;
-    public String mPassword;
-
-    // The parameters for the connection must be modified through setConnectionParameters
-    private boolean mSsl = true;
-    private boolean mTrustSsl = false;
-    private String mClientCertAlias = null;
-
-    public ContentResolver mContentResolver;
-    private final String[] mBindArguments = new String[2];
-    private ArrayList<String> mPingChangeList;
-    // The HttpPost in progress
-    private volatile HttpPost mPendingPost = null;
-    // Our heartbeat when we are waiting for ping boxes to be ready
-    /*package*/ int mPingForceHeartbeat = 2*PING_MINUTES;
-    // The minimum heartbeat we will send
-    /*package*/ int mPingMinHeartbeat = (5*PING_MINUTES)-PING_FUDGE_LOW;
-    // The maximum heartbeat we will send
-    /*package*/ int mPingMaxHeartbeat = (17*PING_MINUTES)-PING_FUDGE_LOW;
-    // The ping time (in seconds)
-    /*package*/ int mPingHeartbeat = PING_STARTING_HEARTBEAT;
-    // The longest successful ping heartbeat
-    private int mPingHighWaterMark = 0;
-    // Whether we've ever lowered the heartbeat
-    /*package*/ boolean mPingHeartbeatDropped = false;
-    // Whether a POST was aborted due to alarm (watchdog alarm)
-    private boolean mPostAborted = false;
-    // Whether a POST was aborted due to reset
-    private boolean mPostReset = false;
-    // Whether or not the sync service is valid (usable)
-    public boolean mIsValid = true;
-
-    // Whether the most recent upsync failed (status 7)
-    public boolean mUpsyncFailed = false;
-
-    public EasSyncService(Context _context, Mailbox _mailbox) {
-        super(_context, _mailbox);
-        mContentResolver = _context.getContentResolver();
-        if (mAccount == null) {
-            mIsValid = false;
-            return;
-        }
-        HostAuth ha = HostAuth.restoreHostAuthWithId(_context, mAccount.mHostAuthKeyRecv);
-        if (ha == null) {
-            mIsValid = false;
-            return;
-        }
-        mSsl = (ha.mFlags & HostAuth.FLAG_SSL) != 0;
-        mTrustSsl = (ha.mFlags & HostAuth.FLAG_TRUST_ALL) != 0;
-    }
-
-    private EasSyncService(String prefix) {
-        super(prefix);
-    }
-
-    public EasSyncService() {
-        this("EAS Validation");
-    }
-
-    /**
-     * Try to wake up a sync thread that is waiting on an HttpClient POST and has waited past its
-     * socket timeout without having thrown an Exception
-     *
-     * @return true if the POST was successfully stopped; false if we've failed and interrupted
-     * the thread
-     */
-    @Override
-    public boolean alarm() {
-        HttpPost post;
-        if (mThread == null) return true;
-        String threadName = mThread.getName();
-
-        // Synchronize here so that we are guaranteed to have valid mPendingPost and mPostLock
-        // executePostWithTimeout (which executes the HttpPost) also uses this lock
-        synchronized(getSynchronizer()) {
-            // Get a reference to the current post lock
-            post = mPendingPost;
-            if (post != null) {
-                if (Eas.USER_LOG) {
-                    URI uri = post.getURI();
-                    if (uri != null) {
-                        String query = uri.getQuery();
-                        if (query == null) {
-                            query = "POST";
-                        }
-                        userLog(threadName, ": Alert, aborting ", query);
-                    } else {
-                        userLog(threadName, ": Alert, no URI?");
-                    }
-                }
-                // Abort the POST
-                mPostAborted = true;
-                post.abort();
-            } else {
-                // If there's no POST, we're done
-                userLog("Alert, no pending POST");
-                return true;
-            }
-        }
-
-        // Wait for the POST to finish
-        try {
-            Thread.sleep(POST_LOCK_TIMEOUT);
-        } catch (InterruptedException e) {
-        }
-
-        State s = mThread.getState();
-        if (Eas.USER_LOG) {
-            userLog(threadName + ": State = " + s.name());
-        }
-
-        synchronized (getSynchronizer()) {
-            // If the thread is still hanging around and the same post is pending, let's try to
-            // stop the thread with an interrupt.
-            if ((s != State.TERMINATED) && (mPendingPost != null) && (mPendingPost == post)) {
-                mStop = true;
-                mThread.interrupt();
-                userLog("Interrupting...");
-                // Let the caller know we had to interrupt the thread
-                return false;
-            }
-        }
-        // Let the caller know that the alarm was handled normally
-        return true;
-    }
-
-    @Override
-    public void reset() {
-        synchronized(getSynchronizer()) {
-            if (mPendingPost != null) {
-                URI uri = mPendingPost.getURI();
-                if (uri != null) {
-                    String query = uri.getQuery();
-                    if (query.startsWith("Cmd=Ping")) {
-                        userLog("Reset, aborting Ping");
-                        mPostReset = true;
-                        mPendingPost.abort();
-                    }
-                }
-            }
-        }
-    }
-
-    @Override
-    public void stop() {
-        mStop = true;
-        synchronized(getSynchronizer()) {
-            if (mPendingPost != null) {
-                mPendingPost.abort();
-            }
-        }
-    }
-
-    @Override
-    public void addRequest(Request request) {
-        // Don't allow duplicates of requests; just refuse them
-        if (mRequestQueue.contains(request)) return;
-        // Add the request
-        super.addRequest(request);
-    }
-
-    private void setupProtocolVersion(EasSyncService service, Header versionHeader)
-            throws MessagingException {
-        // The string is a comma separated list of EAS versions in ascending order
-        // e.g. 1.0,2.0,2.5,12.0,12.1,14.0,14.1
-        String supportedVersions = versionHeader.getValue();
-        userLog("Server supports versions: ", supportedVersions);
-        String[] supportedVersionsArray = supportedVersions.split(",");
-        String ourVersion = null;
-        // Find the most recent version we support
-        for (String version: supportedVersionsArray) {
-            if (version.equals(Eas.SUPPORTED_PROTOCOL_EX2003) ||
-                    version.equals(Eas.SUPPORTED_PROTOCOL_EX2007) ||
-                    version.equals(Eas.SUPPORTED_PROTOCOL_EX2007_SP1) ||
-                    version.equals(Eas.SUPPORTED_PROTOCOL_EX2010) ||
-                    version.equals(Eas.SUPPORTED_PROTOCOL_EX2010_SP1)) {
-                ourVersion = version;
-            }
-        }
-        // If we don't support any of the servers supported versions, throw an exception here
-        // This will cause validation to fail
-        if (ourVersion == null) {
-            Log.w(TAG, "No supported EAS versions: " + supportedVersions);
-            throw new MessagingException(MessagingException.PROTOCOL_VERSION_UNSUPPORTED);
-        } else {
-            // Debug code for testing EAS 14.0; disables support for EAS 14.1
-            // "adb shell setprop log.tag.Exchange14 VERBOSE"
-            if (ourVersion.equals(Eas.SUPPORTED_PROTOCOL_EX2010_SP1) &&
-                    Log.isLoggable("Exchange14", Log.VERBOSE)) {
-                ourVersion = Eas.SUPPORTED_PROTOCOL_EX2010;
-            }
-            service.mProtocolVersion = ourVersion;
-            service.mProtocolVersionDouble = Eas.getProtocolVersionDouble(ourVersion);
-            Account account = service.mAccount;
-            if (account != null) {
-                account.mProtocolVersion = ourVersion;
-                // Fixup search flags, if they're not set
-                if (service.mProtocolVersionDouble >= 12.0 &&
-                        (account.mFlags & Account.FLAGS_SUPPORTS_SEARCH) == 0) {
-                    if (account.isSaved()) {
-                        ContentValues cv = new ContentValues();
-                        account.mFlags |=
-                            Account.FLAGS_SUPPORTS_GLOBAL_SEARCH + Account.FLAGS_SUPPORTS_SEARCH;
-                        cv.put(AccountColumns.FLAGS, account.mFlags);
-                        account.update(service.mContext, cv);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Create an EasSyncService for the specified account
-     *
-     * @param context the caller's context
-     * @param account the account
-     * @return the service, or null if the account is on hold or hasn't been initialized
-     */
-    public static EasSyncService setupServiceForAccount(Context context, Account account) {
-        // Just return null if we're on security hold
-        if ((account.mFlags & Account.FLAGS_SECURITY_HOLD) != 0) {
-            return null;
-        }
-        // If there's no protocol version, we're not initialized
-        String protocolVersion = account.mProtocolVersion;
-        if (protocolVersion == null) {
-            return null;
-        }
-        EasSyncService svc = new EasSyncService("OutOfBand");
-        HostAuth ha = HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv);
-        svc.mProtocolVersion = protocolVersion;
-        svc.mProtocolVersionDouble = Eas.getProtocolVersionDouble(protocolVersion);
-        svc.mContext = context;
-        svc.mHostAddress = ha.mAddress;
-        svc.mUserName = ha.mLogin;
-        svc.mPassword = ha.mPassword;
-        try {
-            svc.setConnectionParameters(
-                    (ha.mFlags & HostAuth.FLAG_SSL) != 0,
-                    (ha.mFlags & HostAuth.FLAG_TRUST_ALL) != 0,
-                    ha.mClientCertAlias);
-            svc.mDeviceId = ExchangeService.getDeviceId(context);
-        } catch (IOException e) {
-            return null;
-        } catch (CertificateException e) {
-            return null;
-        }
-        svc.mAccount = account;
-        return svc;
-    }
-
-    /**
-     * Get a redirect address and validate against it
-     * @param resp the EasResponse to our POST
-     * @param hostAuth the HostAuth we're using to validate
-     * @return true if we have an updated HostAuth (with redirect address); false otherwise
-     */
-    private boolean getValidateRedirect(EasResponse resp, HostAuth hostAuth) {
-        Header locHeader = resp.getHeader("X-MS-Location");
-        if (locHeader != null) {
-            String loc;
-            try {
-                loc = locHeader.getValue();
-                // Reset our host address and uncache our base uri
-                mHostAddress = Uri.parse(loc).getHost();
-                mBaseUriString = null;
-                hostAuth.mAddress = mHostAddress;
-                userLog("Redirecting to: " + loc);
-                return true;
-            } catch (RuntimeException e) {
-                // Just don't crash if the Uri is illegal
-            }
-        }
-        return false;
-    }
-
-    private static final int MAX_REDIRECTS = 3;
-    private int mRedirectCount = 0;
-
-    @Override
-    public Bundle validateAccount(HostAuth hostAuth, Context context) {
-        Bundle bundle = new Bundle();
-        int resultCode = MessagingException.NO_ERROR;
-        try {
-            userLog("Testing EAS: ", hostAuth.mAddress, ", ", hostAuth.mLogin,
-                    ", ssl = ", hostAuth.shouldUseSsl() ? "1" : "0");
-            mContext = context;
-            mHostAddress = hostAuth.mAddress;
-            mUserName = hostAuth.mLogin;
-            mPassword = hostAuth.mPassword;
-
-            setConnectionParameters(
-                    hostAuth.shouldUseSsl(),
-                    hostAuth.shouldTrustAllServerCerts(),
-                    hostAuth.mClientCertAlias);
-            mDeviceId = ExchangeService.getDeviceId(context);
-            mAccount = new Account();
-            mAccount.mEmailAddress = hostAuth.mLogin;
-            EasResponse resp = sendHttpClientOptions();
-            try {
-                int code = resp.getStatus();
-                userLog("Validation (OPTIONS) response: " + code);
-                if (code == HttpStatus.SC_OK) {
-                    // No exception means successful validation
-                    Header commands = resp.getHeader("MS-ASProtocolCommands");
-                    Header versions = resp.getHeader("ms-asprotocolversions");
-                    // Make sure we've got the right protocol version set up
-                    try {
-                        if (commands == null || versions == null) {
-                            userLog("OPTIONS response without commands or versions");
-                            // We'll treat this as a protocol exception
-                            throw new MessagingException(0);
-                        }
-                        setupProtocolVersion(this, versions);
-                    } catch (MessagingException e) {
-                        bundle.putInt(EmailServiceProxy.VALIDATE_BUNDLE_RESULT_CODE,
-                                MessagingException.PROTOCOL_VERSION_UNSUPPORTED);
-                        return bundle;
-                    }
-
-                    // Run second test here for provisioning failures using FolderSync
-                    userLog("Try folder sync");
-                    // Send "0" as the sync key for new accounts; otherwise, use the current key
-                    String syncKey = "0";
-                    Account existingAccount = Utility.findExistingAccount(
-                            context, -1L, hostAuth.mAddress, hostAuth.mLogin);
-                    if (existingAccount != null && existingAccount.mSyncKey != null) {
-                        syncKey = existingAccount.mSyncKey;
-                    }
-                    Serializer s = new Serializer();
-                    s.start(Tags.FOLDER_FOLDER_SYNC).start(Tags.FOLDER_SYNC_KEY).text(syncKey)
-                        .end().end().done();
-                    resp = sendHttpClientPost("FolderSync", s.toByteArray());
-                    code = resp.getStatus();
-                    // Handle HTTP error responses accordingly
-                    if (code == HttpStatus.SC_FORBIDDEN) {
-                        // For validation only, we take 403 as ACCESS_DENIED (the account isn't
-                        // authorized, possibly due to device type)
-                        resultCode = MessagingException.ACCESS_DENIED;
-                    } else if (EasResponse.isProvisionError(code)) {
-                        // The device needs to have security policies enforced
-                        throw new CommandStatusException(CommandStatus.NEEDS_PROVISIONING);
-                    } else if (code == HttpStatus.SC_NOT_FOUND) {
-                        // We get a 404 from OWA addresses (which are NOT EAS addresses)
-                        resultCode = MessagingException.PROTOCOL_VERSION_UNSUPPORTED;
-                    } else if (code == HttpStatus.SC_UNAUTHORIZED) {
-                        resultCode = resp.isMissingCertificate()
-                                ? MessagingException.CLIENT_CERTIFICATE_REQUIRED
-                                : MessagingException.AUTHENTICATION_FAILED;
-                    } else if (code != HttpStatus.SC_OK) {
-                        if ((code == EAS_REDIRECT_CODE) && (mRedirectCount++ < MAX_REDIRECTS) &&
-                                getValidateRedirect(resp, hostAuth)) {
-                            return validateAccount(hostAuth, context);
-                        }
-                        // Fail generically with anything other than success
-                        userLog("Unexpected response for FolderSync: ", code);
-                        resultCode = MessagingException.UNSPECIFIED_EXCEPTION;
-                    } else {
-                        // We need to parse the result to see if we've got a provisioning issue
-                        // (EAS 14.0 only)
-                        if (!resp.isEmpty()) {
-                            InputStream is = resp.getInputStream();
-                            // Create the parser with statusOnly set to true; we only care about
-                            // seeing if a CommandStatusException is thrown (indicating a
-                            // provisioning failure)
-                            new FolderSyncParser(is, new AccountSyncAdapter(this), true).parse();
-                        }
-                        userLog("Validation successful");
-                    }
-                } else if (EasResponse.isAuthError(code)) {
-                    userLog("Authentication failed");
-                    resultCode = resp.isMissingCertificate()
-                            ? MessagingException.CLIENT_CERTIFICATE_REQUIRED
-                            : MessagingException.AUTHENTICATION_FAILED;
-                } else if (code == INTERNAL_SERVER_ERROR_CODE) {
-                    // For Exchange 2003, this could mean an authentication failure OR server error
-                    userLog("Internal server error");
-                    resultCode = MessagingException.AUTHENTICATION_FAILED_OR_SERVER_ERROR;
-                } else {
-                    if ((code == EAS_REDIRECT_CODE) && (mRedirectCount++ < MAX_REDIRECTS) &&
-                            getValidateRedirect(resp, hostAuth)) {
-                        return validateAccount(hostAuth, context);
-                    }
-                    // TODO Need to catch other kinds of errors (e.g. policy) For now, report code.
-                    userLog("Validation failed, reporting I/O error: ", code);
-                    resultCode = MessagingException.IOERROR;
-                }
-            } catch (CommandStatusException e) {
-                int status = e.mStatus;
-                if (CommandStatus.isNeedsProvisioning(status)) {
-                    // Get the policies and see if we are able to support them
-                    ProvisionParser pp = canProvision();
-                    if (pp != null && pp.hasSupportablePolicySet()) {
-                        // Set the proper result code and save the PolicySet in our Bundle
-                        resultCode = MessagingException.SECURITY_POLICIES_REQUIRED;
-                        bundle.putParcelable(EmailServiceProxy.VALIDATE_BUNDLE_POLICY_SET,
-                                pp.getPolicy());
-                        if (mProtocolVersionDouble == Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE) {
-                            mAccount.mSecuritySyncKey = pp.getSecuritySyncKey();
-                            if (!sendSettings()) {
-                                userLog("Denied access: ", CommandStatus.toString(status));
-                                resultCode = MessagingException.ACCESS_DENIED;
-                            }
-                        }
-                    } else
-                        // If not, set the proper code (the account will not be created)
-                        resultCode = MessagingException.SECURITY_POLICIES_UNSUPPORTED;
-                        bundle.putStringArray(
-                                EmailServiceProxy.VALIDATE_BUNDLE_UNSUPPORTED_POLICIES,
-                                ((pp == null) ? null : pp.getUnsupportedPolicies()));
-                } else if (CommandStatus.isDeniedAccess(status)) {
-                    userLog("Denied access: ", CommandStatus.toString(status));
-                    resultCode = MessagingException.ACCESS_DENIED;
-                } else if (CommandStatus.isTransientError(status)) {
-                    userLog("Transient error: ", CommandStatus.toString(status));
-                    resultCode = MessagingException.IOERROR;
-                } else {
-                    userLog("Unexpected response: ", CommandStatus.toString(status));
-                    resultCode = MessagingException.UNSPECIFIED_EXCEPTION;
-                }
-            } finally {
-                resp.close();
-           }
-        } catch (IOException e) {
-            Throwable cause = e.getCause();
-            if (cause != null && cause instanceof CertificateException) {
-                // This could be because the server's certificate failed to validate.
-                userLog("CertificateException caught: ", e.getMessage());
-                resultCode = MessagingException.GENERAL_SECURITY;
-            }
-            userLog("IOException caught: ", e.getMessage());
-            resultCode = MessagingException.IOERROR;
-        } catch (CertificateException e) {
-            // This occurs if the client certificate the user specified is invalid/inaccessible.
-            userLog("CertificateException caught: ", e.getMessage());
-            resultCode = MessagingException.CLIENT_CERTIFICATE_ERROR;
-        }
-        bundle.putInt(EmailServiceProxy.VALIDATE_BUNDLE_RESULT_CODE, resultCode);
-        return bundle;
-    }
-
-    /**
-     * Gets the redirect location from the HTTP headers and uses that to modify the HttpPost so that
-     * it can be reused
-     *
-     * @param resp the HttpResponse that indicates a redirect (451)
-     * @param post the HttpPost that was originally sent to the server
-     * @return the HttpPost, updated with the redirect location
-     */
-    private HttpPost getRedirect(HttpResponse resp, HttpPost post) {
-        Header locHeader = resp.getFirstHeader("X-MS-Location");
-        if (locHeader != null) {
-            String loc = locHeader.getValue();
-            // If we've gotten one and it shows signs of looking like an address, we try
-            // sending our request there
-            if (loc != null && loc.startsWith("http")) {
-                post.setURI(URI.create(loc));
-                return post;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Send the POST command to the autodiscover server, handling a redirect, if necessary, and
-     * return the HttpResponse.  If we get a 401 (unauthorized) error and we're using the
-     * full email address, try the bare user name instead (e.g. foo instead of foo@bar.com)
-     *
-     * @param client the HttpClient to be used for the request
-     * @param post the HttpPost we're going to send
-     * @param canRetry whether we can retry using the bare name on an authentication failure (401)
-     * @return an HttpResponse from the original or redirect server
-     * @throws IOException on any IOException within the HttpClient code
-     * @throws MessagingException
-     */
-    private EasResponse postAutodiscover(HttpClient client, HttpPost post, boolean canRetry)
-            throws IOException, MessagingException {
-        userLog("Posting autodiscover to: " + post.getURI());
-        EasResponse resp = executePostWithTimeout(client, post, COMMAND_TIMEOUT);
-        int code = resp.getStatus();
-        // On a redirect, try the new location
-        if (code == EAS_REDIRECT_CODE) {
-            post = getRedirect(resp.mResponse, post);
-            if (post != null) {
-                userLog("Posting autodiscover to redirect: " + post.getURI());
-                return executePostWithTimeout(client, post, COMMAND_TIMEOUT);
-            }
-        // 401 (Unauthorized) is for true auth errors when used in Autodiscover
-        } else if (code == HttpStatus.SC_UNAUTHORIZED) {
-            if (canRetry && mUserName.contains("@")) {
-                // Try again using the bare user name
-                int atSignIndex = mUserName.indexOf('@');
-                mUserName = mUserName.substring(0, atSignIndex);
-                cacheAuthUserAndBaseUriStrings();
-                userLog("401 received; trying username: ", mUserName);
-                // Recreate the basic authentication string and reset the header
-                post.removeHeaders("Authorization");
-                post.setHeader("Authorization", mAuthString);
-                return postAutodiscover(client, post, false);
-            }
-            throw new MessagingException(MessagingException.AUTHENTICATION_FAILED);
-        // 403 (and others) we'll just punt on
-        } else if (code != HttpStatus.SC_OK) {
-            // We'll try the next address if this doesn't work
-            userLog("Code: " + code + ", throwing IOException");
-            throw new IOException();
-        }
-        return resp;
-    }
-
-    /**
-     * Use the Exchange 2007 AutoDiscover feature to try to retrieve server information using
-     * only an email address and the password
-     *
-     * @param userName the user's email address
-     * @param password the user's password
-     * @return a HostAuth ready to be saved in an Account or null (failure)
-     */
-    public Bundle tryAutodiscover(String userName, String password) throws RemoteException {
-        XmlSerializer s = Xml.newSerializer();
-        ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
-        HostAuth hostAuth = new HostAuth();
-        Bundle bundle = new Bundle();
-        bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE,
-                MessagingException.NO_ERROR);
-        try {
-            // Build the XML document that's sent to the autodiscover server(s)
-            s.setOutput(os, "UTF-8");
-            s.startDocument("UTF-8", false);
-            s.startTag(null, "Autodiscover");
-            s.attribute(null, "xmlns", AUTO_DISCOVER_SCHEMA_PREFIX + "requestschema/2006");
-            s.startTag(null, "Request");
-            s.startTag(null, "EMailAddress").text(userName).endTag(null, "EMailAddress");
-            s.startTag(null, "AcceptableResponseSchema");
-            s.text(AUTO_DISCOVER_SCHEMA_PREFIX + "responseschema/2006");
-            s.endTag(null, "AcceptableResponseSchema");
-            s.endTag(null, "Request");
-            s.endTag(null, "Autodiscover");
-            s.endDocument();
-            String req = os.toString();
-
-            // Initialize the user name and password
-            mUserName = userName;
-            mPassword = password;
-            // Make sure the authentication string is recreated and cached
-            cacheAuthUserAndBaseUriStrings();
-
-            // Split out the domain name
-            int amp = userName.indexOf('@');
-            // The UI ensures that userName is a valid email address
-            if (amp < 0) {
-                throw new RemoteException();
-            }
-            String domain = userName.substring(amp + 1);
-
-            // There are up to four attempts here; the two URLs that we're supposed to try per the
-            // specification, and up to one redirect for each (handled in postAutodiscover)
-            // Note: The expectation is that, of these four attempts, only a single server will
-            // actually be identified as the autodiscover server.  For the identified server,
-            // we may also try a 2nd connection with a different format (bare name).
-
-            // Try the domain first and see if we can get a response
-            HttpPost post = new HttpPost("https://" + domain + AUTO_DISCOVER_PAGE);
-            setHeaders(post, false);
-            post.setHeader("Content-Type", "text/xml");
-            post.setEntity(new StringEntity(req));
-            HttpClient client = getHttpClient(COMMAND_TIMEOUT);
-            EasResponse resp;
-            try {
-                resp = postAutodiscover(client, post, true /*canRetry*/);
-            } catch (IOException e1) {
-                userLog("IOException in autodiscover; trying alternate address");
-                // We catch the IOException here because we have an alternate address to try
-                post.setURI(URI.create("https://autodiscover." + domain + AUTO_DISCOVER_PAGE));
-                // If we fail here, we're out of options, so we let the outer try catch the
-                // IOException and return null
-                resp = postAutodiscover(client, post, true /*canRetry*/);
-            }
-
-            try {
-                // Get the "final" code; if it's not 200, just return null
-                int code = resp.getStatus();
-                userLog("Code: " + code);
-                if (code != HttpStatus.SC_OK) return null;
-
-                InputStream is = resp.getInputStream();
-                // The response to Autodiscover is regular XML (not WBXML)
-                // If we ever get an error in this process, we'll just punt and return null
-                XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
-                XmlPullParser parser = factory.newPullParser();
-                parser.setInput(is, "UTF-8");
-                int type = parser.getEventType();
-                if (type == XmlPullParser.START_DOCUMENT) {
-                    type = parser.next();
-                    if (type == XmlPullParser.START_TAG) {
-                        String name = parser.getName();
-                        if (name.equals("Autodiscover")) {
-                            hostAuth = new HostAuth();
-                            parseAutodiscover(parser, hostAuth);
-                            // On success, we'll have a server address and login
-                            if (hostAuth.mAddress != null) {
-                                // Fill in the rest of the HostAuth
-                                // We use the user name and password that were successful during
-                                // the autodiscover process
-                                hostAuth.mLogin = mUserName;
-                                hostAuth.mPassword = mPassword;
-                                // Note: there is no way we can auto-discover the proper client
-                                // SSL certificate to use, if one is needed.
-                                hostAuth.mPort = 443;
-                                hostAuth.mProtocol = "eas";
-                                hostAuth.mFlags =
-                                    HostAuth.FLAG_SSL | HostAuth.FLAG_AUTHENTICATE;
-                                bundle.putParcelable(
-                                        EmailServiceProxy.AUTO_DISCOVER_BUNDLE_HOST_AUTH, hostAuth);
-                            } else {
-                                bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE,
-                                        MessagingException.UNSPECIFIED_EXCEPTION);
-                            }
-                        }
-                    }
-                }
-            } catch (XmlPullParserException e1) {
-                // This would indicate an I/O error of some sort
-                // We will simply return null and user can configure manually
-            } finally {
-               resp.close();
-            }
-        // There's no reason at all for exceptions to be thrown, and it's ok if so.
-        // We just won't do auto-discover; user can configure manually
-       } catch (IllegalArgumentException e) {
-             bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE,
-                     MessagingException.UNSPECIFIED_EXCEPTION);
-       } catch (IllegalStateException e) {
-            bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE,
-                    MessagingException.UNSPECIFIED_EXCEPTION);
-       } catch (IOException e) {
-            userLog("IOException in Autodiscover", e);
-            bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE,
-                    MessagingException.IOERROR);
-        } catch (MessagingException e) {
-            bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE,
-                    MessagingException.AUTODISCOVER_AUTHENTICATION_FAILED);
-        }
-        return bundle;
-    }
-
-    void parseServer(XmlPullParser parser, HostAuth hostAuth)
-            throws XmlPullParserException, IOException {
-        boolean mobileSync = false;
-        while (true) {
-            int type = parser.next();
-            if (type == XmlPullParser.END_TAG && parser.getName().equals("Server")) {
-                break;
-            } else if (type == XmlPullParser.START_TAG) {
-                String name = parser.getName();
-                if (name.equals("Type")) {
-                    if (parser.nextText().equals("MobileSync")) {
-                        mobileSync = true;
-                    }
-                } else if (mobileSync && name.equals("Url")) {
-                    String url = parser.nextText().toLowerCase();
-                    // This will look like https://<server address>/Microsoft-Server-ActiveSync
-                    // We need to extract the <server address>
-                    if (url.startsWith("https://") &&
-                            url.endsWith("/microsoft-server-activesync")) {
-                        int lastSlash = url.lastIndexOf('/');
-                        hostAuth.mAddress = url.substring(8, lastSlash);
-                        userLog("Autodiscover, server: " + hostAuth.mAddress);
-                    }
-                }
-            }
-        }
-    }
-
-    void parseSettings(XmlPullParser parser, HostAuth hostAuth)
-            throws XmlPullParserException, IOException {
-        while (true) {
-            int type = parser.next();
-            if (type == XmlPullParser.END_TAG && parser.getName().equals("Settings")) {
-                break;
-            } else if (type == XmlPullParser.START_TAG) {
-                String name = parser.getName();
-                if (name.equals("Server")) {
-                    parseServer(parser, hostAuth);
-                }
-            }
-        }
-    }
-
-    void parseAction(XmlPullParser parser, HostAuth hostAuth)
-            throws XmlPullParserException, IOException {
-        while (true) {
-            int type = parser.next();
-            if (type == XmlPullParser.END_TAG && parser.getName().equals("Action")) {
-                break;
-            } else if (type == XmlPullParser.START_TAG) {
-                String name = parser.getName();
-                if (name.equals("Error")) {
-                    // Should parse the error
-                } else if (name.equals("Redirect")) {
-                    Log.d(TAG, "Redirect: " + parser.nextText());
-                } else if (name.equals("Settings")) {
-                    parseSettings(parser, hostAuth);
-                }
-            }
-        }
-    }
-
-    void parseUser(XmlPullParser parser, HostAuth hostAuth)
-            throws XmlPullParserException, IOException {
-        while (true) {
-            int type = parser.next();
-            if (type == XmlPullParser.END_TAG && parser.getName().equals("User")) {
-                break;
-            } else if (type == XmlPullParser.START_TAG) {
-                String name = parser.getName();
-                if (name.equals("EMailAddress")) {
-                    String addr = parser.nextText();
-                    userLog("Autodiscover, email: " + addr);
-                } else if (name.equals("DisplayName")) {
-                    String dn = parser.nextText();
-                    userLog("Autodiscover, user: " + dn);
-                }
-            }
-        }
-    }
-
-    void parseResponse(XmlPullParser parser, HostAuth hostAuth)
-            throws XmlPullParserException, IOException {
-        while (true) {
-            int type = parser.next();
-            if (type == XmlPullParser.END_TAG && parser.getName().equals("Response")) {
-                break;
-            } else if (type == XmlPullParser.START_TAG) {
-                String name = parser.getName();
-                if (name.equals("User")) {
-                    parseUser(parser, hostAuth);
-                } else if (name.equals("Action")) {
-                    parseAction(parser, hostAuth);
-                }
-            }
-        }
-    }
-
-    void parseAutodiscover(XmlPullParser parser, HostAuth hostAuth)
-            throws XmlPullParserException, IOException {
-        while (true) {
-            int type = parser.nextTag();
-            if (type == XmlPullParser.END_TAG && parser.getName().equals("Autodiscover")) {
-                break;
-            } else if (type == XmlPullParser.START_TAG && parser.getName().equals("Response")) {
-                parseResponse(parser, hostAuth);
-            }
-        }
-    }
-
-    /**
-     * Contact the GAL and obtain a list of matching accounts
-     * @param context caller's context
-     * @param accountId the account Id to search
-     * @param filter the characters entered so far
-     * @return a result record or null for no data
-     *
-     * TODO: shorter timeout for interactive lookup
-     * TODO: make watchdog actually work (it doesn't understand our service w/Mailbox == 0)
-     * TODO: figure out why sendHttpClientPost() hangs - possibly pool exhaustion
-     */
-    static public GalResult searchGal(Context context, long accountId, String filter, int limit) {
-        Account acct = Account.restoreAccountWithId(context, accountId);
-        if (acct != null) {
-            EasSyncService svc = setupServiceForAccount(context, acct);
-            if (svc == null) return null;
-            try {
-                Serializer s = new Serializer();
-                s.start(Tags.SEARCH_SEARCH).start(Tags.SEARCH_STORE);
-                s.data(Tags.SEARCH_NAME, "GAL").data(Tags.SEARCH_QUERY, filter);
-                s.start(Tags.SEARCH_OPTIONS);
-                s.data(Tags.SEARCH_RANGE, "0-" + Integer.toString(limit - 1));
-                s.end().end().end().done();
-                EasResponse resp = svc.sendHttpClientPost("Search", s.toByteArray());
-                try {
-                    int code = resp.getStatus();
-                    if (code == HttpStatus.SC_OK) {
-                        InputStream is = resp.getInputStream();
-                        try {
-                            GalParser gp = new GalParser(is, svc);
-                            if (gp.parse()) {
-                                return gp.getGalResult();
-                            }
-                        } finally {
-                            is.close();
-                        }
-                    } else {
-                        svc.userLog("GAL lookup returned " + code);
-                    }
-                } finally {
-                    resp.close();
-                }
-            } catch (IOException e) {
-                // GAL is non-critical; we'll just go on
-                svc.userLog("GAL lookup exception " + e);
-            }
-        }
-        return null;
-    }
-    /**
-     * Send an email responding to a Message that has been marked as a meeting request.  The message
-     * will consist a little bit of event information and an iCalendar attachment
-     * @param msg the meeting request email
-     */
-    private void sendMeetingResponseMail(Message msg, int response) {
-        // Get the meeting information; we'd better have some...
-        if (msg.mMeetingInfo == null) return;
-        PackedString meetingInfo = new PackedString(msg.mMeetingInfo);
-
-        // This will come as "First Last" <box@server.blah>, so we use Address to
-        // parse it into parts; we only need the email address part for the ics file
-        Address[] addrs = Address.parse(meetingInfo.get(MeetingInfo.MEETING_ORGANIZER_EMAIL));
-        // It shouldn't be possible, but handle it anyway
-        if (addrs.length != 1) return;
-        String organizerEmail = addrs[0].getAddress();
-
-        String dtStamp = meetingInfo.get(MeetingInfo.MEETING_DTSTAMP);
-        String dtStart = meetingInfo.get(MeetingInfo.MEETING_DTSTART);
-        String dtEnd = meetingInfo.get(MeetingInfo.MEETING_DTEND);
-
-        // What we're doing here is to create an Entity that looks like an Event as it would be
-        // stored by CalendarProvider
-        ContentValues entityValues = new ContentValues();
-        Entity entity = new Entity(entityValues);
-
-        // Fill in times, location, title, and organizer
-        entityValues.put("DTSTAMP",
-                CalendarUtilities.convertEmailDateTimeToCalendarDateTime(dtStamp));
-        entityValues.put(Events.DTSTART, Utility.parseEmailDateTimeToMillis(dtStart));
-        entityValues.put(Events.DTEND, Utility.parseEmailDateTimeToMillis(dtEnd));
-        entityValues.put(Events.EVENT_LOCATION, meetingInfo.get(MeetingInfo.MEETING_LOCATION));
-        entityValues.put(Events.TITLE, meetingInfo.get(MeetingInfo.MEETING_TITLE));
-        entityValues.put(Events.ORGANIZER, organizerEmail);
-
-        // Add ourselves as an attendee, using our account email address
-        ContentValues attendeeValues = new ContentValues();
-        attendeeValues.put(Attendees.ATTENDEE_RELATIONSHIP,
-                Attendees.RELATIONSHIP_ATTENDEE);
-        attendeeValues.put(Attendees.ATTENDEE_EMAIL, mAccount.mEmailAddress);
-        entity.addSubValue(Attendees.CONTENT_URI, attendeeValues);
-
-        // Add the organizer
-        ContentValues organizerValues = new ContentValues();
-        organizerValues.put(Attendees.ATTENDEE_RELATIONSHIP,
-                Attendees.RELATIONSHIP_ORGANIZER);
-        organizerValues.put(Attendees.ATTENDEE_EMAIL, organizerEmail);
-        entity.addSubValue(Attendees.CONTENT_URI, organizerValues);
-
-        // Create a message from the Entity we've built.  The message will have fields like
-        // to, subject, date, and text filled in.  There will also be an "inline" attachment
-        // which is in iCalendar format
-        int flag;
-        switch(response) {
-            case EmailServiceConstants.MEETING_REQUEST_ACCEPTED:
-                flag = Message.FLAG_OUTGOING_MEETING_ACCEPT;
-                break;
-            case EmailServiceConstants.MEETING_REQUEST_DECLINED:
-                flag = Message.FLAG_OUTGOING_MEETING_DECLINE;
-                break;
-            case EmailServiceConstants.MEETING_REQUEST_TENTATIVE:
-            default:
-                flag = Message.FLAG_OUTGOING_MEETING_TENTATIVE;
-                break;
-        }
-        Message outgoingMsg =
-            CalendarUtilities.createMessageForEntity(mContext, entity, flag,
-                    meetingInfo.get(MeetingInfo.MEETING_UID), mAccount);
-        // Assuming we got a message back (we might not if the event has been deleted), send it
-        if (outgoingMsg != null) {
-            EasOutboxService.sendMessage(mContext, mAccount.mId, outgoingMsg);
-        }
-    }
-
-    /**
-     * Responds to a move request.  The MessageMoveRequest is basically our
-     * wrapper for the MoveItems service call
-     * @param req the request (message id and "to" mailbox id)
-     * @throws IOException
-     */
-    protected void messageMoveRequest(MessageMoveRequest req) throws IOException {
-        // Retrieve the message and mailbox; punt if either are null
-        Message msg = Message.restoreMessageWithId(mContext, req.mMessageId);
-        if (msg == null) return;
-        Cursor c = mContentResolver.query(ContentUris.withAppendedId(Message.UPDATED_CONTENT_URI,
-                msg.mId), new String[] {MessageColumns.MAILBOX_KEY}, null, null, null);
-        if (c == null) throw new ProviderUnavailableException();
-        Mailbox srcMailbox = null;
-        try {
-            if (!c.moveToNext()) return;
-            srcMailbox = Mailbox.restoreMailboxWithId(mContext, c.getLong(0));
-        } finally {
-            c.close();
-        }
-        if (srcMailbox == null) return;
-        Mailbox dstMailbox = Mailbox.restoreMailboxWithId(mContext, req.mMailboxId);
-        if (dstMailbox == null) return;
-        Serializer s = new Serializer();
-        s.start(Tags.MOVE_MOVE_ITEMS).start(Tags.MOVE_MOVE);
-        s.data(Tags.MOVE_SRCMSGID, msg.mServerId);
-        s.data(Tags.MOVE_SRCFLDID, srcMailbox.mServerId);
-        s.data(Tags.MOVE_DSTFLDID, dstMailbox.mServerId);
-        s.end().end().done();
-        EasResponse resp = sendHttpClientPost("MoveItems", s.toByteArray());
-        try {
-            int status = resp.getStatus();
-            if (status == HttpStatus.SC_OK) {
-                if (!resp.isEmpty()) {
-                    InputStream is = resp.getInputStream();
-                    MoveItemsParser p = new MoveItemsParser(is, this);
-                    p.parse();
-                    int statusCode = p.getStatusCode();
-                    ContentValues cv = new ContentValues();
-                    if (statusCode == MoveItemsParser.STATUS_CODE_REVERT) {
-                        // Restore the old mailbox id
-                        cv.put(MessageColumns.MAILBOX_KEY, srcMailbox.mServerId);
-                        mContentResolver.update(
-                                ContentUris.withAppendedId(Message.CONTENT_URI, req.mMessageId),
-                                cv, null, null);
-                    } else if (statusCode == MoveItemsParser.STATUS_CODE_SUCCESS) {
-                        // Update with the new server id
-                        cv.put(SyncColumns.SERVER_ID, p.getNewServerId());
-                        cv.put(Message.FLAGS, msg.mFlags | MESSAGE_FLAG_MOVED_MESSAGE);
-                        mContentResolver.update(
-                                ContentUris.withAppendedId(Message.CONTENT_URI, req.mMessageId),
-                                cv, null, null);
-                    }
-                    if (statusCode == MoveItemsParser.STATUS_CODE_SUCCESS
-                            || statusCode == MoveItemsParser.STATUS_CODE_REVERT) {
-                        // If we revert or succeed, we no longer need the update information
-                        // OR the now-duplicate email (the new copy will be synced down)
-                        mContentResolver.delete(ContentUris.withAppendedId(
-                                Message.UPDATED_CONTENT_URI, req.mMessageId), null, null);
-                    } else {
-                        // In this case, we're retrying, so do nothing.  The request will be
-                        // handled next sync
-                    }
-                }
-            } else if (EasResponse.isAuthError(status)) {
-                throw new EasAuthenticationException();
-            } else {
-                userLog("Move items request failed, code: " + status);
-                throw new IOException();
-            }
-        } finally {
-            resp.close();
-        }
-    }
-
-    /**
-     * Responds to a meeting request.  The MeetingResponseRequest is basically our
-     * wrapper for the meetingResponse service call
-     * @param req the request (message id and response code)
-     * @throws IOException
-     */
-    protected void sendMeetingResponse(MeetingResponseRequest req) throws IOException {
-        // Retrieve the message and mailbox; punt if either are null
-        Message msg = Message.restoreMessageWithId(mContext, req.mMessageId);
-        if (msg == null) return;
-        Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, msg.mMailboxKey);
-        if (mailbox == null) return;
-        Serializer s = new Serializer();
-        s.start(Tags.MREQ_MEETING_RESPONSE).start(Tags.MREQ_REQUEST);
-        s.data(Tags.MREQ_USER_RESPONSE, Integer.toString(req.mResponse));
-        s.data(Tags.MREQ_COLLECTION_ID, mailbox.mServerId);
-        s.data(Tags.MREQ_REQ_ID, msg.mServerId);
-        s.end().end().done();
-        EasResponse resp = sendHttpClientPost("MeetingResponse", s.toByteArray());
-        try {
-            int status = resp.getStatus();
-            if (status == HttpStatus.SC_OK) {
-                if (!resp.isEmpty()) {
-                    InputStream is = resp.getInputStream();
-                    new MeetingResponseParser(is, this).parse();
-                    String meetingInfo = msg.mMeetingInfo;
-                    if (meetingInfo != null) {
-                        String responseRequested = new PackedString(meetingInfo).get(
-                                MeetingInfo.MEETING_RESPONSE_REQUESTED);
-                        // If there's no tag, or a non-zero tag, we send the response mail
-                        if ("0".equals(responseRequested)) {
-                            return;
-                        }
-                    }
-                    sendMeetingResponseMail(msg, req.mResponse);
-                }
-            } else if (EasResponse.isAuthError(status)) {
-                throw new EasAuthenticationException();
-            } else {
-                userLog("Meeting response request failed, code: " + status);
-                throw new IOException();
-            }
-        } finally {
-            resp.close();
-       }
-    }
-
-    /**
-     * Using mUserName and mPassword, lazily create the strings that are commonly used in our HTTP
-     * POSTs, including the authentication header string, the base URI we use to communicate with
-     * EAS, and the user information string (user, deviceId, and deviceType)
-     */
-    private void cacheAuthUserAndBaseUriStrings() {
-        if (mAuthString == null || mUserString == null || mBaseUriString == null) {
-            String safeUserName = Uri.encode(mUserName);
-            String cs = mUserName + ':' + mPassword;
-            mAuthString = "Basic " + Base64.encodeToString(cs.getBytes(), Base64.NO_WRAP);
-            mUserString = "&User=" + safeUserName + "&DeviceId=" + mDeviceId +
-                "&DeviceType=" + DEVICE_TYPE;
-            String scheme =
-                EmailClientConnectionManager.makeScheme(mSsl, mTrustSsl, mClientCertAlias);
-            mBaseUriString = scheme + "://" + mHostAddress + "/Microsoft-Server-ActiveSync";
-        }
-    }
-
-    @VisibleForTesting
-    String makeUriString(String cmd, String extra) {
-        cacheAuthUserAndBaseUriStrings();
-        String uriString = mBaseUriString;
-        if (cmd != null) {
-            uriString += "?Cmd=" + cmd + mUserString;
-        }
-        if (extra != null) {
-            uriString += extra;
-        }
-        return uriString;
-    }
-
-    /**
-     * Set standard HTTP headers, using a policy key if required
-     * @param method the method we are going to send
-     * @param usePolicyKey whether or not a policy key should be sent in the headers
-     */
-    /*package*/ void setHeaders(HttpRequestBase method, boolean usePolicyKey) {
-        method.setHeader("Authorization", mAuthString);
-        method.setHeader("MS-ASProtocolVersion", mProtocolVersion);
-        method.setHeader("User-Agent", USER_AGENT);
-        method.setHeader("Accept-Encoding", "gzip");
-        if (usePolicyKey) {
-            // If there's an account in existence, use its key; otherwise (we're creating the
-            // account), send "0".  The server will respond with code 449 if there are policies
-            // to be enforced
-            String key = "0";
-            if (mAccount != null) {
-                String accountKey = mAccount.mSecuritySyncKey;
-                if (!TextUtils.isEmpty(accountKey)) {
-                    key = accountKey;
-                }
-            }
-            method.setHeader("X-MS-PolicyKey", key);
-        }
-    }
-
-    protected void setConnectionParameters(
-            boolean useSsl, boolean trustAllServerCerts, String clientCertAlias)
-            throws CertificateException {
-
-        EmailClientConnectionManager connManager = getClientConnectionManager();
-
-        mSsl = useSsl;
-        mTrustSsl = trustAllServerCerts;
-        mClientCertAlias = clientCertAlias;
-
-        // Register the new alias, if needed.
-        if (mClientCertAlias != null) {
-            // Ensure that the connection manager knows to use the proper client certificate
-            // when establishing connections for this service.
-            connManager.registerClientCert(mContext, mClientCertAlias, mTrustSsl);
-        }
-    }
-
-    private EmailClientConnectionManager getClientConnectionManager() {
-        return ExchangeService.getClientConnectionManager();
-    }
-
-    private HttpClient getHttpClient(int timeout) {
-        HttpParams params = new BasicHttpParams();
-        HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
-        HttpConnectionParams.setSoTimeout(params, timeout);
-        HttpConnectionParams.setSocketBufferSize(params, 8192);
-        HttpClient client = new DefaultHttpClient(getClientConnectionManager(), params);
-        return client;
-    }
-
-    public EasResponse sendHttpClientPost(String cmd, byte[] bytes) throws IOException {
-        return sendHttpClientPost(cmd, new ByteArrayEntity(bytes), COMMAND_TIMEOUT);
-    }
-
-    protected EasResponse sendHttpClientPost(String cmd, HttpEntity entity) throws IOException {
-        return sendHttpClientPost(cmd, entity, COMMAND_TIMEOUT);
-    }
-
-    protected EasResponse sendPing(byte[] bytes, int heartbeat) throws IOException {
-       Thread.currentThread().setName(mAccount.mDisplayName + ": Ping");
-       if (Eas.USER_LOG) {
-           userLog("Send ping, timeout: " + heartbeat + "s, high: " + mPingHighWaterMark + 's');
-       }
-       return sendHttpClientPost(PING_COMMAND, new ByteArrayEntity(bytes), (heartbeat+5)*SECONDS);
-    }
-
-    /**
-     * Convenience method for executePostWithTimeout for use other than with the Ping command
-     */
-    protected EasResponse executePostWithTimeout(HttpClient client, HttpPost method, int timeout)
-            throws IOException {
-        return executePostWithTimeout(client, method, timeout, false);
-    }
-
-    /**
-     * Handle executing an HTTP POST command with proper timeout, watchdog, and ping behavior
-     * @param client the HttpClient
-     * @param method the HttpPost
-     * @param timeout the timeout before failure, in ms
-     * @param isPingCommand whether the POST is for the Ping command (requires wakelock logic)
-     * @return the HttpResponse
-     * @throws IOException
-     */
-    protected EasResponse executePostWithTimeout(HttpClient client, HttpPost method, int timeout,
-            boolean isPingCommand) throws IOException {
-        synchronized(getSynchronizer()) {
-            mPendingPost = method;
-            long alarmTime = timeout + WATCHDOG_TIMEOUT_ALLOWANCE;
-            if (isPingCommand) {
-                ExchangeService.runAsleep(mMailboxId, alarmTime);
-            } else {
-                ExchangeService.setWatchdogAlarm(mMailboxId, alarmTime);
-            }
-        }
-        try {
-            return EasResponse.fromHttpRequest(getClientConnectionManager(), client, method);
-        } finally {
-            synchronized(getSynchronizer()) {
-                if (isPingCommand) {
-                    ExchangeService.runAwake(mMailboxId);
-                } else {
-                    ExchangeService.clearWatchdogAlarm(mMailboxId);
-                }
-                mPendingPost = null;
-            }
-        }
-    }
-
-    public EasResponse sendHttpClientPost(String cmd, HttpEntity entity, int timeout)
-            throws IOException {
-        HttpClient client = getHttpClient(timeout);
-        boolean isPingCommand = cmd.equals(PING_COMMAND);
-
-        // Split the mail sending commands
-        String extra = null;
-        boolean msg = false;
-        if (cmd.startsWith("SmartForward&") || cmd.startsWith("SmartReply&")) {
-            int cmdLength = cmd.indexOf('&');
-            extra = cmd.substring(cmdLength);
-            cmd = cmd.substring(0, cmdLength);
-            msg = true;
-        } else if (cmd.startsWith("SendMail&")) {
-            msg = true;
-        }
-
-        String us = makeUriString(cmd, extra);
-        HttpPost method = new HttpPost(URI.create(us));
-        // Send the proper Content-Type header; it's always wbxml except for messages when
-        // the EAS protocol version is < 14.0
-        // If entity is null (e.g. for attachments), don't set this header
-        if (msg && (mProtocolVersionDouble < Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE)) {
-            method.setHeader("Content-Type", "message/rfc822");
-        } else if (entity != null) {
-            method.setHeader("Content-Type", "application/vnd.ms-sync.wbxml");
-        }
-        setHeaders(method, !isPingCommand);
-        // NOTE
-        // The next lines are added at the insistence of $VENDOR, who is seeing inappropriate
-        // network activity related to the Ping command on some networks with some servers.
-        // This code should be removed when the underlying issue is resolved
-        if (isPingCommand) {
-            method.setHeader("Connection", "close");
-        }
-        method.setEntity(entity);
-        return executePostWithTimeout(client, method, timeout, isPingCommand);
-    }
-
-    protected EasResponse sendHttpClientOptions() throws IOException {
-        cacheAuthUserAndBaseUriStrings();
-        // For OPTIONS, just use the base string and the single header
-        String uriString = mBaseUriString;
-        HttpOptions method = new HttpOptions(URI.create(uriString));
-        method.setHeader("Authorization", mAuthString);
-        method.setHeader("User-Agent", USER_AGENT);
-        HttpClient client = getHttpClient(COMMAND_TIMEOUT);
-        return EasResponse.fromHttpRequest(getClientConnectionManager(), client, method);
-    }
-
-    private String getTargetCollectionClassFromCursor(Cursor c) {
-        int type = c.getInt(Mailbox.CONTENT_TYPE_COLUMN);
-        if (type == Mailbox.TYPE_CONTACTS) {
-            return "Contacts";
-        } else if (type == Mailbox.TYPE_CALENDAR) {
-            return "Calendar";
-        } else {
-            return "Email";
-        }
-    }
-
-    /**
-     * Negotiate provisioning with the server.  First, get policies form the server and see if
-     * the policies are supported by the device.  Then, write the policies to the account and
-     * tell SecurityPolicy that we have policies in effect.  Finally, see if those policies are
-     * active; if so, acknowledge the policies to the server and get a final policy key that we
-     * use in future EAS commands and write this key to the account.
-     * @return whether or not provisioning has been successful
-     * @throws IOException
-     */
-    private boolean tryProvision() throws IOException {
-        // First, see if provisioning is even possible, i.e. do we support the policies required
-        // by the server
-        ProvisionParser pp = canProvision();
-        if (pp != null && pp.hasSupportablePolicySet()) {
-            // Get the policies from ProvisionParser
-            Policy policy = pp.getPolicy();
-            Policy oldPolicy = null;
-            // Grab the old policy (if any)
-            if (mAccount.mPolicyKey > 0) {
-                oldPolicy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey);
-            }
-            // Update the account with a null policyKey (the key we've gotten is
-            // temporary and cannot be used for syncing)
-            Policy.setAccountPolicy(mContext, mAccount, policy, null);
-            // Make sure mAccount is current (with latest policy key)
-            mAccount.refresh(mContext);
-            // Make sure that SecurityPolicy is up-to-date
-            SecurityPolicyDelegate.policiesUpdated(mContext, mAccount.mId);
-            if (pp.getRemoteWipe()) {
-                // We've gotten a remote wipe command
-                ExchangeService.alwaysLog("!!! Remote wipe request received");
-                // Start by setting the account to security hold
-                SecurityPolicyDelegate.setAccountHoldFlag(mContext, mAccount, true);
-                // Force a stop to any running syncs for this account (except this one)
-                ExchangeService.stopNonAccountMailboxSyncsForAccount(mAccount.mId);
-
-                // If we're not the admin, we can't do the wipe, so just return
-                if (!SecurityPolicyDelegate.isActiveAdmin(mContext)) {
-                    ExchangeService.alwaysLog("!!! Not device admin; can't wipe");
-                    return false;
-                }
-
-                // First, we've got to acknowledge it, but wrap the wipe in try/catch so that
-                // we wipe the device regardless of any errors in acknowledgment
-                try {
-                    ExchangeService.alwaysLog("!!! Acknowledging remote wipe to server");
-                    acknowledgeRemoteWipe(pp.getSecuritySyncKey());
-                } catch (Exception e) {
-                    // Because remote wipe is such a high priority task, we don't want to
-                    // circumvent it if there's an exception in acknowledgment
-                }
-                // Then, tell SecurityPolicy to wipe the device
-                ExchangeService.alwaysLog("!!! Executing remote wipe");
-                SecurityPolicyDelegate.remoteWipe(mContext);
-                return false;
-            } else if (SecurityPolicyDelegate.isActive(mContext, policy)) {
-                // See if the required policies are in force; if they are, acknowledge the policies
-                // to the server and get the final policy key
-                // NOTE: For EAS 14.0, we already have the acknowledgment in the ProvisionParser
-                String securitySyncKey;
-                if (mProtocolVersionDouble == Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE) {
-                    securitySyncKey = pp.getSecuritySyncKey();
-                } else {
-                    securitySyncKey = acknowledgeProvision(pp.getSecuritySyncKey(),
-                            PROVISION_STATUS_OK);
-                }
-                if (securitySyncKey != null) {
-                    // If attachment policies have changed, fix up any affected attachment records
-                    if (oldPolicy != null) {
-                        if ((oldPolicy.mDontAllowAttachments != policy.mDontAllowAttachments) ||
-                                (oldPolicy.mMaxAttachmentSize != policy.mMaxAttachmentSize)) {
-                            Policy.setAttachmentFlagsForNewPolicy(mContext, mAccount, policy);
-                        }
-                    }
-                    // Write the final policy key to the Account and say we've been successful
-                    Policy.setAccountPolicy(mContext, mAccount, policy, securitySyncKey);
-                    // Release any mailboxes that might be in a security hold
-                    ExchangeService.releaseSecurityHold(mAccount);
-                    return true;
-                }
-            } else {
-                // Notify that we are blocked because of policies
-                // TODO: Indicate unsupported policies here?
-                SecurityPolicyDelegate.policiesRequired(mContext, mAccount.mId);
-            }
-        }
-        return false;
-    }
-
-    private String getPolicyType() {
-        return (mProtocolVersionDouble >=
-            Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) ? EAS_12_POLICY_TYPE : EAS_2_POLICY_TYPE;
-    }
-
-    /**
-     * Obtain a set of policies from the server and determine whether those policies are supported
-     * by the device.
-     * @return the ProvisionParser (holds policies and key) if we receive policies; null otherwise
-     * @throws IOException
-     */
-    private ProvisionParser canProvision() throws IOException {
-        Serializer s = new Serializer();
-        s.start(Tags.PROVISION_PROVISION);
-        if (mProtocolVersionDouble >= Eas.SUPPORTED_PROTOCOL_EX2010_SP1_DOUBLE) {
-            // Send settings information in 14.1 and greater
-            s.start(Tags.SETTINGS_DEVICE_INFORMATION).start(Tags.SETTINGS_SET);
-            s.data(Tags.SETTINGS_MODEL, Build.MODEL);
-            //s.data(Tags.SETTINGS_IMEI, "");
-            //s.data(Tags.SETTINGS_FRIENDLY_NAME, "Friendly Name");
-            s.data(Tags.SETTINGS_OS, "Android " + Build.VERSION.RELEASE);
-            //s.data(Tags.SETTINGS_OS_LANGUAGE, "");
-            //s.data(Tags.SETTINGS_PHONE_NUMBER, "");
-            //s.data(Tags.SETTINGS_MOBILE_OPERATOR, "");
-            s.data(Tags.SETTINGS_USER_AGENT, USER_AGENT);
-            s.end().end();  // SETTINGS_SET, SETTINGS_DEVICE_INFORMATION
-        }
-        s.start(Tags.PROVISION_POLICIES);
-        s.start(Tags.PROVISION_POLICY).data(Tags.PROVISION_POLICY_TYPE, getPolicyType()).end();
-        s.end();  // PROVISION_POLICIES
-        s.end().done(); // PROVISION_PROVISION
-        EasResponse resp = sendHttpClientPost("Provision", s.toByteArray());
-        try {
-            int code = resp.getStatus();
-            if (code == HttpStatus.SC_OK) {
-                InputStream is = resp.getInputStream();
-                ProvisionParser pp = new ProvisionParser(is, this);
-                if (pp.parse()) {
-                    // The PolicySet in the ProvisionParser will have the requirements for all KNOWN
-                    // policies.  If others are required, hasSupportablePolicySet will be false
-                    if (pp.hasSupportablePolicySet() &&
-                            mProtocolVersionDouble == Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE) {
-                        // In EAS 14.0, we need the final security key in order to use the settings
-                        // command
-                        String policyKey = acknowledgeProvision(pp.getSecuritySyncKey(),
-                                PROVISION_STATUS_OK);
-                        if (policyKey != null) {
-                            pp.setSecuritySyncKey(policyKey);
-                        }
-                    } else if (!pp.hasSupportablePolicySet())  {
-                        // Try to acknowledge using the "partial" status (i.e. we can partially
-                        // accommodate the required policies).  The server will agree to this if the
-                        // "allow non-provisionable devices" setting is enabled on the server
-                        ExchangeService.log("PolicySet is NOT fully supportable");
-                        String policyKey = acknowledgeProvision(pp.getSecuritySyncKey(),
-                                PROVISION_STATUS_PARTIAL);
-                        // Return either the parser (success) or null (failure)
-                        if (policyKey != null) {
-                            pp.clearUnsupportedPolicies();
-                        }
-                    }
-                    return pp;
-                }
-            }
-        } finally {
-            resp.close();
-        }
-
-        // On failures, simply return null
-        return null;
-    }
-
-    /**
-     * Acknowledge that we support the policies provided by the server, and that these policies
-     * are in force.
-     * @param tempKey the initial (temporary) policy key sent by the server
-     * @return the final policy key, which can be used for syncing
-     * @throws IOException
-     */
-    private void acknowledgeRemoteWipe(String tempKey) throws IOException {
-        acknowledgeProvisionImpl(tempKey, PROVISION_STATUS_OK, true);
-    }
-
-    private String acknowledgeProvision(String tempKey, String result) throws IOException {
-        return acknowledgeProvisionImpl(tempKey, result, false);
-    }
-
-    private String acknowledgeProvisionImpl(String tempKey, String status,
-            boolean remoteWipe) throws IOException {
-        Serializer s = new Serializer();
-        s.start(Tags.PROVISION_PROVISION).start(Tags.PROVISION_POLICIES);
-        s.start(Tags.PROVISION_POLICY);
-
-        // Use the proper policy type, depending on EAS version
-        s.data(Tags.PROVISION_POLICY_TYPE, getPolicyType());
-
-        s.data(Tags.PROVISION_POLICY_KEY, tempKey);
-        s.data(Tags.PROVISION_STATUS, status);
-        s.end().end(); // PROVISION_POLICY, PROVISION_POLICIES
-        if (remoteWipe) {
-            s.start(Tags.PROVISION_REMOTE_WIPE);
-            s.data(Tags.PROVISION_STATUS, PROVISION_STATUS_OK);
-            s.end();
-        }
-        s.end().done(); // PROVISION_PROVISION
-        EasResponse resp = sendHttpClientPost("Provision", s.toByteArray());
-        try {
-            int code = resp.getStatus();
-            if (code == HttpStatus.SC_OK) {
-                InputStream is = resp.getInputStream();
-                ProvisionParser pp = new ProvisionParser(is, this);
-                if (pp.parse()) {
-                    // Return the final policy key from the ProvisionParser
-                    ExchangeService.log("Provision confirmation received for " +
-                            (PROVISION_STATUS_PARTIAL.equals(status) ? "PART" : "FULL") + " set");
-                    return pp.getSecuritySyncKey();
-                }
-            }
-        } finally {
-            resp.close();
-        }
-        // On failures, log issue and return null
-        ExchangeService.log("Provision confirmation failed for" +
-                (PROVISION_STATUS_PARTIAL.equals(status) ? "PART" : "FULL") + " set");
-        return null;
-    }
-
-    private boolean sendSettings() throws IOException {
-        Serializer s = new Serializer();
-        s.start(Tags.SETTINGS_SETTINGS);
-        s.start(Tags.SETTINGS_DEVICE_INFORMATION).start(Tags.SETTINGS_SET);
-        s.data(Tags.SETTINGS_MODEL, Build.MODEL);
-        s.data(Tags.SETTINGS_OS, "Android " + Build.VERSION.RELEASE);
-        s.data(Tags.SETTINGS_USER_AGENT, USER_AGENT);
-        s.end().end().end().done(); // SETTINGS_SET, SETTINGS_DEVICE_INFORMATION, SETTINGS_SETTINGS
-        EasResponse resp = sendHttpClientPost("Settings", s.toByteArray());
-        try {
-            int code = resp.getStatus();
-            if (code == HttpStatus.SC_OK) {
-                InputStream is = resp.getInputStream();
-                SettingsParser sp = new SettingsParser(is, this);
-                return sp.parse();
-            }
-        } finally {
-            resp.close();
-        }
-        // On failures, simply return false
-        return false;
-    }
-
-    /**
-     * Translate exit status code to service status code (used in callbacks)
-     * @param exitStatus the service's exit status
-     * @return the corresponding service status
-     */
-    private int exitStatusToServiceStatus(int exitStatus) {
-        switch(exitStatus) {
-            case EXIT_SECURITY_FAILURE:
-                return EmailServiceStatus.SECURITY_FAILURE;
-            case EXIT_LOGIN_FAILURE:
-                return EmailServiceStatus.LOGIN_FAILED;
-            default:
-                return EmailServiceStatus.SUCCESS;
-        }
-    }
-
-    /**
-     * If possible, update the account to the new server address; report result
-     * @param resp the EasResponse from the current POST
-     * @return whether or not the redirect is handled and the POST should be retried
-     */
-    private boolean canHandleAccountMailboxRedirect(EasResponse resp) {
-        userLog("AccountMailbox redirect error");
-        HostAuth ha =
-                HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeyRecv);
-        if (ha != null && getValidateRedirect(resp, ha)) {
-            // Update the account's HostAuth with new values
-            ContentValues haValues = new ContentValues();
-            haValues.put(HostAuthColumns.ADDRESS, ha.mAddress);
-            ha.update(mContext, haValues);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Performs FolderSync
-     *
-     * @throws IOException
-     * @throws EasParserException
-     */
-    public void runAccountMailbox() throws IOException, EasParserException {
-        // Check that the account's mailboxes are consistent
-        MailboxUtilities.checkMailboxConsistency(mContext, mAccount.mId);
-        // Initialize exit status to success
-        mExitStatus = EXIT_DONE;
-        try {
-            try {
-                ExchangeService.callback()
-                    .syncMailboxListStatus(mAccount.mId, EmailServiceStatus.IN_PROGRESS, 0);
-            } catch (RemoteException e1) {
-                // Don't care if this fails
-            }
-
-            if (mAccount.mSyncKey == null) {
-                mAccount.mSyncKey = "0";
-                userLog("Account syncKey INIT to 0");
-                ContentValues cv = new ContentValues();
-                cv.put(AccountColumns.SYNC_KEY, mAccount.mSyncKey);
-                mAccount.update(mContext, cv);
-            }
-
-            boolean firstSync = mAccount.mSyncKey.equals("0");
-            if (firstSync) {
-                userLog("Initial FolderSync");
-            }
-
-            // When we first start up, change all mailboxes to push.
-            ContentValues cv = new ContentValues();
-            cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PUSH);
-            if (mContentResolver.update(Mailbox.CONTENT_URI, cv,
-                    WHERE_ACCOUNT_AND_SYNC_INTERVAL_PING,
-                    new String[] {Long.toString(mAccount.mId)}) > 0) {
-                ExchangeService.kick("change ping boxes to push");
-            }
-
-            // Determine our protocol version, if we haven't already and save it in the Account
-            // Also re-check protocol version at least once a day (in case of upgrade)
-            if (mAccount.mProtocolVersion == null || firstSync ||
-                   ((System.currentTimeMillis() - mMailbox.mSyncTime) > DAYS)) {
-                userLog("Determine EAS protocol version");
-                EasResponse resp = sendHttpClientOptions();
-                try {
-                    int code = resp.getStatus();
-                    userLog("OPTIONS response: ", code);
-                    if (code == HttpStatus.SC_OK) {
-                        Header header = resp.getHeader("MS-ASProtocolCommands");
-                        userLog(header.getValue());
-                        header = resp.getHeader("ms-asprotocolversions");
-                        try {
-                            setupProtocolVersion(this, header);
-                        } catch (MessagingException e) {
-                            // Since we've already validated, this can't really happen
-                            // But if it does, we'll rethrow this...
-                            throw new IOException();
-                        }
-                        // Save the protocol version
-                        cv.clear();
-                        // Save the protocol version in the account; if we're using 12.0 or greater,
-                        // set the flag for support of SmartForward
-                        cv.put(Account.PROTOCOL_VERSION, mProtocolVersion);
-                        mAccount.update(mContext, cv);
-                        cv.clear();
-                        // Save the sync time of the account mailbox to current time
-                        cv.put(Mailbox.SYNC_TIME, System.currentTimeMillis());
-                        mMailbox.update(mContext, cv);
-                    } else if (code == EAS_REDIRECT_CODE && canHandleAccountMailboxRedirect(resp)) {
-                        // Cause this to re-run
-                        throw new IOException("Will retry after a brief hold...");
-                    } else {
-                        errorLog("OPTIONS command failed; throwing IOException");
-                        throw new IOException();
-                    }
-                } finally {
-                    resp.close();
-                }
-            }
-
-            // Make sure we've upgraded flags for ICS if we're using v12.0 or later
-            if (mProtocolVersionDouble >= 12.0 &&
-                    (mAccount.mFlags & Account.FLAGS_SUPPORTS_SEARCH) == 0) {
-                cv.clear();
-                mAccount.mFlags = mAccount.mFlags | Account.FLAGS_SUPPORTS_SMART_FORWARD |
-                        Account.FLAGS_SUPPORTS_SEARCH | Account.FLAGS_SUPPORTS_GLOBAL_SEARCH;
-                cv.put(AccountColumns.FLAGS, mAccount.mFlags);
-                mAccount.update(mContext, cv);
-            }
-
-            // Change all pushable boxes to push when we start the account mailbox
-            if (mAccount.mSyncInterval == Account.CHECK_INTERVAL_PUSH) {
-                cv.clear();
-                cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PUSH);
-                if (mContentResolver.update(Mailbox.CONTENT_URI, cv,
-                        ExchangeService.WHERE_IN_ACCOUNT_AND_PUSHABLE,
-                        new String[] {Long.toString(mAccount.mId)}) > 0) {
-                    userLog("Push account; set pushable boxes to push...");
-                }
-            }
-
-            while (!mStop) {
-                // If we're not allowed to sync (e.g. roaming policy), leave now
-                if (!ExchangeService.canAutoSync(mAccount)) return;
-                userLog("Sending Account syncKey: ", mAccount.mSyncKey);
-                Serializer s = new Serializer();
-                s.start(Tags.FOLDER_FOLDER_SYNC).start(Tags.FOLDER_SYNC_KEY)
-                    .text(mAccount.mSyncKey).end().end().done();
-                EasResponse resp = sendHttpClientPost("FolderSync", s.toByteArray());
-                try {
-                    if (mStop) break;
-                    int code = resp.getStatus();
-                    if (code == HttpStatus.SC_OK) {
-                        if (!resp.isEmpty()) {
-                            InputStream is = resp.getInputStream();
-                            // Returns true if we need to sync again
-                            if (new FolderSyncParser(is, new AccountSyncAdapter(this)).parse()) {
-                                continue;
-                            }
-                        }
-                    } else if (EasResponse.isProvisionError(code)) {
-                        userLog("FolderSync provisioning error: ", code);
-                        throw new CommandStatusException(CommandStatus.NEEDS_PROVISIONING);
-                    } else if (EasResponse.isAuthError(code)) {
-                        userLog("FolderSync auth error: ", code);
-                        mExitStatus = EXIT_LOGIN_FAILURE;
-                        return;
-                    } else if (code == EAS_REDIRECT_CODE && canHandleAccountMailboxRedirect(resp)) {
-                        // This will cause a retry of the FolderSync
-                        continue;
-                    } else {
-                        userLog("FolderSync response error: ", code);
-                    }
-                } finally {
-                    resp.close();
-                }
-
-                // Change all push/hold boxes to push
-                cv.clear();
-                cv.put(Mailbox.SYNC_INTERVAL, Account.CHECK_INTERVAL_PUSH);
-                if (mContentResolver.update(Mailbox.CONTENT_URI, cv,
-                        WHERE_PUSH_HOLD_NOT_ACCOUNT_MAILBOX,
-                        new String[] {Long.toString(mAccount.mId)}) > 0) {
-                    userLog("Set push/hold boxes to push...");
-                }
-
-                try {
-                    ExchangeService.callback()
-                        .syncMailboxListStatus(mAccount.mId, exitStatusToServiceStatus(mExitStatus),
-                                0);
-                } catch (RemoteException e1) {
-                    // Don't care if this fails
-                }
-
-                // Before each run of the pingLoop, if this Account has a PolicySet, make sure it's
-                // active; otherwise, clear out the key/flag.  This should cause a provisioning
-                // error on the next POST, and start the security sequence over again
-                String key = mAccount.mSecuritySyncKey;
-                if (!TextUtils.isEmpty(key)) {
-                    Policy policy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey);
-                    if ((policy != null) && !SecurityPolicyDelegate.isActive(mContext, policy)) {
-                        resetSecurityPolicies();
-                    }
-                }
-
-                // Wait for push notifications.
-                String threadName = Thread.currentThread().getName();
-                try {
-                    runPingLoop();
-                } catch (StaleFolderListException e) {
-                    // We break out if we get told about a stale folder list
-                    userLog("Ping interrupted; folder list requires sync...");
-                } catch (IllegalHeartbeatException e) {
-                    // If we're sending an illegal heartbeat, reset either the min or the max to
-                    // that heartbeat
-                    resetHeartbeats(e.mLegalHeartbeat);
-                } finally {
-                    Thread.currentThread().setName(threadName);
-                }
-            }
-        } catch (CommandStatusException e) {
-            // If the sync error is a provisioning failure (perhaps policies changed),
-            // let's try the provisioning procedure
-            // Provisioning must only be attempted for the account mailbox - trying to
-            // provision any other mailbox may result in race conditions and the
-            // creation of multiple policy keys.
-            int status = e.mStatus;
-            if (CommandStatus.isNeedsProvisioning(status)) {
-                if (!tryProvision()) {
-                    // Set the appropriate failure status
-                    mExitStatus = EXIT_SECURITY_FAILURE;
-                    return;
-                }
-            } else if (CommandStatus.isDeniedAccess(status)) {
-                mExitStatus = EXIT_ACCESS_DENIED;
-                try {
-                    ExchangeService.callback().syncMailboxListStatus(mAccount.mId,
-                            EmailServiceStatus.ACCESS_DENIED, 0);
-                } catch (RemoteException e1) {
-                    // Don't care if this fails
-                }
-                return;
-            } else {
-                userLog("Unexpected status: " + CommandStatus.toString(status));
-                mExitStatus = EXIT_EXCEPTION;
-            }
-        } catch (IOException e) {
-            // We catch this here to send the folder sync status callback
-            // A folder sync failed callback will get sent from run()
-            try {
-                if (!mStop) {
-                    // NOTE: The correct status is CONNECTION_ERROR, but the UI displays this, and
-                    // it's not really appropriate for EAS as this is not unexpected for a ping and
-                    // connection errors are retried in any case
-                    ExchangeService.callback()
-                        .syncMailboxListStatus(mAccount.mId, EmailServiceStatus.SUCCESS, 0);
-                }
-            } catch (RemoteException e1) {
-                // Don't care if this fails
-            }
-            throw e;
-        }
-    }
-
-    /**
-     * Reset either our minimum or maximum ping heartbeat to a heartbeat known to be legal
-     * @param legalHeartbeat a known legal heartbeat (from the EAS server)
-     */
-    /*package*/ void resetHeartbeats(int legalHeartbeat) {
-        userLog("Resetting min/max heartbeat, legal = " + legalHeartbeat);
-        // We are here because the current heartbeat (mPingHeartbeat) is invalid.  Depending on
-        // whether the argument is above or below the current heartbeat, we can infer the need to
-        // change either the minimum or maximum heartbeat
-        if (legalHeartbeat > mPingHeartbeat) {
-            // The legal heartbeat is higher than the ping heartbeat; therefore, our minimum was
-            // too low.  We respond by raising either or both of the minimum heartbeat or the
-            // force heartbeat to the argument value
-            if (mPingMinHeartbeat < legalHeartbeat) {
-                mPingMinHeartbeat = legalHeartbeat;
-            }
-            if (mPingForceHeartbeat < legalHeartbeat) {
-                mPingForceHeartbeat = legalHeartbeat;
-            }
-            // If our minimum is now greater than the max, bring them together
-            if (mPingMinHeartbeat > mPingMaxHeartbeat) {
-                mPingMaxHeartbeat = legalHeartbeat;
-            }
-        } else if (legalHeartbeat < mPingHeartbeat) {
-            // The legal heartbeat is lower than the ping heartbeat; therefore, our maximum was
-            // too high.  We respond by lowering the maximum to the argument value
-            mPingMaxHeartbeat = legalHeartbeat;
-            // If our maximum is now less than the minimum, bring them together
-            if (mPingMaxHeartbeat < mPingMinHeartbeat) {
-                mPingMinHeartbeat = legalHeartbeat;
-            }
-        }
-        // Set current heartbeat to the legal heartbeat
-        mPingHeartbeat = legalHeartbeat;
-        // Allow the heartbeat logic to run
-        mPingHeartbeatDropped = false;
-    }
-
-    private void pushFallback(long mailboxId) {
-        Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, mailboxId);
-        if (mailbox == null) {
-            return;
-        }
-        ContentValues cv = new ContentValues();
-        int mins = PING_FALLBACK_PIM;
-        if (mailbox.mType == Mailbox.TYPE_INBOX) {
-            mins = PING_FALLBACK_INBOX;
-        }
-        cv.put(Mailbox.SYNC_INTERVAL, mins);
-        mContentResolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId),
-                cv, null, null);
-        errorLog("*** PING ERROR LOOP: Set " + mailbox.mDisplayName + " to " + mins + " min sync");
-        ExchangeService.kick("push fallback");
-    }
-
-    /**
-     * Simplistic attempt to determine a NAT timeout, based on experience with various carriers
-     * and networks.  The string "reset by peer" is very common in these situations, so we look for
-     * that specifically.  We may add additional tests here as more is learned.
-     * @param message
-     * @return whether this message is likely associated with a NAT failure
-     */
-    private boolean isLikelyNatFailure(String message) {
-        if (message == null) return false;
-        if (message.contains("reset by peer")) {
-            return true;
-        }
-        return false;
-    }
-
-    private void runPingLoop() throws IOException, StaleFolderListException,
-            IllegalHeartbeatException, CommandStatusException {
-        int pingHeartbeat = mPingHeartbeat;
-        userLog("runPingLoop");
-        // Do push for all sync services here
-        long endTime = System.currentTimeMillis() + (30*MINUTES);
-        HashMap<String, Integer> pingErrorMap = new HashMap<String, Integer>();
-        ArrayList<String> readyMailboxes = new ArrayList<String>();
-        ArrayList<String> notReadyMailboxes = new ArrayList<String>();
-        int pingWaitCount = 0;
-        long inboxId = -1;
-
-        while ((System.currentTimeMillis() < endTime) && !mStop) {
-            // Count of pushable mailboxes
-            int pushCount = 0;
-            // Count of mailboxes that can be pushed right now
-            int canPushCount = 0;
-            // Count of uninitialized boxes
-            int uninitCount = 0;
-
-            Serializer s = new Serializer();
-            Cursor c = mContentResolver.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
-                    MailboxColumns.ACCOUNT_KEY + '=' + mAccount.mId +
-                    AND_FREQUENCY_PING_PUSH_AND_NOT_ACCOUNT_MAILBOX, null, null);
-            if (c == null) throw new ProviderUnavailableException();
-            notReadyMailboxes.clear();
-            readyMailboxes.clear();
-            // Look for an inbox, and remember its id
-            if (inboxId == -1) {
-                inboxId = Mailbox.findMailboxOfType(mContext, mAccount.mId, Mailbox.TYPE_INBOX);
-            }
-            try {
-                // Loop through our pushed boxes seeing what is available to push
-                while (c.moveToNext()) {
-                    pushCount++;
-                    // Two requirements for push:
-                    // 1) ExchangeService tells us the mailbox is syncable (not running/not stopped)
-                    // 2) The syncKey isn't "0" (i.e. it's synced at least once)
-                    long mailboxId = c.getLong(Mailbox.CONTENT_ID_COLUMN);
-                    int pingStatus = ExchangeService.pingStatus(mailboxId);
-                    String mailboxName = c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN);
-                    if (pingStatus == ExchangeService.PING_STATUS_OK) {
-                        String syncKey = c.getString(Mailbox.CONTENT_SYNC_KEY_COLUMN);
-                        if ((syncKey == null) || syncKey.equals("0")) {
-                            // We can't push until the initial sync is done
-                            pushCount--;
-                            uninitCount++;
-                            continue;
-                        }
-
-                        if (canPushCount++ == 0) {
-                            // Initialize the Ping command
-                            s.start(Tags.PING_PING)
-                                .data(Tags.PING_HEARTBEAT_INTERVAL,
-                                        Integer.toString(pingHeartbeat))
-                                .start(Tags.PING_FOLDERS);
-                        }
-
-                        String folderClass = getTargetCollectionClassFromCursor(c);
-                        s.start(Tags.PING_FOLDER)
-                            .data(Tags.PING_ID, c.getString(Mailbox.CONTENT_SERVER_ID_COLUMN))
-                            .data(Tags.PING_CLASS, folderClass)
-                            .end();
-                        readyMailboxes.add(mailboxName);
-                    } else if ((pingStatus == ExchangeService.PING_STATUS_RUNNING) ||
-                            (pingStatus == ExchangeService.PING_STATUS_WAITING)) {
-                        notReadyMailboxes.add(mailboxName);
-                    } else if (pingStatus == ExchangeService.PING_STATUS_UNABLE) {
-                        pushCount--;
-                        userLog(mailboxName, " in error state; ignore");
-                        continue;
-                    }
-                }
-            } finally {
-                c.close();
-            }
-
-            if (Eas.USER_LOG) {
-                if (!notReadyMailboxes.isEmpty()) {
-                    userLog("Ping not ready for: " + notReadyMailboxes);
-                }
-                if (!readyMailboxes.isEmpty()) {
-                    userLog("Ping ready for: " + readyMailboxes);
-                }
-            }
-
-            // If we've waited 10 seconds or more, just ping with whatever boxes are ready
-            // But use a shorter than normal heartbeat
-            boolean forcePing = !notReadyMailboxes.isEmpty() && (pingWaitCount > 5);
-
-            if ((canPushCount > 0) && ((canPushCount == pushCount) || forcePing)) {
-                // If all pingable boxes are ready for push, send Ping to the server
-                s.end().end().done();
-                pingWaitCount = 0;
-                mPostReset = false;
-                mPostAborted = false;
-
-                // If we've been stopped, this is a good time to return
-                if (mStop) return;
-
-                long pingTime = SystemClock.elapsedRealtime();
-                try {
-                    // Send the ping, wrapped by appropriate timeout/alarm
-                    if (forcePing) {
-                        userLog("Forcing ping after waiting for all boxes to be ready");
-                    }
-                    EasResponse resp =
-                        sendPing(s.toByteArray(), forcePing ? mPingForceHeartbeat : pingHeartbeat);
-
-                    try {
-                        int code = resp.getStatus();
-                        userLog("Ping response: ", code);
-
-                        // If we're not allowed to sync (e.g. roaming policy), terminate gracefully
-                        // now; otherwise we might start a sync based on the response
-                        if (!ExchangeService.canAutoSync(mAccount)) {
-                            mStop = true;
-                        }
-
-                        // Return immediately if we've been asked to stop during the ping
-                        if (mStop) {
-                            userLog("Stopping pingLoop");
-                            return;
-                        }
-
-                        if (code == HttpStatus.SC_OK) {
-                            // Make sure to clear out any pending sync errors
-                            ExchangeService.removeFromSyncErrorMap(mMailboxId);
-                            if (!resp.isEmpty()) {
-                                InputStream is = resp.getInputStream();
-                                int pingResult = parsePingResult(is, mContentResolver,
-                                        pingErrorMap);
-                                // If our ping completed (status = 1), and wasn't forced and we're
-                                // not at the maximum, try increasing timeout by two minutes
-                                if (pingResult == PROTOCOL_PING_STATUS_COMPLETED && !forcePing) {
-                                    if (pingHeartbeat > mPingHighWaterMark) {
-                                        mPingHighWaterMark = pingHeartbeat;
-                                        userLog("Setting high water mark at: ", mPingHighWaterMark);
-                                    }
-                                    if ((pingHeartbeat < mPingMaxHeartbeat) &&
-                                            !mPingHeartbeatDropped) {
-                                        pingHeartbeat += PING_HEARTBEAT_INCREMENT;
-                                        if (pingHeartbeat > mPingMaxHeartbeat) {
-                                            pingHeartbeat = mPingMaxHeartbeat;
-                                        }
-                                        userLog("Increase ping heartbeat to ", pingHeartbeat, "s");
-                                    }
-                                }
-                            } else {
-                                userLog("Ping returned empty result; throwing IOException");
-                                throw new IOException();
-                            }
-                        } else if (EasResponse.isAuthError(code)) {
-                            mExitStatus = EXIT_LOGIN_FAILURE;
-                            userLog("Authorization error during Ping: ", code);
-                            throw new IOException();
-                        } else if (EasResponse.isProvisionError(code)) {
-                            userLog("Provisioning required during Ping: ", code);
-                            throw new CommandStatusException(CommandStatus.NEEDS_PROVISIONING);
-                        }
-                    } finally {
-                        resp.close();
-                    }
-                } catch (IOException e) {
-                    String message = e.getMessage();
-                    // If we get the exception that is indicative of a NAT timeout and if we
-                    // haven't yet "fixed" the timeout, back off by two minutes and "fix" it
-                    boolean hasMessage = message != null;
-                    userLog("IOException runPingLoop: " + (hasMessage ? message : "[no message]"));
-                    if (mPostReset) {
-                        // Nothing to do in this case; this is ExchangeService telling us to try
-                        // another ping.
-                    } else if (mPostAborted || isLikelyNatFailure(message)) {
-                        long pingLength = SystemClock.elapsedRealtime() - pingTime;
-                        if ((pingHeartbeat > mPingMinHeartbeat) &&
-                                (pingHeartbeat > mPingHighWaterMark)) {
-                            pingHeartbeat -= PING_HEARTBEAT_INCREMENT;
-                            mPingHeartbeatDropped = true;
-                            if (pingHeartbeat < mPingMinHeartbeat) {
-                                pingHeartbeat = mPingMinHeartbeat;
-                            }
-                            userLog("Decreased ping heartbeat to ", pingHeartbeat, "s");
-                        } else if (mPostAborted) {
-                            // There's no point in throwing here; this can happen in two cases
-                            // 1) An alarm, which indicates minutes without activity; no sense
-                            //    backing off
-                            // 2) ExchangeService abort, due to sync of mailbox.  Again, we want to
-                            //    keep on trying to ping
-                            userLog("Ping aborted; retry");
-                        } else if (pingLength < 2000) {
-                            userLog("Abort or NAT type return < 2 seconds; throwing IOException");
-                            throw e;
-                        } else {
-                            userLog("NAT type IOException");
-                        }
-                    } else if (hasMessage && message.contains("roken pipe")) {
-                        // The "broken pipe" error (uppercase or lowercase "b") seems to be an
-                        // internal error, so let's not throw an exception (which leads to delays)
-                        // but rather simply run through the loop again
-                    } else {
-                        throw e;
-                    }
-                }
-            } else if (forcePing) {
-                // In this case, there aren't any boxes that are pingable, but there are boxes
-                // waiting (for IOExceptions)
-                userLog("pingLoop waiting 60s for any pingable boxes");
-                sleep(60*SECONDS, true);
-            } else if (pushCount > 0) {
-                // If we want to Ping, but can't just yet, wait a little bit
-                // TODO Change sleep to wait and use notify from ExchangeService when a sync ends
-                sleep(2*SECONDS, false);
-                pingWaitCount++;
-                //userLog("pingLoop waited 2s for: ", (pushCount - canPushCount), " box(es)");
-            } else if (uninitCount > 0) {
-                // In this case, we're doing an initial sync of at least one mailbox.  Since this
-                // is typically a one-time case, I'm ok with trying again every 10 seconds until
-                // we're in one of the other possible states.
-                userLog("pingLoop waiting for initial sync of ", uninitCount, " box(es)");
-                sleep(10*SECONDS, true);
-            } else if (inboxId == -1) {
-                // In this case, we're still syncing mailboxes, so sleep for only a short time
-                sleep(45*SECONDS, true);
-            } else {
-                // We've got nothing to do, so we'll check again in 20 minutes at which time
-                // we'll update the folder list, check for policy changes and/or remote wipe, etc.
-                // Let the device sleep in the meantime...
-                userLog(ACCOUNT_MAILBOX_SLEEP_TEXT);
-                sleep(ACCOUNT_MAILBOX_SLEEP_TIME, true);
-            }
-        }
-
-        // Save away the current heartbeat
-        mPingHeartbeat = pingHeartbeat;
-    }
-
-    private void sleep(long ms, boolean runAsleep) {
-        if (runAsleep) {
-            ExchangeService.runAsleep(mMailboxId, ms+(5*SECONDS));
-        }
-        try {
-            Thread.sleep(ms);
-        } catch (InterruptedException e) {
-            // Doesn't matter whether we stop early; it's the thought that counts
-        } finally {
-            if (runAsleep) {
-                ExchangeService.runAwake(mMailboxId);
-            }
-        }
-    }
-
-    private int parsePingResult(InputStream is, ContentResolver cr,
-            HashMap<String, Integer> errorMap)
-            throws IOException, StaleFolderListException, IllegalHeartbeatException,
-                CommandStatusException {
-        PingParser pp = new PingParser(is, this);
-        if (pp.parse()) {
-            // True indicates some mailboxes need syncing...
-            // syncList has the serverId's of the mailboxes...
-            mBindArguments[0] = Long.toString(mAccount.mId);
-            mPingChangeList = pp.getSyncList();
-            for (String serverId: mPingChangeList) {
-                mBindArguments[1] = serverId;
-                Cursor c = cr.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
-                        WHERE_ACCOUNT_KEY_AND_SERVER_ID, mBindArguments, null);
-                if (c == null) throw new ProviderUnavailableException();
-                try {
-                    if (c.moveToFirst()) {
-
-                        /**
-                         * Check the boxes reporting changes to see if there really were any...
-                         * We do this because bugs in various Exchange servers can put us into a
-                         * looping behavior by continually reporting changes in a mailbox, even when
-                         * there aren't any.
-                         *
-                         * This behavior is seemingly random, and therefore we must code defensively
-                         * by backing off of push behavior when it is detected.
-                         *
-                         * One known cause, on certain Exchange 2003 servers, is acknowledged by
-                         * Microsoft, and the server hotfix for this case can be found at
-                         * http://support.microsoft.com/kb/923282
-                         */
-
-                        // Check the status of the last sync
-                        String status = c.getString(Mailbox.CONTENT_SYNC_STATUS_COLUMN);
-                        int type = ExchangeService.getStatusType(status);
-                        // This check should always be true...
-                        if (type == ExchangeService.SYNC_PING) {
-                            int changeCount = ExchangeService.getStatusChangeCount(status);
-                            if (changeCount > 0) {
-                                errorMap.remove(serverId);
-                            } else if (changeCount == 0) {
-                                // This means that a ping reported changes in error; we keep a count
-                                // of consecutive errors of this kind
-                                String name = c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN);
-                                Integer failures = errorMap.get(serverId);
-                                if (failures == null) {
-                                    userLog("Last ping reported changes in error for: ", name);
-                                    errorMap.put(serverId, 1);
-                                } else if (failures > MAX_PING_FAILURES) {
-                                    // We'll back off of push for this box
-                                    pushFallback(c.getLong(Mailbox.CONTENT_ID_COLUMN));
-                                    continue;
-                                } else {
-                                    userLog("Last ping reported changes in error for: ", name);
-                                    errorMap.put(serverId, failures + 1);
-                                }
-                            }
-                        }
-
-                        // If there were no problems with previous sync, we'll start another one
-                        ExchangeService.startManualSync(c.getLong(Mailbox.CONTENT_ID_COLUMN),
-                                ExchangeService.SYNC_PING, null);
-                    }
-                } finally {
-                    c.close();
-                }
-            }
-        }
-        return pp.getSyncStatus();
-    }
-
-    /**
-     * Common code to sync E+PIM data
-     *
-     * @param target an EasMailbox, EasContacts, or EasCalendar object
-     */
-    public void sync(AbstractSyncAdapter target) throws IOException {
-        Mailbox mailbox = target.mMailbox;
-
-        boolean moreAvailable = true;
-        int loopingCount = 0;
-        while (!mStop && (moreAvailable || hasPendingRequests())) {
-            // If we have no connectivity, just exit cleanly. ExchangeService will start us up again
-            // when connectivity has returned
-            if (!hasConnectivity()) {
-                userLog("No connectivity in sync; finishing sync");
-                mExitStatus = EXIT_DONE;
-                return;
-            }
-
-            // Every time through the loop we check to see if we're still syncable
-            if (!target.isSyncable()) {
-                mExitStatus = EXIT_DONE;
-                return;
-            }
-
-            // Now, handle various requests
-            while (true) {
-                Request req = null;
-
-                if (mRequestQueue.isEmpty()) {
-                    break;
-                } else {
-                    req = mRequestQueue.peek();
-                }
-
-                // Our two request types are PartRequest (loading attachment) and
-                // MeetingResponseRequest (respond to a meeting request)
-                if (req instanceof PartRequest) {
-                    TrafficStats.setThreadStatsTag(
-                            TrafficFlags.getAttachmentFlags(mContext, mAccount));
-                    new AttachmentLoader(this, (PartRequest)req).loadAttachment();
-                    TrafficStats.setThreadStatsTag(TrafficFlags.getSyncFlags(mContext, mAccount));
-                } else if (req instanceof MeetingResponseRequest) {
-                    sendMeetingResponse((MeetingResponseRequest)req);
-                } else if (req instanceof MessageMoveRequest) {
-                    messageMoveRequest((MessageMoveRequest)req);
-                }
-
-                // If there's an exception handling the request, we'll throw it
-                // Otherwise, we remove the request
-                mRequestQueue.remove();
-            }
-
-            // Don't sync if we've got nothing to do
-            if (!moreAvailable) {
-                continue;
-            }
-
-            Serializer s = new Serializer();
-
-            String className = target.getCollectionName();
-            String syncKey = target.getSyncKey();
-            userLog("sync, sending ", className, " syncKey: ", syncKey);
-            s.start(Tags.SYNC_SYNC)
-                .start(Tags.SYNC_COLLECTIONS)
-                .start(Tags.SYNC_COLLECTION);
-            // The "Class" element is removed in EAS 12.1 and later versions
-            if (mProtocolVersionDouble < Eas.SUPPORTED_PROTOCOL_EX2007_SP1_DOUBLE) {
-                s.data(Tags.SYNC_CLASS, className);
-            }
-            s.data(Tags.SYNC_SYNC_KEY, syncKey)
-                .data(Tags.SYNC_COLLECTION_ID, mailbox.mServerId);
-
-            // Start with the default timeout
-            int timeout = COMMAND_TIMEOUT;
-            if (!syncKey.equals("0")) {
-                // EAS doesn't allow GetChanges in an initial sync; sending other options
-                // appears to cause the server to delay its response in some cases, and this delay
-                // can be long enough to result in an IOException and total failure to sync.
-                // Therefore, we don't send any options with the initial sync.
-                // Set the truncation amount, body preference, lookback, etc.
-                target.sendSyncOptions(mProtocolVersionDouble, s);
-            } else {
-                // Use enormous timeout for initial sync, which empirically can take a while longer
-                timeout = 120*SECONDS;
-            }
-            // Send our changes up to the server
-            if (mUpsyncFailed) {
-                if (Eas.USER_LOG) {
-                    Log.d(TAG, "Inhibiting upsync this cycle");
-                }
-            } else {
-                target.sendLocalChanges(s);
-            }
-
-            s.end().end().end().done();
-            EasResponse resp = sendHttpClientPost("Sync", new ByteArrayEntity(s.toByteArray()),
-                    timeout);
-            try {
-                int code = resp.getStatus();
-                if (code == HttpStatus.SC_OK) {
-                    // In EAS 12.1, we can get "empty" sync responses, which indicate that there are
-                    // no changes in the mailbox; handle that case here
-                    // There are two cases here; if we get back a compressed stream (GZIP), we won't
-                    // know until we try to parse it (and generate an EmptyStreamException). If we
-                    // get uncompressed data, the response will be empty (i.e. have zero length)
-                    boolean emptyStream = false;
-                    if (!resp.isEmpty()) {
-                        InputStream is = resp.getInputStream();
-                        try {
-                            moreAvailable = target.parse(is);
-                            // If we inhibited upsync, we need yet another sync
-                            if (mUpsyncFailed) {
-                                moreAvailable = true;
-                            }
-
-                            if (target.isLooping()) {
-                                loopingCount++;
-                                userLog("** Looping: " + loopingCount);
-                                // After the maximum number of loops, we'll set moreAvailable to
-                                // false and allow the sync loop to terminate
-                                if (moreAvailable && (loopingCount > MAX_LOOPING_COUNT)) {
-                                    userLog("** Looping force stopped");
-                                    moreAvailable = false;
-                                }
-                            } else {
-                                loopingCount = 0;
-                            }
-
-                            // Cleanup clears out the updated/deleted tables, and we don't want to
-                            // do that if our upsync failed; clear the flag otherwise
-                            if (!mUpsyncFailed) {
-                                target.cleanup();
-                            } else {
-                                mUpsyncFailed = false;
-                            }
-                        } catch (EmptyStreamException e) {
-                            userLog("Empty stream detected in GZIP response");
-                            emptyStream = true;
-                        } catch (CommandStatusException e) {
-                            // TODO 14.1
-                            int status = e.mStatus;
-                            if (CommandStatus.isNeedsProvisioning(status)) {
-                                mExitStatus = EXIT_SECURITY_FAILURE;
-                            } else if (CommandStatus.isDeniedAccess(status)) {
-                                mExitStatus = EXIT_ACCESS_DENIED;
-                            } else if (CommandStatus.isTransientError(status)) {
-                                mExitStatus = EXIT_IO_ERROR;
-                            } else {
-                                mExitStatus = EXIT_EXCEPTION;
-                            }
-                            return;
-                        }
-                    } else {
-                        emptyStream = true;
-                    }
-
-                    if (emptyStream) {
-                        // If this happens, exit cleanly, and change the interval from push to ping
-                        // if necessary
-                        userLog("Empty sync response; finishing");
-                        if (mMailbox.mSyncInterval == Mailbox.CHECK_INTERVAL_PUSH) {
-                            userLog("Changing mailbox from push to ping");
-                            ContentValues cv = new ContentValues();
-                            cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PING);
-                            mContentResolver.update(
-                                    ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailbox.mId),
-                                    cv, null, null);
-                        }
-                        if (mRequestQueue.isEmpty()) {
-                            mExitStatus = EXIT_DONE;
-                            return;
-                        } else {
-                            continue;
-                        }
-                    }
-                } else {
-                    userLog("Sync response error: ", code);
-                    if (EasResponse.isProvisionError(code)) {
-                        mExitStatus = EXIT_SECURITY_FAILURE;
-                    } else if (EasResponse.isAuthError(code)) {
-                        mExitStatus = EXIT_LOGIN_FAILURE;
-                    } else {
-                        mExitStatus = EXIT_IO_ERROR;
-                    }
-                    return;
-                }
-            } finally {
-                resp.close();
-            }
-        }
-        mExitStatus = EXIT_DONE;
-    }
-
-    protected boolean setupService() {
-        synchronized(getSynchronizer()) {
-            mThread = Thread.currentThread();
-            android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
-            TAG = mThread.getName();
-        }
-        // Make sure account and mailbox are always the latest from the database
-        mAccount = Account.restoreAccountWithId(mContext, mAccount.mId);
-        if (mAccount == null) return false;
-        mMailbox = Mailbox.restoreMailboxWithId(mContext, mMailbox.mId);
-        if (mMailbox == null) return false;
-        HostAuth ha = HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeyRecv);
-        if (ha == null) return false;
-        mHostAddress = ha.mAddress;
-        mUserName = ha.mLogin;
-        mPassword = ha.mPassword;
-
-        try {
-            setConnectionParameters(
-                    (ha.mFlags & HostAuth.FLAG_SSL) != 0,
-                    (ha.mFlags & HostAuth.FLAG_TRUST_ALL) != 0,
-                    ha.mClientCertAlias);
-        } catch (CertificateException e) {
-            userLog("Couldn't retrieve certificate for connection");
-            try {
-                ExchangeService.callback().syncMailboxStatus(mMailboxId,
-                        EmailServiceStatus.CLIENT_CERTIFICATE_ERROR, 0);
-            } catch (RemoteException e1) {
-                // Don't care if this fails.
-            }
-            return false;
-        }
-
-        // Set up our protocol version from the Account
-        mProtocolVersion = mAccount.mProtocolVersion;
-        // If it hasn't been set up, start with default version
-        if (mProtocolVersion == null) {
-            mProtocolVersion = Eas.DEFAULT_PROTOCOL_VERSION;
-        }
-        mProtocolVersionDouble = Eas.getProtocolVersionDouble(mProtocolVersion);
-
-        // Do checks to address historical policy sets.
-        Policy policy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey);
-        if ((policy != null) && policy.mRequireEncryptionExternal) {
-            // External storage encryption is not supported at this time. In a previous release,
-            // prior to the system supporting true removable storage on Honeycomb, we accepted
-            // this since we emulated external storage on partitions that could be encrypted.
-            // If that was set before, we must clear it out now that the system supports true
-            // removable storage (which can't be encrypted).
-            resetSecurityPolicies();
-        }
-        return true;
-    }
-
-    /**
-     * Clears out the security policies associated with the account, forcing a provision error
-     * and a re-sync of the policy information for the account.
-     */
-    private void resetSecurityPolicies() {
-        ContentValues cv = new ContentValues();
-        cv.put(AccountColumns.SECURITY_FLAGS, 0);
-        cv.putNull(AccountColumns.SECURITY_SYNC_KEY);
-        long accountId = mAccount.mId;
-        mContentResolver.update(ContentUris.withAppendedId(
-                Account.CONTENT_URI, accountId), cv, null, null);
-        SecurityPolicyDelegate.policiesRequired(mContext, accountId);
-    }
-
-    @Override
-    public void run() {
-        try {
-            // Make sure account and mailbox are still valid
-            if (!setupService()) return;
-            // If we've been stopped, we're done
-            if (mStop) return;
-
-            // Whether or not we're the account mailbox
-            try {
-                mDeviceId = ExchangeService.getDeviceId(mContext);
-                int trafficFlags = TrafficFlags.getSyncFlags(mContext, mAccount);
-                if ((mMailbox == null) || (mAccount == null)) {
-                    return;
-                } else if (mMailbox.mType == Mailbox.TYPE_EAS_ACCOUNT_MAILBOX) {
-                    TrafficStats.setThreadStatsTag(trafficFlags | TrafficFlags.DATA_EMAIL);
-                    runAccountMailbox();
-                } else {
-                    AbstractSyncAdapter target;
-                    if (mMailbox.mType == Mailbox.TYPE_CONTACTS) {
-                        TrafficStats.setThreadStatsTag(trafficFlags | TrafficFlags.DATA_CONTACTS);
-                        target = new ContactsSyncAdapter( this);
-                    } else if (mMailbox.mType == Mailbox.TYPE_CALENDAR) {
-                        TrafficStats.setThreadStatsTag(trafficFlags | TrafficFlags.DATA_CALENDAR);
-                        target = new CalendarSyncAdapter(this);
-                    } else {
-                        TrafficStats.setThreadStatsTag(trafficFlags | TrafficFlags.DATA_EMAIL);
-                        target = new EmailSyncAdapter(this);
-                    }
-                    // We loop because someone might have put a request in while we were syncing
-                    // and we've missed that opportunity...
-                    do {
-                        if (mRequestTime != 0) {
-                            userLog("Looping for user request...");
-                            mRequestTime = 0;
-                        }
-                        String syncKey = target.getSyncKey();
-                        if (mSyncReason >= ExchangeService.SYNC_CALLBACK_START ||
-                                "0".equals(syncKey)) {
-                            try {
-                                ExchangeService.callback().syncMailboxStatus(mMailboxId,
-                                        EmailServiceStatus.IN_PROGRESS, 0);
-                            } catch (RemoteException e1) {
-                                // Don't care if this fails
-                            }
-                        }
-                        sync(target);
-                    } while (mRequestTime != 0);
-                }
-            } catch (EasAuthenticationException e) {
-                userLog("Caught authentication error");
-                mExitStatus = EXIT_LOGIN_FAILURE;
-            } catch (IOException e) {
-                String message = e.getMessage();
-                userLog("Caught IOException: ", (message == null) ? "No message" : message);
-                mExitStatus = EXIT_IO_ERROR;
-            } catch (Exception e) {
-                userLog("Uncaught exception in EasSyncService", e);
-            } finally {
-                int status;
-                ExchangeService.done(this);
-                if (!mStop) {
-                    userLog("Sync finished");
-                    switch (mExitStatus) {
-                        case EXIT_IO_ERROR:
-                            status = EmailServiceStatus.CONNECTION_ERROR;
-                            break;
-                        case EXIT_DONE:
-                            status = EmailServiceStatus.SUCCESS;
-                            ContentValues cv = new ContentValues();
-                            cv.put(Mailbox.SYNC_TIME, System.currentTimeMillis());
-                            String s = "S" + mSyncReason + ':' + status + ':' + mChangeCount;
-                            cv.put(Mailbox.SYNC_STATUS, s);
-                            mContentResolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI,
-                                    mMailboxId), cv, null, null);
-                            break;
-                        case EXIT_LOGIN_FAILURE:
-                            status = EmailServiceStatus.LOGIN_FAILED;
-                            break;
-                        case EXIT_SECURITY_FAILURE:
-                            status = EmailServiceStatus.SECURITY_FAILURE;
-                            // Ask for a new folder list. This should wake up the account mailbox; a
-                            // security error in account mailbox should start provisioning
-                            ExchangeService.reloadFolderList(mContext, mAccount.mId, true);
-                            break;
-                        case EXIT_ACCESS_DENIED:
-                            status = EmailServiceStatus.ACCESS_DENIED;
-                            break;
-                        default:
-                            status = EmailServiceStatus.REMOTE_EXCEPTION;
-                            errorLog("Sync ended due to an exception.");
-                            break;
-                    }
-                } else {
-                    userLog("Stopped sync finished.");
-                    status = EmailServiceStatus.SUCCESS;
-                }
-
-                // Send a callback (doesn't matter how the sync was started)
-                try {
-                    // Unless the user specifically asked for a sync, we don't want to report
-                    // connection issues, as they are likely to be transient.  In this case, we
-                    // simply report success, so that the progress indicator terminates without
-                    // putting up an error banner
-                    if (mSyncReason != ExchangeService.SYNC_UI_REQUEST &&
-                            status == EmailServiceStatus.CONNECTION_ERROR) {
-                        status = EmailServiceStatus.SUCCESS;
-                    }
-                    ExchangeService.callback().syncMailboxStatus(mMailboxId, status, 0);
-                } catch (RemoteException e1) {
-                    // Don't care if this fails
-                }
-
-                // Make sure ExchangeService knows about this
-                ExchangeService.kick("sync finished");
-            }
-        } catch (ProviderUnavailableException e) {
-            Log.e(TAG, "EmailProvider unavailable; sync ended prematurely");
-        }
-    }
-}
diff --git a/src/com/android/exchange/EmailSyncAdapterService.java b/src/com/android/exchange/EmailSyncAdapterService.java
deleted file mode 100644
index f91ee13..0000000
--- a/src/com/android/exchange/EmailSyncAdapterService.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-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 android.accounts.Account;
-import android.accounts.OperationCanceledException;
-import android.app.Service;
-import android.content.AbstractThreadedSyncAdapter;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SyncResult;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.util.Log;
-
-public class EmailSyncAdapterService extends Service {
-    private static final String TAG = "EAS EmailSyncAdapterService";
-    private static SyncAdapterImpl sSyncAdapter = null;
-    private static final Object sSyncAdapterLock = new Object();
-
-    private static final String[] ID_PROJECTION = new String[] {EmailContent.RECORD_ID};
-    private static final String ACCOUNT_AND_TYPE_INBOX =
-        MailboxColumns.ACCOUNT_KEY + "=? AND " + MailboxColumns.TYPE + '=' + Mailbox.TYPE_INBOX;
-
-    public EmailSyncAdapterService() {
-        super();
-    }
-
-    private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
-        private Context mContext;
-
-        public SyncAdapterImpl(Context context) {
-            super(context, true /* autoInitialize */);
-            mContext = context;
-        }
-
-        @Override
-        public void onPerformSync(Account account, Bundle extras,
-                String authority, ContentProviderClient provider, SyncResult syncResult) {
-            try {
-                EmailSyncAdapterService.performSync(mContext, account, extras,
-                        authority, provider, syncResult);
-            } catch (OperationCanceledException e) {
-            }
-        }
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        synchronized (sSyncAdapterLock) {
-            if (sSyncAdapter == null) {
-                sSyncAdapter = new SyncAdapterImpl(getApplicationContext());
-            }
-        }
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return sSyncAdapter.getSyncAdapterBinder();
-    }
-
-    /**
-     * Partial integration with system SyncManager; we tell our EAS ExchangeService to start an
-     * inbox sync when we get the signal from the system SyncManager.
-     */
-    private static void performSync(Context context, Account account, Bundle extras,
-            String authority, ContentProviderClient provider, SyncResult syncResult)
-            throws OperationCanceledException {
-        ContentResolver cr = context.getContentResolver();
-        Log.i(TAG, "performSync");
-
-        // Find the (EmailProvider) account associated with this email address
-        Cursor accountCursor =
-            cr.query(com.android.emailcommon.provider.Account.CONTENT_URI,
-                    ID_PROJECTION, AccountColumns.EMAIL_ADDRESS + "=?", new String[] {account.name},
-                    null);
-        try {
-            if (accountCursor.moveToFirst()) {
-                long accountId = accountCursor.getLong(0);
-                // Now, find the inbox associated with the account
-                Cursor mailboxCursor = cr.query(Mailbox.CONTENT_URI, ID_PROJECTION,
-                        ACCOUNT_AND_TYPE_INBOX, new String[] {Long.toString(accountId)}, null);
-                try {
-                     if (mailboxCursor.moveToFirst()) {
-                        Log.i(TAG, "Mail sync requested for " + account.name);
-                        // Ask for a sync from our sync manager
-                        ExchangeService.serviceRequest(mailboxCursor.getLong(0),
-                                ExchangeService.SYNC_KICK);
-                    }
-                } finally {
-                    mailboxCursor.close();
-                }
-            }
-        } finally {
-            accountCursor.close();
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/exchange/EmailSyncAlarmReceiver.java b/src/com/android/exchange/EmailSyncAlarmReceiver.java
deleted file mode 100644
index 8006016..0000000
--- a/src/com/android/exchange/EmailSyncAlarmReceiver.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- *  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;
-
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.util.Log;
-
-import com.android.emailcommon.provider.EmailContent.Message;
-import com.android.emailcommon.provider.EmailContent.MessageColumns;
-import com.android.emailcommon.provider.ProviderUnavailableException;
-
-import java.util.ArrayList;
-
-/**
- * EmailSyncAlarmReceiver (USAR) is used by the SyncManager to start up-syncs of user-modified data
- * back to the Exchange server.
- *
- * Here's how this works for Email, for example:
- *
- * 1) User modifies or deletes an email from the UI.
- * 2) SyncManager, which has a ContentObserver watching the Message class, is alerted to a change
- * 3) SyncManager sets an alarm (to be received by USAR) for a few seconds in the
- * future (currently 15), the delay preventing excess syncing (think of it as a debounce mechanism).
- * 4) ESAR Receiver's onReceive method is called
- * 5) ESAR goes through all change and deletion records and compiles a list of mailboxes which have
- * changes to be uploaded.
- * 6) ESAR calls SyncManager to start syncs of those mailboxes
- *
- * If EmailProvider isn't available, the upsyncs will happen the next time ExchangeService starts
- *
- */
-public class EmailSyncAlarmReceiver extends BroadcastReceiver {
-    final String[] MAILBOX_DATA_PROJECTION = {MessageColumns.MAILBOX_KEY};
-
-    @Override
-    public void onReceive(final Context context, Intent intent) {
-        new Thread(new Runnable() {
-            public void run() {
-                handleReceive(context);
-            }
-        }).start();
-    }
-
-    private void handleReceive(Context context) {
-        ArrayList<Long> mailboxesToNotify = new ArrayList<Long>();
-        ContentResolver cr = context.getContentResolver();
-        int messageCount = 0;
-        
-        // Get a selector for EAS accounts (we don't want to sync on changes to POP/IMAP messages)
-        String selector = ExchangeService.getEasAccountSelector();
-        
-        try {
-            // Find all of the deletions
-            Cursor c = cr.query(Message.DELETED_CONTENT_URI, MAILBOX_DATA_PROJECTION, selector,
-                   null, null);
-            if (c == null) throw new ProviderUnavailableException();
-            try {
-                // Keep track of which mailboxes to notify; we'll only notify each one once
-                while (c.moveToNext()) {
-                    messageCount++;
-                    long mailboxId = c.getLong(0);
-                    if (!mailboxesToNotify.contains(mailboxId)) {
-                        mailboxesToNotify.add(mailboxId);
-                    }
-                }
-            } finally {
-                c.close();
-            }
-
-            // Now, find changed messages
-            c = cr.query(Message.UPDATED_CONTENT_URI, MAILBOX_DATA_PROJECTION, selector,
-                    null, null);
-            if (c == null) throw new ProviderUnavailableException();
-            try {
-                // Keep track of which mailboxes to notify; we'll only notify each one once
-                while (c.moveToNext()) {
-                    messageCount++;
-                    long mailboxId = c.getLong(0);
-                    if (!mailboxesToNotify.contains(mailboxId)) {
-                        mailboxesToNotify.add(mailboxId);
-                    }
-                }
-            } finally {
-                c.close();
-            }
-
-            // Request service from the mailbox
-            for (Long mailboxId: mailboxesToNotify) {
-                ExchangeService.serviceRequest(mailboxId, ExchangeService.SYNC_UPSYNC);
-            }
-        } catch (ProviderUnavailableException e) {
-            Log.e("EmailSyncAlarmReceiver", "EmailProvider unavailable; aborting alarm receiver");
-        }
-    }
-}
diff --git a/src/com/android/exchange/Exchange.java b/src/com/android/exchange/Exchange.java
deleted file mode 100644
index 496e1f5..0000000
--- a/src/com/android/exchange/Exchange.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-import android.app.Application;
-
-public class Exchange extends Application {
-    // TODO Investigate whether this class is needed
-}
diff --git a/src/com/android/exchange/ExchangeService.java b/src/com/android/exchange/ExchangeService.java
deleted file mode 100644
index 833e0c4..0000000
--- a/src/com/android/exchange/ExchangeService.java
+++ /dev/null
@@ -1,2583 +0,0 @@
-/*
- * 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;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.State;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.Process;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.provider.CalendarContract;
-import android.provider.CalendarContract.Calendars;
-import android.provider.CalendarContract.Events;
-import android.provider.ContactsContract;
-import android.util.Log;
-
-import com.android.emailcommon.Api;
-import com.android.emailcommon.TempDirectory;
-import com.android.emailcommon.provider.Account;
-import com.android.emailcommon.provider.EmailContent;
-import com.android.emailcommon.provider.EmailContent.Attachment;
-import com.android.emailcommon.provider.EmailContent.HostAuthColumns;
-import com.android.emailcommon.provider.EmailContent.MailboxColumns;
-import com.android.emailcommon.provider.EmailContent.Message;
-import com.android.emailcommon.provider.EmailContent.SyncColumns;
-import com.android.emailcommon.provider.HostAuth;
-import com.android.emailcommon.provider.Mailbox;
-import com.android.emailcommon.provider.Policy;
-import com.android.emailcommon.provider.ProviderUnavailableException;
-import com.android.emailcommon.service.AccountServiceProxy;
-import com.android.emailcommon.service.EmailServiceProxy;
-import com.android.emailcommon.service.EmailServiceStatus;
-import com.android.emailcommon.service.IEmailService;
-import com.android.emailcommon.service.IEmailServiceCallback;
-import com.android.emailcommon.service.PolicyServiceProxy;
-import com.android.emailcommon.service.SearchParams;
-import com.android.emailcommon.utility.EmailAsyncTask;
-import com.android.emailcommon.utility.EmailClientConnectionManager;
-import com.android.emailcommon.utility.Utility;
-import com.android.exchange.adapter.CalendarSyncAdapter;
-import com.android.exchange.adapter.ContactsSyncAdapter;
-import com.android.exchange.adapter.Search;
-import com.android.exchange.provider.MailboxUtilities;
-import com.android.exchange.utility.FileLogger;
-
-import org.apache.http.conn.params.ConnManagerPNames;
-import org.apache.http.conn.params.ConnPerRoute;
-import org.apache.http.conn.routing.HttpRoute;
-import org.apache.http.params.BasicHttpParams;
-import org.apache.http.params.HttpParams;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * The ExchangeService handles all aspects of starting, maintaining, and stopping the various sync
- * adapters used by Exchange.  However, it is capable of handing any kind of email sync, and it
- * would be appropriate to use for IMAP push, when that functionality is added to the Email
- * application.
- *
- * The Email application communicates with EAS sync adapters via ExchangeService's binder interface,
- * which exposes UI-related functionality to the application (see the definitions below)
- *
- * ExchangeService uses ContentObservers to detect changes to accounts, mailboxes, and messages in
- * order to maintain proper 2-way syncing of data.  (More documentation to follow)
- *
- */
-public class ExchangeService extends Service implements Runnable {
-
-    private static final String TAG = "ExchangeService";
-
-    // The ExchangeService's mailbox "id"
-    public static final int EXTRA_MAILBOX_ID = -1;
-    public static final int EXCHANGE_SERVICE_MAILBOX_ID = 0;
-
-    private static final int SECONDS = 1000;
-    private static final int MINUTES = 60*SECONDS;
-    private static final int ONE_DAY_MINUTES = 1440;
-
-    private static final int EXCHANGE_SERVICE_HEARTBEAT_TIME = 15*MINUTES;
-    private static final int CONNECTIVITY_WAIT_TIME = 10*MINUTES;
-
-    // Sync hold constants for services with transient errors
-    private static final int HOLD_DELAY_MAXIMUM = 4*MINUTES;
-
-    // Reason codes when ExchangeService.kick is called (mainly for debugging)
-    // UI has changed data, requiring an upsync of changes
-    public static final int SYNC_UPSYNC = 0;
-    // A scheduled sync (when not using push)
-    public static final int SYNC_SCHEDULED = 1;
-    // Mailbox was marked push
-    public static final int SYNC_PUSH = 2;
-    // A ping (EAS push signal) was received
-    public static final int SYNC_PING = 3;
-    // Misc.
-    public static final int SYNC_KICK = 4;
-    // A part request (attachment load, for now) was sent to ExchangeService
-    public static final int SYNC_SERVICE_PART_REQUEST = 5;
-
-    // Requests >= SYNC_CALLBACK_START generate callbacks to the UI
-    public static final int SYNC_CALLBACK_START = 6;
-    // startSync was requested of ExchangeService (other than due to user request)
-    public static final int SYNC_SERVICE_START_SYNC = SYNC_CALLBACK_START + 0;
-    // startSync was requested of ExchangeService (due to user request)
-    public static final int SYNC_UI_REQUEST = SYNC_CALLBACK_START + 1;
-
-    private static final String WHERE_PUSH_OR_PING_NOT_ACCOUNT_MAILBOX =
-        MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.TYPE + "!=" +
-        Mailbox.TYPE_EAS_ACCOUNT_MAILBOX + " and " + MailboxColumns.SYNC_INTERVAL +
-        " IN (" + Mailbox.CHECK_INTERVAL_PING + ',' + Mailbox.CHECK_INTERVAL_PUSH + ')';
-    protected static final String WHERE_IN_ACCOUNT_AND_PUSHABLE =
-        MailboxColumns.ACCOUNT_KEY + "=? and type in (" + Mailbox.TYPE_INBOX + ','
-        + Mailbox.TYPE_EAS_ACCOUNT_MAILBOX + ',' + Mailbox.TYPE_CONTACTS + ','
-        + Mailbox.TYPE_CALENDAR + ')';
-    protected static final String WHERE_IN_ACCOUNT_AND_TYPE_INBOX =
-        MailboxColumns.ACCOUNT_KEY + "=? and type = " + Mailbox.TYPE_INBOX ;
-    private static final String WHERE_MAILBOX_KEY = Message.MAILBOX_KEY + "=?";
-    private static final String WHERE_PROTOCOL_EAS = HostAuthColumns.PROTOCOL + "=\"" +
-        AbstractSyncService.EAS_PROTOCOL + "\"";
-    private static final String WHERE_NOT_INTERVAL_NEVER_AND_ACCOUNT_KEY_IN =
-        "(" + MailboxColumns.TYPE + '=' + Mailbox.TYPE_OUTBOX
-        + " or " + MailboxColumns.SYNC_INTERVAL + "!=" + Mailbox.CHECK_INTERVAL_NEVER + ')'
-        + " and " + MailboxColumns.ACCOUNT_KEY + " in (";
-    private static final String ACCOUNT_KEY_IN = MailboxColumns.ACCOUNT_KEY + " in (";
-    private static final String WHERE_CALENDAR_ID = Events.CALENDAR_ID + "=?";
-
-    // Offsets into the syncStatus data for EAS that indicate type, exit status, and change count
-    // The format is S<type_char>:<exit_char>:<change_count>
-    public static final int STATUS_TYPE_CHAR = 1;
-    public static final int STATUS_EXIT_CHAR = 3;
-    public static final int STATUS_CHANGE_COUNT_OFFSET = 5;
-
-    // Ready for ping
-    public static final int PING_STATUS_OK = 0;
-    // Service already running (can't ping)
-    public static final int PING_STATUS_RUNNING = 1;
-    // Service waiting after I/O error (can't ping)
-    public static final int PING_STATUS_WAITING = 2;
-    // Service had a fatal error; can't run
-    public static final int PING_STATUS_UNABLE = 3;
-
-    private static final int MAX_CLIENT_CONNECTION_MANAGER_SHUTDOWNS = 1;
-
-    // We synchronize on this for all actions affecting the service and error maps
-    private static final Object sSyncLock = new Object();
-    // All threads can use this lock to wait for connectivity
-    public static final Object sConnectivityLock = new Object();
-    public static boolean sConnectivityHold = false;
-
-    // Keeps track of running services (by mailbox id)
-    private final HashMap<Long, AbstractSyncService> mServiceMap =
-        new HashMap<Long, AbstractSyncService>();
-    // Keeps track of services whose last sync ended with an error (by mailbox id)
-    /*package*/ ConcurrentHashMap<Long, SyncError> mSyncErrorMap =
-        new ConcurrentHashMap<Long, SyncError>();
-    // Keeps track of which services require a wake lock (by mailbox id)
-    private final HashMap<Long, Boolean> mWakeLocks = new HashMap<Long, Boolean>();
-    // Keeps track of PendingIntents for mailbox alarms (by mailbox id)
-    private final HashMap<Long, PendingIntent> mPendingIntents = new HashMap<Long, PendingIntent>();
-    // The actual WakeLock obtained by ExchangeService
-    private WakeLock mWakeLock = null;
-    // Keep our cached list of active Accounts here
-    public final AccountList mAccountList = new AccountList();
-
-    // Observers that we use to look for changed mail-related data
-    private final Handler mHandler = new Handler();
-    private AccountObserver mAccountObserver;
-    private MailboxObserver mMailboxObserver;
-    private SyncedMessageObserver mSyncedMessageObserver;
-
-    // Concurrent because CalendarSyncAdapter can modify the map during a wipe
-    private final ConcurrentHashMap<Long, CalendarObserver> mCalendarObservers =
-        new ConcurrentHashMap<Long, CalendarObserver>();
-
-    private ContentResolver mResolver;
-
-    // The singleton ExchangeService object, with its thread and stop flag
-    protected static ExchangeService INSTANCE;
-    private static Thread sServiceThread = null;
-    // Cached unique device id
-    private static String sDeviceId = null;
-    // ConnectionManager that all EAS threads can use
-    private static EmailClientConnectionManager sClientConnectionManager = null;
-    // Count of ClientConnectionManager shutdowns
-    private static volatile int sClientConnectionManagerShutdownCount = 0;
-
-    private static volatile boolean sStartingUp = false;
-    private static volatile boolean sStop = false;
-
-    // The reason for ExchangeService's next wakeup call
-    private String mNextWaitReason;
-    // Whether we have an unsatisfied "kick" pending
-    private boolean mKicked = false;
-
-    // Receiver of connectivity broadcasts
-    private ConnectivityReceiver mConnectivityReceiver = null;
-    private ConnectivityReceiver mBackgroundDataSettingReceiver = null;
-    private volatile boolean mBackgroundData = true;
-    // The most current NetworkInfo (from ConnectivityManager)
-    private NetworkInfo mNetworkInfo;
-
-    // Callbacks as set up via setCallback
-    private final RemoteCallbackList<IEmailServiceCallback> mCallbackList =
-        new RemoteCallbackList<IEmailServiceCallback>();
-
-    private interface ServiceCallbackWrapper {
-        public void call(IEmailServiceCallback cb) throws RemoteException;
-    }
-
-    /**
-     * Proxy that can be used by various sync adapters to tie into ExchangeService's callback system
-     * Used this way:  ExchangeService.callback().callbackMethod(args...);
-     * The proxy wraps checking for existence of a ExchangeService instance
-     * Failures of these callbacks can be safely ignored.
-     */
-    static private final IEmailServiceCallback.Stub sCallbackProxy =
-        new IEmailServiceCallback.Stub() {
-
-        /**
-         * Broadcast a callback to the everyone that's registered
-         *
-         * @param wrapper the ServiceCallbackWrapper used in the broadcast
-         */
-        private synchronized void broadcastCallback(ServiceCallbackWrapper wrapper) {
-            RemoteCallbackList<IEmailServiceCallback> callbackList =
-                (INSTANCE == null) ? null: INSTANCE.mCallbackList;
-            if (callbackList != null) {
-                // Call everyone on our callback list
-                int count = callbackList.beginBroadcast();
-                try {
-                    for (int i = 0; i < count; i++) {
-                        try {
-                            wrapper.call(callbackList.getBroadcastItem(i));
-                        } catch (RemoteException e) {
-                            // Safe to ignore
-                        } catch (RuntimeException e) {
-                            // We don't want an exception in one call to prevent other calls, so
-                            // we'll just log this and continue
-                            Log.e(TAG, "Caught RuntimeException in broadcast", e);
-                        }
-                    }
-                } finally {
-                    // No matter what, we need to finish the broadcast
-                    callbackList.finishBroadcast();
-                }
-            }
-        }
-
-        public void loadAttachmentStatus(final long messageId, final long attachmentId,
-                final int status, final int progress) {
-            broadcastCallback(new ServiceCallbackWrapper() {
-                @Override
-                public void call(IEmailServiceCallback cb) throws RemoteException {
-                    cb.loadAttachmentStatus(messageId, attachmentId, status, progress);
-                }
-            });
-        }
-
-        public void sendMessageStatus(final long messageId, final String subject, final int status,
-                final int progress) {
-            broadcastCallback(new ServiceCallbackWrapper() {
-                @Override
-                public void call(IEmailServiceCallback cb) throws RemoteException {
-                    cb.sendMessageStatus(messageId, subject, status, progress);
-                }
-            });
-        }
-
-        public void syncMailboxListStatus(final long accountId, final int status,
-                final int progress) {
-            broadcastCallback(new ServiceCallbackWrapper() {
-                @Override
-                public void call(IEmailServiceCallback cb) throws RemoteException {
-                    cb.syncMailboxListStatus(accountId, status, progress);
-                }
-            });
-        }
-
-        public void syncMailboxStatus(final long mailboxId, final int status,
-                final int progress) {
-            broadcastCallback(new ServiceCallbackWrapper() {
-                @Override
-                public void call(IEmailServiceCallback cb) throws RemoteException {
-                    cb.syncMailboxStatus(mailboxId, status, progress);
-                }
-            });
-        }
-    };
-
-    /**
-     * Create our EmailService implementation here.
-     */
-    private final IEmailService.Stub mBinder = new IEmailService.Stub() {
-
-        public int getApiLevel() {
-            return Api.LEVEL;
-        }
-
-        public Bundle validate(HostAuth hostAuth) throws RemoteException {
-            return AbstractSyncService.validate(EasSyncService.class,
-                    hostAuth, ExchangeService.this);
-        }
-
-        public Bundle autoDiscover(String userName, String password) throws RemoteException {
-            return new EasSyncService().tryAutodiscover(userName, password);
-        }
-
-        public void startSync(long mailboxId, boolean userRequest) throws RemoteException {
-            ExchangeService exchangeService = INSTANCE;
-            if (exchangeService == null) return;
-            checkExchangeServiceServiceRunning();
-            Mailbox m = Mailbox.restoreMailboxWithId(exchangeService, mailboxId);
-            if (m == null) return;
-            Account acct = Account.restoreAccountWithId(exchangeService, m.mAccountKey);
-            if (acct == null) return;
-            // If this is a user request and we're being held, release the hold; this allows us to
-            // try again (the hold might have been specific to this account and released already)
-            if (userRequest) {
-                if (onSyncDisabledHold(acct)) {
-                    releaseSyncHolds(exchangeService, AbstractSyncService.EXIT_ACCESS_DENIED, acct);
-                    log("User requested sync of account in sync disabled hold; releasing");
-                } else if (onSecurityHold(acct)) {
-                    releaseSyncHolds(exchangeService, AbstractSyncService.EXIT_SECURITY_FAILURE,
-                            acct);
-                    log("User requested sync of account in security hold; releasing");
-                }
-                if (sConnectivityHold) {
-                    try {
-                        // UI is expecting the callbacks....
-                        sCallbackProxy.syncMailboxStatus(mailboxId, EmailServiceStatus.IN_PROGRESS,
-                                0);
-                        sCallbackProxy.syncMailboxStatus(mailboxId,
-                                EmailServiceStatus.CONNECTION_ERROR, 0);
-                    } catch (RemoteException ignore) {
-                    }
-                    return;
-                }
-            }
-            if (m.mType == Mailbox.TYPE_OUTBOX) {
-                // We're using SERVER_ID to indicate an error condition (it has no other use for
-                // sent mail)  Upon request to sync the Outbox, we clear this so that all messages
-                // are candidates for sending.
-                ContentValues cv = new ContentValues();
-                cv.put(SyncColumns.SERVER_ID, 0);
-                exchangeService.getContentResolver().update(Message.CONTENT_URI,
-                    cv, WHERE_MAILBOX_KEY, new String[] {Long.toString(mailboxId)});
-                // Clear the error state; the Outbox sync will be started from checkMailboxes
-                exchangeService.mSyncErrorMap.remove(mailboxId);
-                kick("start outbox");
-                // Outbox can't be synced in EAS
-                return;
-            } else if (!isSyncable(m)) {
-                try {
-                    // UI may be expecting the callbacks, so send them
-                    sCallbackProxy.syncMailboxStatus(mailboxId, EmailServiceStatus.IN_PROGRESS, 0);
-                    sCallbackProxy.syncMailboxStatus(mailboxId, EmailServiceStatus.SUCCESS, 0);
-                } catch (RemoteException ignore) {
-                    // We tried
-                }
-                return;
-            }
-            startManualSync(mailboxId, userRequest ? ExchangeService.SYNC_UI_REQUEST :
-                ExchangeService.SYNC_SERVICE_START_SYNC, null);
-        }
-
-        public void stopSync(long mailboxId) throws RemoteException {
-            stopManualSync(mailboxId);
-        }
-
-        public void loadAttachment(long attachmentId, boolean background) throws RemoteException {
-            Attachment att = Attachment.restoreAttachmentWithId(ExchangeService.this, attachmentId);
-            log("loadAttachment " + attachmentId + ": " + att.mFileName);
-            sendMessageRequest(new PartRequest(att, null, null));
-        }
-
-        public void updateFolderList(long accountId) throws RemoteException {
-            reloadFolderList(ExchangeService.this, accountId, false);
-        }
-
-        public void hostChanged(long accountId) throws RemoteException {
-            ExchangeService exchangeService = INSTANCE;
-            if (exchangeService == null) return;
-            ConcurrentHashMap<Long, SyncError> syncErrorMap = exchangeService.mSyncErrorMap;
-            // Go through the various error mailboxes
-            for (long mailboxId: syncErrorMap.keySet()) {
-                SyncError error = syncErrorMap.get(mailboxId);
-                // If it's a login failure, look a little harder
-                Mailbox m = Mailbox.restoreMailboxWithId(exchangeService, mailboxId);
-                // If it's for the account whose host has changed, clear the error
-                // If the mailbox is no longer around, remove the entry in the map
-                if (m == null) {
-                    syncErrorMap.remove(mailboxId);
-                } else if (error != null && m.mAccountKey == accountId) {
-                    error.fatal = false;
-                    error.holdEndTime = 0;
-                }
-            }
-            // Stop any running syncs
-            exchangeService.stopAccountSyncs(accountId, true);
-            // Kick ExchangeService
-            kick("host changed");
-        }
-
-        public void setLogging(int flags) throws RemoteException {
-            Eas.setUserDebug(flags);
-        }
-
-        public void sendMeetingResponse(long messageId, int response) throws RemoteException {
-            sendMessageRequest(new MeetingResponseRequest(messageId, response));
-        }
-
-        public void loadMore(long messageId) throws RemoteException {
-        }
-
-        // The following three methods are not implemented in this version
-        public boolean createFolder(long accountId, String name) throws RemoteException {
-            return false;
-        }
-
-        public boolean deleteFolder(long accountId, String name) throws RemoteException {
-            return false;
-        }
-
-        public boolean renameFolder(long accountId, String oldName, String newName)
-                throws RemoteException {
-            return false;
-        }
-
-        public void setCallback(IEmailServiceCallback cb) throws RemoteException {
-            mCallbackList.register(cb);
-        }
-
-        /**
-         * Delete PIM (calendar, contacts) data for the specified account
-         *
-         * @param accountId the account whose data should be deleted
-         * @throws RemoteException
-         */
-        public void deleteAccountPIMData(long accountId) throws RemoteException {
-            // Stop any running syncs
-            ExchangeService.stopAccountSyncs(accountId);
-            // Delete the data
-            ExchangeService.deleteAccountPIMData(accountId);
-        }
-
-        public int searchMessages(long accountId, SearchParams searchParams, long destMailboxId) {
-            ExchangeService exchangeService = INSTANCE;
-            if (exchangeService == null) return 0;
-            return Search.searchMessages(exchangeService, accountId, searchParams,
-                    destMailboxId);
-        }
-    };
-
-    /**
-     * Return a list of all Accounts in EmailProvider.  Because the result of this call may be used
-     * in account reconciliation, an exception is thrown if the result cannot be guaranteed accurate
-     * @param context the caller's context
-     * @param accounts a list that Accounts will be added into
-     * @return the list of Accounts
-     * @throws ProviderUnavailableException if the list of Accounts cannot be guaranteed valid
-     */
-    private static AccountList collectEasAccounts(Context context, AccountList accounts) {
-        ContentResolver resolver = context.getContentResolver();
-        Cursor c = resolver.query(Account.CONTENT_URI, Account.CONTENT_PROJECTION, null, null,
-                null);
-        // We must throw here; callers might use the information we provide for reconciliation, etc.
-        if (c == null) throw new ProviderUnavailableException();
-        try {
-            ContentValues cv = new ContentValues();
-            while (c.moveToNext()) {
-                long hostAuthId = c.getLong(Account.CONTENT_HOST_AUTH_KEY_RECV_COLUMN);
-                if (hostAuthId > 0) {
-                    HostAuth ha = HostAuth.restoreHostAuthWithId(context, hostAuthId);
-                    if (ha != null && ha.mProtocol.equals("eas")) {
-                        Account account = new Account();
-                        account.restore(c);
-                        // Cache the HostAuth
-                        account.mHostAuthRecv = ha;
-                        accounts.add(account);
-                        // Fixup flags for inbox (should accept moved mail)
-                        Mailbox inbox = Mailbox.restoreMailboxOfType(context, account.mId,
-                                Mailbox.TYPE_INBOX);
-                        if (inbox != null &&
-                                ((inbox.mFlags & Mailbox.FLAG_ACCEPTS_MOVED_MAIL) == 0)) {
-                            cv.put(MailboxColumns.FLAGS,
-                                    inbox.mFlags | Mailbox.FLAG_ACCEPTS_MOVED_MAIL);
-                            resolver.update(
-                                    ContentUris.withAppendedId(Mailbox.CONTENT_URI, inbox.mId), cv,
-                                    null, null);
-                        }
-                    }
-                }
-            }
-        } finally {
-            c.close();
-        }
-        return accounts;
-    }
-
-    static class AccountList extends ArrayList<Account> {
-        private static final long serialVersionUID = 1L;
-
-        @Override
-        public boolean add(Account account) {
-            // Cache the account manager account
-            account.mAmAccount = new android.accounts.Account(account.mEmailAddress,
-                    Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE);
-            super.add(account);
-            return true;
-        }
-
-        public boolean contains(long id) {
-            for (Account account : this) {
-                if (account.mId == id) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        public Account getById(long id) {
-            for (Account account : this) {
-                if (account.mId == id) {
-                    return account;
-                }
-            }
-            return null;
-        }
-
-        public Account getByName(String accountName) {
-            for (Account account : this) {
-                if (account.mEmailAddress.equalsIgnoreCase(accountName)) {
-                    return account;
-                }
-            }
-            return null;
-        }
-    }
-
-    public static void deleteAccountPIMData(long accountId) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService == null) return;
-        Mailbox mailbox =
-            Mailbox.restoreMailboxOfType(exchangeService, accountId, Mailbox.TYPE_CONTACTS);
-        if (mailbox != null) {
-            EasSyncService service = new EasSyncService(exchangeService, mailbox);
-            ContactsSyncAdapter adapter = new ContactsSyncAdapter(service);
-            adapter.wipe();
-        }
-        mailbox =
-            Mailbox.restoreMailboxOfType(exchangeService, accountId, Mailbox.TYPE_CALENDAR);
-        if (mailbox != null) {
-            EasSyncService service = new EasSyncService(exchangeService, mailbox);
-            CalendarSyncAdapter adapter = new CalendarSyncAdapter(service);
-            adapter.wipe();
-        }
-    }
-
-    private boolean onSecurityHold(Account account) {
-        return (account.mFlags & Account.FLAGS_SECURITY_HOLD) != 0;
-    }
-
-    private boolean onSyncDisabledHold(Account account) {
-        return (account.mFlags & Account.FLAGS_SYNC_DISABLED) != 0;
-    }
-
-    class AccountObserver extends ContentObserver {
-        String mSyncableEasMailboxSelector = null;
-        String mEasAccountSelector = null;
-
-        // Runs when ExchangeService first starts
-        public AccountObserver(Handler handler) {
-            super(handler);
-            // At startup, we want to see what EAS accounts exist and cache them
-            // TODO: Move database work out of UI thread
-            Context context = getContext();
-            synchronized (mAccountList) {
-                try {
-                    collectEasAccounts(context, mAccountList);
-                } catch (ProviderUnavailableException e) {
-                    // Just leave if EmailProvider is unavailable
-                    return;
-                }
-                // Create an account mailbox for any account without one
-                for (Account account : mAccountList) {
-                    int cnt = Mailbox.count(context, Mailbox.CONTENT_URI, "accountKey="
-                            + account.mId, null);
-                    if (cnt == 0) {
-                        // This case handles a newly created account
-                        addAccountMailbox(account.mId);
-                    }
-                }
-            }
-            // Run through accounts and update account hold information
-            Utility.runAsync(new Runnable() {
-                @Override
-                public void run() {
-                    synchronized (mAccountList) {
-                        for (Account account : mAccountList) {
-                            if ((account.mFlags & Account.FLAGS_SECURITY_HOLD) != 0) {
-                                // If we're in a security hold, and our policies are active, release
-                                // the hold; otherwise, ping PolicyService that this account's
-                                // policies are required
-                                if (PolicyServiceProxy.isActive(ExchangeService.this, null)) {
-                                    PolicyServiceProxy.setAccountHoldFlag(ExchangeService.this,
-                                            account, false);
-                                    log("isActive true; release hold for " + account.mDisplayName);
-                                } else {
-                                    PolicyServiceProxy.policiesRequired(ExchangeService.this,
-                                            account.mId);
-                                }
-                            }
-                        }
-                    }
-                }});
-        }
-
-        /**
-         * Returns a String suitable for appending to a where clause that selects for all syncable
-         * mailboxes in all eas accounts
-         * @return a complex selection string that is not to be cached
-         */
-        public String getSyncableEasMailboxWhere() {
-            if (mSyncableEasMailboxSelector == null) {
-                StringBuilder sb = new StringBuilder(WHERE_NOT_INTERVAL_NEVER_AND_ACCOUNT_KEY_IN);
-                boolean first = true;
-                synchronized (mAccountList) {
-                    for (Account account : mAccountList) {
-                        if (!first) {
-                            sb.append(',');
-                        } else {
-                            first = false;
-                        }
-                        sb.append(account.mId);
-                    }
-                }
-                sb.append(')');
-                mSyncableEasMailboxSelector = sb.toString();
-            }
-            return mSyncableEasMailboxSelector;
-        }
-
-        /**
-         * Returns a String suitable for appending to a where clause that selects for all eas
-         * accounts.
-         * @return a String in the form "accountKey in (a, b, c...)" that is not to be cached
-         */
-        public String getAccountKeyWhere() {
-            if (mEasAccountSelector == null) {
-                StringBuilder sb = new StringBuilder(ACCOUNT_KEY_IN);
-                boolean first = true;
-                synchronized (mAccountList) {
-                    for (Account account : mAccountList) {
-                        if (!first) {
-                            sb.append(',');
-                        } else {
-                            first = false;
-                        }
-                        sb.append(account.mId);
-                    }
-                }
-                sb.append(')');
-                mEasAccountSelector = sb.toString();
-            }
-            return mEasAccountSelector;
-        }
-
-        private void onAccountChanged() {
-            try {
-                maybeStartExchangeServiceThread();
-                Context context = getContext();
-
-                // A change to the list requires us to scan for deletions (stop running syncs)
-                // At startup, we want to see what accounts exist and cache them
-                AccountList currentAccounts = new AccountList();
-                try {
-                    collectEasAccounts(context, currentAccounts);
-                } catch (ProviderUnavailableException e) {
-                    // Just leave if EmailProvider is unavailable
-                    return;
-                }
-                synchronized (mAccountList) {
-                    for (Account account : mAccountList) {
-                        boolean accountIncomplete =
-                            (account.mFlags & Account.FLAGS_INCOMPLETE) != 0;
-                        // If the current list doesn't include this account and the account wasn't
-                        // incomplete, then this is a deletion
-                        if (!currentAccounts.contains(account.mId) && !accountIncomplete) {
-                            // The implication is that the account has been deleted; let's find out
-                            alwaysLog("Observer found deleted account: " + account.mDisplayName);
-                            // Run the reconciler (the reconciliation itself runs in the Email app)
-                            runAccountReconcilerSync(ExchangeService.this);
-                            // See if the account is still around
-                            Account deletedAccount =
-                                Account.restoreAccountWithId(context, account.mId);
-                            if (deletedAccount != null) {
-                                // It is; add it to our account list
-                                alwaysLog("Account still in provider: " + account.mDisplayName);
-                                currentAccounts.add(account);
-                            } else {
-                                // It isn't; stop syncs and clear our selectors
-                                alwaysLog("Account deletion confirmed: " + account.mDisplayName);
-                                stopAccountSyncs(account.mId, true);
-                                mSyncableEasMailboxSelector = null;
-                                mEasAccountSelector = null;
-                            }
-                        } else {
-                            // Get the newest version of this account
-                            Account updatedAccount =
-                                Account.restoreAccountWithId(context, account.mId);
-                            if (updatedAccount == null) continue;
-                            if (account.mSyncInterval != updatedAccount.mSyncInterval
-                                    || account.mSyncLookback != updatedAccount.mSyncLookback) {
-                                // Set the inbox interval to the interval of the Account
-                                // This setting should NOT affect other boxes
-                                ContentValues cv = new ContentValues();
-                                cv.put(MailboxColumns.SYNC_INTERVAL, updatedAccount.mSyncInterval);
-                                getContentResolver().update(Mailbox.CONTENT_URI, cv,
-                                        WHERE_IN_ACCOUNT_AND_TYPE_INBOX, new String[] {
-                                        Long.toString(account.mId)
-                                });
-                                // Stop all current syncs; the appropriate ones will restart
-                                log("Account " + account.mDisplayName + " changed; stop syncs");
-                                stopAccountSyncs(account.mId, true);
-                            }
-
-                            // See if this account is no longer on security hold
-                            if (onSecurityHold(account) && !onSecurityHold(updatedAccount)) {
-                                releaseSyncHolds(ExchangeService.this,
-                                        AbstractSyncService.EXIT_SECURITY_FAILURE, account);
-                            }
-
-                            // Put current values into our cached account
-                            account.mSyncInterval = updatedAccount.mSyncInterval;
-                            account.mSyncLookback = updatedAccount.mSyncLookback;
-                            account.mFlags = updatedAccount.mFlags;
-                        }
-                    }
-                    // Look for new accounts
-                    for (Account account : currentAccounts) {
-                        if (!mAccountList.contains(account.mId)) {
-                            // Don't forget to cache the HostAuth
-                            HostAuth ha = HostAuth.restoreHostAuthWithId(getContext(),
-                                    account.mHostAuthKeyRecv);
-                            if (ha == null) continue;
-                            account.mHostAuthRecv = ha;
-                            // This is an addition; create our magic hidden mailbox...
-                            log("Account observer found new account: " + account.mDisplayName);
-                            addAccountMailbox(account.mId);
-                            mAccountList.add(account);
-                            mSyncableEasMailboxSelector = null;
-                            mEasAccountSelector = null;
-                        }
-                    }
-                    // Finally, make sure our account list is up to date
-                    mAccountList.clear();
-                    mAccountList.addAll(currentAccounts);
-                }
-
-                // See if there's anything to do...
-                kick("account changed");
-            } catch (ProviderUnavailableException e) {
-                alwaysLog("Observer failed; provider unavailable");
-            }
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            new Thread(new Runnable() {
-               public void run() {
-                   onAccountChanged();
-                }}, "Account Observer").start();
-        }
-
-        private void addAccountMailbox(long acctId) {
-            Account acct = Account.restoreAccountWithId(getContext(), acctId);
-            Mailbox main = new Mailbox();
-            main.mDisplayName = Eas.ACCOUNT_MAILBOX_PREFIX;
-            main.mServerId = Eas.ACCOUNT_MAILBOX_PREFIX + System.nanoTime();
-            main.mAccountKey = acct.mId;
-            main.mType = Mailbox.TYPE_EAS_ACCOUNT_MAILBOX;
-            main.mSyncInterval = Mailbox.CHECK_INTERVAL_PUSH;
-            main.mFlagVisible = false;
-            main.save(getContext());
-            log("Initializing account: " + acct.mDisplayName);
-        }
-
-    }
-
-    /**
-     * Register a specific Calendar's data observer; we need to recognize when the SYNC_EVENTS
-     * column has changed (when sync has turned off or on)
-     * @param account the Account whose Calendar we're observing
-     */
-    private void registerCalendarObserver(Account account) {
-        // Get a new observer
-        CalendarObserver observer = new CalendarObserver(mHandler, account);
-        if (observer.mCalendarId != 0) {
-            // If we find the Calendar (and we'd better) register it and store it in the map
-            mCalendarObservers.put(account.mId, observer);
-            mResolver.registerContentObserver(
-                    ContentUris.withAppendedId(Calendars.CONTENT_URI, observer.mCalendarId), false,
-                    observer);
-        }
-    }
-
-    /**
-     * Unregister all CalendarObserver's
-     */
-    static public void unregisterCalendarObservers() {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService == null) return;
-        ContentResolver resolver = exchangeService.mResolver;
-        for (CalendarObserver observer: exchangeService.mCalendarObservers.values()) {
-            resolver.unregisterContentObserver(observer);
-        }
-        exchangeService.mCalendarObservers.clear();
-    }
-
-    /**
-     * Return the syncable state of an account's calendar, as determined by the sync_events column
-     * of our Calendar (from CalendarProvider2)
-     * Note that the current state of sync_events is cached in our CalendarObserver
-     * @param accountId the id of the account whose calendar we are checking
-     * @return whether or not syncing of events is enabled
-     */
-    private boolean isCalendarEnabled(long accountId) {
-        CalendarObserver observer = mCalendarObservers.get(accountId);
-        if (observer != null) {
-            return (observer.mSyncEvents == 1);
-        }
-        // If there's no observer, there's no Calendar in CalendarProvider2, so we return true
-        // to allow Calendar creation
-        return true;
-    }
-
-    private class CalendarObserver extends ContentObserver {
-        long mAccountId;
-        long mCalendarId;
-        long mSyncEvents;
-        String mAccountName;
-
-        public CalendarObserver(Handler handler, Account account) {
-            super(handler);
-            mAccountId = account.mId;
-            mAccountName = account.mEmailAddress;
-
-            // Find the Calendar for this account
-            Cursor c = mResolver.query(Calendars.CONTENT_URI,
-                    new String[] {Calendars._ID, Calendars.SYNC_EVENTS},
-                    CalendarSyncAdapter.CALENDAR_SELECTION,
-                    new String[] {account.mEmailAddress, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE},
-                    null);
-            if (c != null) {
-                // Save its id and its sync events status
-                try {
-                    if (c.moveToFirst()) {
-                        mCalendarId = c.getLong(0);
-                        mSyncEvents = c.getLong(1);
-                    }
-                } finally {
-                    c.close();
-                }
-            }
-        }
-
-        @Override
-        public synchronized void onChange(boolean selfChange) {
-            // See if the user has changed syncing of our calendar
-            if (!selfChange) {
-                new Thread(new Runnable() {
-                    public void run() {
-                        try {
-                            Cursor c = mResolver.query(Calendars.CONTENT_URI,
-                                    new String[] {Calendars.SYNC_EVENTS}, Calendars._ID + "=?",
-                                    new String[] {Long.toString(mCalendarId)}, null);
-                            if (c == null) return;
-                            // Get its sync events; if it's changed, we've got work to do
-                            try {
-                                if (c.moveToFirst()) {
-                                    long newSyncEvents = c.getLong(0);
-                                    if (newSyncEvents != mSyncEvents) {
-                                        log("_sync_events changed for calendar in " + mAccountName);
-                                        Mailbox mailbox = Mailbox.restoreMailboxOfType(INSTANCE,
-                                                mAccountId, Mailbox.TYPE_CALENDAR);
-                                        // Sanity check for mailbox deletion
-                                        if (mailbox == null) return;
-                                        ContentValues cv = new ContentValues();
-                                        if (newSyncEvents == 0) {
-                                            // When sync is disabled, we're supposed to delete
-                                            // all events in the calendar
-                                            log("Deleting events and setting syncKey to 0 for " +
-                                                    mAccountName);
-                                            // First, stop any sync that's ongoing
-                                            stopManualSync(mailbox.mId);
-                                            // Set the syncKey to 0 (reset)
-                                            EasSyncService service =
-                                                new EasSyncService(INSTANCE, mailbox);
-                                            CalendarSyncAdapter adapter =
-                                                new CalendarSyncAdapter(service);
-                                            try {
-                                                adapter.setSyncKey("0", false);
-                                            } catch (IOException e) {
-                                                // The provider can't be reached; nothing to be done
-                                            }
-                                            // Reset the sync key locally and stop syncing
-                                            cv.put(Mailbox.SYNC_KEY, "0");
-                                            cv.put(Mailbox.SYNC_INTERVAL,
-                                                    Mailbox.CHECK_INTERVAL_NEVER);
-                                            mResolver.update(ContentUris.withAppendedId(
-                                                    Mailbox.CONTENT_URI, mailbox.mId), cv, null,
-                                                    null);
-                                            // Delete all events using the sync adapter
-                                            // parameter so that the deletion is only local
-                                            Uri eventsAsSyncAdapter =
-                                                CalendarSyncAdapter.asSyncAdapter(
-                                                    Events.CONTENT_URI,
-                                                    mAccountName,
-                                                    Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE);
-                                            mResolver.delete(eventsAsSyncAdapter, WHERE_CALENDAR_ID,
-                                                    new String[] {Long.toString(mCalendarId)});
-                                        } else {
-                                            // Make this a push mailbox and kick; this will start
-                                            // a resync of the Calendar; the account mailbox will
-                                            // ping on this during the next cycle of the ping loop
-                                            cv.put(Mailbox.SYNC_INTERVAL,
-                                                    Mailbox.CHECK_INTERVAL_PUSH);
-                                            mResolver.update(ContentUris.withAppendedId(
-                                                    Mailbox.CONTENT_URI, mailbox.mId), cv, null,
-                                                    null);
-                                            kick("calendar sync changed");
-                                        }
-
-                                        // Save away the new value
-                                        mSyncEvents = newSyncEvents;
-                                    }
-                                }
-                            } finally {
-                                c.close();
-                            }
-                        } catch (ProviderUnavailableException e) {
-                            Log.w(TAG, "Observer failed; provider unavailable");
-                        }
-                    }}, "Calendar Observer").start();
-            }
-        }
-    }
-
-    private class MailboxObserver extends ContentObserver {
-        public MailboxObserver(Handler handler) {
-            super(handler);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            // See if there's anything to do...
-            if (!selfChange) {
-                kick("mailbox changed");
-            }
-        }
-    }
-
-    private class SyncedMessageObserver extends ContentObserver {
-        Intent syncAlarmIntent = new Intent(INSTANCE, EmailSyncAlarmReceiver.class);
-        PendingIntent syncAlarmPendingIntent =
-            PendingIntent.getBroadcast(INSTANCE, 0, syncAlarmIntent, 0);
-        AlarmManager alarmManager = (AlarmManager)INSTANCE.getSystemService(Context.ALARM_SERVICE);
-
-        public SyncedMessageObserver(Handler handler) {
-            super(handler);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            alarmManager.set(AlarmManager.RTC_WAKEUP,
-                    System.currentTimeMillis() + 10*SECONDS, syncAlarmPendingIntent);
-        }
-    }
-
-    static public IEmailServiceCallback callback() {
-        return sCallbackProxy;
-    }
-
-    static public Account getAccountById(long accountId) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService != null) {
-            AccountList accountList = exchangeService.mAccountList;
-            synchronized (accountList) {
-                return accountList.getById(accountId);
-            }
-        }
-        return null;
-    }
-
-    static public Account getAccountByName(String accountName) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService != null) {
-            AccountList accountList = exchangeService.mAccountList;
-            synchronized (accountList) {
-                return accountList.getByName(accountName);
-            }
-        }
-        return null;
-    }
-
-    static public String getEasAccountSelector() {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService != null && exchangeService.mAccountObserver != null) {
-            return exchangeService.mAccountObserver.getAccountKeyWhere();
-        }
-        return null;
-    }
-
-    public class SyncStatus {
-        static public final int NOT_RUNNING = 0;
-        static public final int DIED = 1;
-        static public final int SYNC = 2;
-        static public final int IDLE = 3;
-    }
-
-    /*package*/ class SyncError {
-        int reason;
-        boolean fatal = false;
-        long holdDelay = 15*SECONDS;
-        long holdEndTime = System.currentTimeMillis() + holdDelay;
-
-        SyncError(int _reason, boolean _fatal) {
-            reason = _reason;
-            fatal = _fatal;
-        }
-
-        /**
-         * We double the holdDelay from 15 seconds through 4 mins
-         */
-        void escalate() {
-            if (holdDelay < HOLD_DELAY_MAXIMUM) {
-                holdDelay *= 2;
-            }
-            holdEndTime = System.currentTimeMillis() + holdDelay;
-        }
-    }
-
-    private void logSyncHolds() {
-        if (Eas.USER_LOG) {
-            log("Sync holds:");
-            long time = System.currentTimeMillis();
-            for (long mailboxId : mSyncErrorMap.keySet()) {
-                Mailbox m = Mailbox.restoreMailboxWithId(this, mailboxId);
-                if (m == null) {
-                    log("Mailbox " + mailboxId + " no longer exists");
-                } else {
-                    SyncError error = mSyncErrorMap.get(mailboxId);
-                    if (error != null) {
-                        log("Mailbox " + m.mDisplayName + ", error = " + error.reason
-                                + ", fatal = " + error.fatal);
-                        if (error.holdEndTime > 0) {
-                            log("Hold ends in " + ((error.holdEndTime - time) / 1000) + "s");
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Release security holds for the specified account
-     * @param account the account whose Mailboxes should be released from security hold
-     */
-    static public void releaseSecurityHold(Account account) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService != null) {
-            exchangeService.releaseSyncHolds(INSTANCE, AbstractSyncService.EXIT_SECURITY_FAILURE,
-                    account);
-        }
-    }
-
-    /**
-     * Release a specific type of hold (the reason) for the specified Account; if the account
-     * is null, mailboxes from all accounts with the specified hold will be released
-     * @param reason the reason for the SyncError (AbstractSyncService.EXIT_XXX)
-     * @param account an Account whose mailboxes should be released (or all if null)
-     * @return whether or not any mailboxes were released
-     */
-    /*package*/ boolean releaseSyncHolds(Context context, int reason, Account account) {
-        boolean holdWasReleased = releaseSyncHoldsImpl(context, reason, account);
-        kick("security release");
-        return holdWasReleased;
-    }
-
-    private boolean releaseSyncHoldsImpl(Context context, int reason, Account account) {
-        boolean holdWasReleased = false;
-        for (long mailboxId: mSyncErrorMap.keySet()) {
-            if (account != null) {
-                Mailbox m = Mailbox.restoreMailboxWithId(context, mailboxId);
-                if (m == null) {
-                    mSyncErrorMap.remove(mailboxId);
-                } else if (m.mAccountKey != account.mId) {
-                    continue;
-                }
-            }
-            SyncError error = mSyncErrorMap.get(mailboxId);
-            if (error != null && error.reason == reason) {
-                mSyncErrorMap.remove(mailboxId);
-                holdWasReleased = true;
-            }
-        }
-        return holdWasReleased;
-    }
-
-    /**
-     * Reconcile Exchange accounts with AccountManager (asynchronous)
-     * @param context the caller's Context
-     */
-    public static void reconcileAccounts(final Context context) {
-        Utility.runAsync(new Runnable() {
-            @Override
-            public void run() {
-                ExchangeService exchangeService = INSTANCE;
-                if (exchangeService != null) {
-                    exchangeService.runAccountReconcilerSync(context);
-                }
-            }});
-    }
-
-    /**
-     * Blocking call to the account reconciler
-     */
-    public static void runAccountReconcilerSync(Context context) {
-        alwaysLog("Reconciling accounts...");
-        new AccountServiceProxy(context).reconcileAccounts(
-                HostAuth.SCHEME_EAS, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE);
-    }
-
-    public static void log(String str) {
-        log(TAG, str);
-    }
-
-    public static void log(String tag, String str) {
-        if (Eas.USER_LOG) {
-            Log.d(tag, str);
-            if (Eas.FILE_LOG) {
-                FileLogger.log(tag, str);
-            }
-        }
-    }
-
-    public static void alwaysLog(String str) {
-        if (!Eas.USER_LOG) {
-            Log.d(TAG, str);
-        } else {
-            log(str);
-        }
-    }
-
-    /**
-     * EAS requires a unique device id, so that sync is possible from a variety of different
-     * devices (e.g. the syncKey is specific to a device)  If we're on an emulator or some other
-     * device that doesn't provide one, we can create it as "device".
-     * This would work on a real device as well, but it would be better to use the "real" id if
-     * it's available
-     */
-    static public String getDeviceId(Context context) throws IOException {
-        if (sDeviceId == null) {
-            sDeviceId = new AccountServiceProxy(context).getDeviceId();
-            alwaysLog("Received deviceId from Email app: " + sDeviceId);
-        }
-        return sDeviceId;
-    }
-
-    @Override
-    public IBinder onBind(Intent arg0) {
-        return mBinder;
-    }
-
-    static public ConnPerRoute sConnPerRoute = new ConnPerRoute() {
-        public int getMaxForRoute(HttpRoute route) {
-            return 8;
-        }
-    };
-
-    static public synchronized EmailClientConnectionManager getClientConnectionManager() {
-        if (sClientConnectionManager == null) {
-            // After two tries, kill the process.  Most likely, this will happen in the background
-            // The service will restart itself after about 5 seconds
-            if (sClientConnectionManagerShutdownCount > MAX_CLIENT_CONNECTION_MANAGER_SHUTDOWNS) {
-                alwaysLog("Shutting down process to unblock threads");
-                Process.killProcess(Process.myPid());
-            }
-            HttpParams params = new BasicHttpParams();
-            params.setIntParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 25);
-            params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, sConnPerRoute);
-            sClientConnectionManager = EmailClientConnectionManager.newInstance(params);
-        }
-        // Null is a valid return result if we get an exception
-        return sClientConnectionManager;
-    }
-
-    static private synchronized void shutdownConnectionManager() {
-        if (sClientConnectionManager != null) {
-            log("Shutting down ClientConnectionManager");
-            sClientConnectionManager.shutdown();
-            sClientConnectionManagerShutdownCount++;
-            sClientConnectionManager = null;
-        }
-    }
-
-    public static void stopAccountSyncs(long acctId) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService != null) {
-            exchangeService.stopAccountSyncs(acctId, true);
-        }
-    }
-
-    private void stopAccountSyncs(long acctId, boolean includeAccountMailbox) {
-        synchronized (sSyncLock) {
-            List<Long> deletedBoxes = new ArrayList<Long>();
-            for (Long mid : mServiceMap.keySet()) {
-                Mailbox box = Mailbox.restoreMailboxWithId(this, mid);
-                if (box != null) {
-                    if (box.mAccountKey == acctId) {
-                        if (!includeAccountMailbox &&
-                                box.mType == Mailbox.TYPE_EAS_ACCOUNT_MAILBOX) {
-                            AbstractSyncService svc = mServiceMap.get(mid);
-                            if (svc != null) {
-                                svc.stop();
-                            }
-                            continue;
-                        }
-                        AbstractSyncService svc = mServiceMap.get(mid);
-                        if (svc != null) {
-                            svc.stop();
-                            Thread t = svc.mThread;
-                            if (t != null) {
-                                t.interrupt();
-                            }
-                        }
-                        deletedBoxes.add(mid);
-                    }
-                }
-            }
-            for (Long mid : deletedBoxes) {
-                releaseMailbox(mid);
-            }
-        }
-    }
-
-    static private void reloadFolderListFailed(long accountId) {
-        try {
-            callback().syncMailboxListStatus(accountId,
-                    EmailServiceStatus.ACCOUNT_UNINITIALIZED, 0);
-        } catch (RemoteException e1) {
-            // Don't care if this fails
-        }
-    }
-
-    static public void reloadFolderList(Context context, long accountId, boolean force) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService == null) return;
-        Cursor c = context.getContentResolver().query(Mailbox.CONTENT_URI,
-                Mailbox.CONTENT_PROJECTION, MailboxColumns.ACCOUNT_KEY + "=? AND " +
-                MailboxColumns.TYPE + "=?",
-                new String[] {Long.toString(accountId),
-                    Long.toString(Mailbox.TYPE_EAS_ACCOUNT_MAILBOX)}, null);
-        try {
-            if (c.moveToFirst()) {
-                synchronized(sSyncLock) {
-                    Mailbox mailbox = new Mailbox();
-                    mailbox.restore(c);
-                    Account acct = Account.restoreAccountWithId(context, accountId);
-                    if (acct == null) {
-                        reloadFolderListFailed(accountId);
-                        return;
-                    }
-                    String syncKey = acct.mSyncKey;
-                    // No need to reload the list if we don't have one
-                    if (!force && (syncKey == null || syncKey.equals("0"))) {
-                        reloadFolderListFailed(accountId);
-                        return;
-                    }
-
-                    // Change all ping/push boxes to push/hold
-                    ContentValues cv = new ContentValues();
-                    cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PUSH_HOLD);
-                    context.getContentResolver().update(Mailbox.CONTENT_URI, cv,
-                            WHERE_PUSH_OR_PING_NOT_ACCOUNT_MAILBOX,
-                            new String[] {Long.toString(accountId)});
-                    log("Set push/ping boxes to push/hold");
-
-                    long id = mailbox.mId;
-                    AbstractSyncService svc = exchangeService.mServiceMap.get(id);
-                    // Tell the service we're done
-                    if (svc != null) {
-                        synchronized (svc.getSynchronizer()) {
-                            svc.stop();
-                            // Interrupt the thread so that it can stop
-                            Thread thread = svc.mThread;
-                            if (thread != null) {
-                                thread.setName(thread.getName() + " (Stopped)");
-                                thread.interrupt();
-                            }
-                        }
-                        // Abandon the service
-                        exchangeService.releaseMailbox(id);
-                        // And have it start naturally
-                        kick("reload folder list");
-                    }
-                }
-            }
-        } finally {
-            c.close();
-        }
-    }
-
-    /**
-     * Informs ExchangeService that an account has a new folder list; as a result, any existing
-     * folder might have become invalid.  Therefore, we act as if the account has been deleted, and
-     * then we reinitialize it.
-     *
-     * @param acctId
-     */
-    static public void stopNonAccountMailboxSyncsForAccount(long acctId) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService != null) {
-            exchangeService.stopAccountSyncs(acctId, false);
-            kick("reload folder list");
-        }
-    }
-
-    private void acquireWakeLock(long id) {
-        synchronized (mWakeLocks) {
-            Boolean lock = mWakeLocks.get(id);
-            if (lock == null) {
-                if (mWakeLock == null) {
-                    PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
-                    mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MAIL_SERVICE");
-                    mWakeLock.acquire();
-                    //log("+WAKE LOCK ACQUIRED");
-                }
-                mWakeLocks.put(id, true);
-             }
-        }
-    }
-
-    private void releaseWakeLock(long id) {
-        synchronized (mWakeLocks) {
-            Boolean lock = mWakeLocks.get(id);
-            if (lock != null) {
-                mWakeLocks.remove(id);
-                if (mWakeLocks.isEmpty()) {
-                    if (mWakeLock != null) {
-                        mWakeLock.release();
-                    }
-                    mWakeLock = null;
-                    //log("+WAKE LOCK RELEASED");
-                } else {
-                }
-            }
-        }
-    }
-
-    static public String alarmOwner(long id) {
-        if (id == EXTRA_MAILBOX_ID) {
-            return "ExchangeService";
-        } else {
-            String name = Long.toString(id);
-            if (Eas.USER_LOG && INSTANCE != null) {
-                Mailbox m = Mailbox.restoreMailboxWithId(INSTANCE, id);
-                if (m != null) {
-                    name = m.mDisplayName + '(' + m.mAccountKey + ')';
-                }
-            }
-            return "Mailbox " + name;
-        }
-    }
-
-    private void clearAlarm(long id) {
-        synchronized (mPendingIntents) {
-            PendingIntent pi = mPendingIntents.get(id);
-            if (pi != null) {
-                AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
-                alarmManager.cancel(pi);
-                //log("+Alarm cleared for " + alarmOwner(id));
-                mPendingIntents.remove(id);
-            }
-        }
-    }
-
-    private void setAlarm(long id, long millis) {
-        synchronized (mPendingIntents) {
-            PendingIntent pi = mPendingIntents.get(id);
-            if (pi == null) {
-                Intent i = new Intent(this, MailboxAlarmReceiver.class);
-                i.putExtra("mailbox", id);
-                i.setData(Uri.parse("Box" + id));
-                pi = PendingIntent.getBroadcast(this, 0, i, 0);
-                mPendingIntents.put(id, pi);
-
-                AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
-                alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + millis, pi);
-                //log("+Alarm set for " + alarmOwner(id) + ", " + millis/1000 + "s");
-            }
-        }
-    }
-
-    private void clearAlarms() {
-        AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
-        synchronized (mPendingIntents) {
-            for (PendingIntent pi : mPendingIntents.values()) {
-                alarmManager.cancel(pi);
-            }
-            mPendingIntents.clear();
-        }
-    }
-
-    static public void runAwake(long id) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService != null) {
-            exchangeService.acquireWakeLock(id);
-            exchangeService.clearAlarm(id);
-        }
-    }
-
-    static public void runAsleep(long id, long millis) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService != null) {
-            exchangeService.setAlarm(id, millis);
-            exchangeService.releaseWakeLock(id);
-        }
-    }
-
-    static public void clearWatchdogAlarm(long id) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService != null) {
-            exchangeService.clearAlarm(id);
-        }
-    }
-
-    static public void setWatchdogAlarm(long id, long millis) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService != null) {
-            exchangeService.setAlarm(id, millis);
-        }
-    }
-
-    static public void alert(Context context, final long id) {
-        final ExchangeService exchangeService = INSTANCE;
-        checkExchangeServiceServiceRunning();
-        if (id < 0) {
-            log("ExchangeService alert");
-            kick("ping ExchangeService");
-        } else if (exchangeService == null) {
-            context.startService(new Intent(context, ExchangeService.class));
-        } else {
-            final AbstractSyncService service = exchangeService.mServiceMap.get(id);
-            if (service != null) {
-                // Handle alerts in a background thread, as we are typically called from a
-                // broadcast receiver, and are therefore running in the UI thread
-                String threadName = "ExchangeService Alert: ";
-                if (service.mMailbox != null) {
-                    threadName += service.mMailbox.mDisplayName;
-                }
-                new Thread(new Runnable() {
-                   public void run() {
-                       Mailbox m = Mailbox.restoreMailboxWithId(exchangeService, id);
-                       if (m != null) {
-                           // We ignore drafts completely (doesn't sync).  Changes in Outbox are
-                           // handled in the checkMailboxes loop, so we can ignore these pings.
-                           if (Eas.USER_LOG) {
-                               Log.d(TAG, "Alert for mailbox " + id + " (" + m.mDisplayName + ")");
-                           }
-                           if (m.mType == Mailbox.TYPE_DRAFTS || m.mType == Mailbox.TYPE_OUTBOX) {
-                               String[] args = new String[] {Long.toString(m.mId)};
-                               ContentResolver resolver = INSTANCE.mResolver;
-                               resolver.delete(Message.DELETED_CONTENT_URI, WHERE_MAILBOX_KEY,
-                                       args);
-                               resolver.delete(Message.UPDATED_CONTENT_URI, WHERE_MAILBOX_KEY,
-                                       args);
-                               return;
-                           }
-                           service.mAccount = Account.restoreAccountWithId(INSTANCE, m.mAccountKey);
-                           service.mMailbox = m;
-                           // Send the alarm to the sync service
-                           if (!service.alarm()) {
-                               // A false return means that we were forced to interrupt the thread
-                               // In this case, we release the mailbox so that we can start another
-                               // thread to do the work
-                               log("Alarm failed; releasing mailbox");
-                               synchronized(sSyncLock) {
-                                   exchangeService.releaseMailbox(id);
-                               }
-                               // Shutdown the connection manager; this should close all of our
-                               // sockets and generate IOExceptions all around.
-                               ExchangeService.shutdownConnectionManager();
-                           }
-                       }
-                    }}, threadName).start();
-            }
-        }
-    }
-
-    public class ConnectivityReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
-                Bundle b = intent.getExtras();
-                if (b != null) {
-                    NetworkInfo a = (NetworkInfo)b.get(ConnectivityManager.EXTRA_NETWORK_INFO);
-                    String info = "Connectivity alert for " + a.getTypeName();
-                    State state = a.getState();
-                    if (state == State.CONNECTED) {
-                        info += " CONNECTED";
-                        log(info);
-                        synchronized (sConnectivityLock) {
-                            sConnectivityLock.notifyAll();
-                        }
-                        kick("connected");
-                    } else if (state == State.DISCONNECTED) {
-                        info += " DISCONNECTED";
-                        log(info);
-                        kick("disconnected");
-                    }
-                }
-            } else if (intent.getAction().equals(
-                    ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED)) {
-                ConnectivityManager cm =
-                        (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
-                mBackgroundData = cm.getBackgroundDataSetting();
-                // If background data is now on, we want to kick ExchangeService
-                if (mBackgroundData) {
-                    kick("background data on");
-                    log("Background data on; restart syncs");
-                // Otherwise, stop all syncs
-                } else {
-                    log("Background data off: stop all syncs");
-                    EmailAsyncTask.runAsyncParallel(new Runnable() {
-                        @Override
-                        public void run() {
-                            synchronized (mAccountList) {
-                                for (Account account : mAccountList)
-                                    ExchangeService.stopAccountSyncs(account.mId);
-                            }
-                        }});
-                }
-            }
-        }
-    }
-
-    /**
-     * Starts a service thread and enters it into the service map
-     * This is the point of instantiation of all sync threads
-     * @param service the service to start
-     * @param m the Mailbox on which the service will operate
-     */
-    private void startServiceThread(AbstractSyncService service, Mailbox m) {
-        if (m == null) return;
-        synchronized (sSyncLock) {
-            String mailboxName = m.mDisplayName;
-            String accountName = service.mAccount.mDisplayName;
-            Thread thread = new Thread(service, mailboxName + "[" + accountName + "]");
-            log("Starting thread for " + mailboxName + " in account " + accountName);
-            thread.start();
-            mServiceMap.put(m.mId, service);
-            runAwake(m.mId);
-            if ((m.mServerId != null) && !m.mServerId.startsWith(Eas.ACCOUNT_MAILBOX_PREFIX)) {
-                stopPing(m.mAccountKey);
-            }
-        }
-    }
-
-    /**
-     * Stop any ping in progress for the given account
-     * @param accountId
-     */
-    private void stopPing(long accountId) {
-        // Go through our active mailboxes looking for the right one
-        synchronized (sSyncLock) {
-            for (long mailboxId: mServiceMap.keySet()) {
-                Mailbox m = Mailbox.restoreMailboxWithId(this, mailboxId);
-                if (m != null) {
-                    String serverId = m.mServerId;
-                    if (m.mAccountKey == accountId && serverId != null &&
-                            serverId.startsWith(Eas.ACCOUNT_MAILBOX_PREFIX)) {
-                        // Here's our account mailbox; reset him (stopping pings)
-                        AbstractSyncService svc = mServiceMap.get(mailboxId);
-                        svc.reset();
-                    }
-                }
-            }
-        }
-    }
-
-    private void requestSync(Mailbox m, int reason, Request req) {
-        // Don't sync if there's no connectivity
-        if (sConnectivityHold || (m == null) || sStop) {
-            if (reason >= SYNC_CALLBACK_START) {
-                try {
-                    sCallbackProxy.syncMailboxStatus(m.mId, EmailServiceStatus.CONNECTION_ERROR, 0);
-                } catch (RemoteException e) {
-                    // We tried...
-                }
-            }
-            return;
-        }
-        synchronized (sSyncLock) {
-            Account acct = Account.restoreAccountWithId(this, m.mAccountKey);
-            if (acct != null) {
-                // Always make sure there's not a running instance of this service
-                AbstractSyncService service = mServiceMap.get(m.mId);
-                if (service == null) {
-                    service = new EasSyncService(this, m);
-                    if (!((EasSyncService)service).mIsValid) return;
-                    service.mSyncReason = reason;
-                    if (req != null) {
-                        service.addRequest(req);
-                    }
-                    startServiceThread(service, m);
-                }
-            }
-        }
-    }
-
-    private void stopServiceThreads() {
-        synchronized (sSyncLock) {
-            ArrayList<Long> toStop = new ArrayList<Long>();
-
-            // Keep track of which services to stop
-            for (Long mailboxId : mServiceMap.keySet()) {
-                toStop.add(mailboxId);
-            }
-
-            // Shut down all of those running services
-            for (Long mailboxId : toStop) {
-                AbstractSyncService svc = mServiceMap.get(mailboxId);
-                if (svc != null) {
-                    log("Stopping " + svc.mAccount.mDisplayName + '/' + svc.mMailbox.mDisplayName);
-                    svc.stop();
-                    if (svc.mThread != null) {
-                        svc.mThread.interrupt();
-                    }
-                }
-                releaseWakeLock(mailboxId);
-            }
-        }
-    }
-
-    private void waitForConnectivity() {
-        boolean waiting = false;
-        ConnectivityManager cm =
-            (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
-        while (!sStop) {
-            NetworkInfo info = cm.getActiveNetworkInfo();
-            if (info != null) {
-                mNetworkInfo = info;
-                // We're done if there's an active network
-                if (waiting) {
-                    // If we've been waiting, release any I/O error holds
-                    releaseSyncHolds(this, AbstractSyncService.EXIT_IO_ERROR, null);
-                    // And log what's still being held
-                    logSyncHolds();
-                }
-                return;
-            } else {
-                // If this is our first time through the loop, shut down running service threads
-                if (!waiting) {
-                    waiting = true;
-                    stopServiceThreads();
-                }
-                // Wait until a network is connected (or 10 mins), but let the device sleep
-                // We'll set an alarm just in case we don't get notified (bugs happen)
-                synchronized (sConnectivityLock) {
-                    runAsleep(EXTRA_MAILBOX_ID, CONNECTIVITY_WAIT_TIME+5*SECONDS);
-                    try {
-                        log("Connectivity lock...");
-                        sConnectivityHold = true;
-                        sConnectivityLock.wait(CONNECTIVITY_WAIT_TIME);
-                        log("Connectivity lock released...");
-                    } catch (InterruptedException e) {
-                        // This is fine; we just go around the loop again
-                    } finally {
-                        sConnectivityHold = false;
-                    }
-                    runAwake(EXTRA_MAILBOX_ID);
-                }
-            }
-        }
-    }
-
-    /**
-     * Note that there are two ways the EAS ExchangeService service can be created:
-     *
-     * 1) as a background service instantiated via startService (which happens on boot, when the
-     * first EAS account is created, etc), in which case the service thread is spun up, mailboxes
-     * sync, etc. and
-     * 2) to execute an RPC call from the UI, in which case the background service will already be
-     * running most of the time (unless we're creating a first EAS account)
-     *
-     * If the running background service detects that there are no EAS accounts (on boot, if none
-     * were created, or afterward if the last remaining EAS account is deleted), it will call
-     * stopSelf() to terminate operation.
-     *
-     * The goal is to ensure that the background service is running at all times when there is at
-     * least one EAS account in existence
-     *
-     * Because there are edge cases in which our process can crash (typically, this has been seen
-     * in UI crashes, ANR's, etc.), it's possible for the UI to start up again without the
-     * background service having been started.  We explicitly try to start the service in Welcome
-     * (to handle the case of the app having been reloaded).  We also start the service on any
-     * startSync call (if it isn't already running)
-     */
-    @Override
-    public void onCreate() {
-        Utility.runAsync(new Runnable() {
-            @Override
-            public void run() {
-                // Quick checks first, before getting the lock
-                if (sStartingUp) return;
-                synchronized (sSyncLock) {
-                    alwaysLog("!!! EAS ExchangeService, onCreate");
-                    // Try to start up properly; we might be coming back from a crash that the Email
-                    // application isn't aware of.
-                    startService(new Intent(EmailServiceProxy.EXCHANGE_INTENT));
-                    if (sStop) {
-                        return;
-                    }
-                }
-            }});
-    }
-
-    @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        alwaysLog("!!! EAS ExchangeService, onStartCommand, startingUp = " + sStartingUp +
-                ", running = " + (INSTANCE != null));
-        if (!sStartingUp && INSTANCE == null) {
-            sStartingUp = true;
-            Utility.runAsync(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        synchronized (sSyncLock) {
-                            // ExchangeService cannot start unless we can connect to AccountService
-                            if (!new AccountServiceProxy(ExchangeService.this).test()) {
-                                alwaysLog("!!! Email application not found; stopping self");
-                                stopSelf();
-                            }
-                            if (sDeviceId == null) {
-                                try {
-                                    String deviceId = getDeviceId(ExchangeService.this);
-                                    if (deviceId != null) {
-                                        sDeviceId = deviceId;
-                                    }
-                                } catch (IOException e) {
-                                }
-                                if (sDeviceId == null) {
-                                    alwaysLog("!!! deviceId unknown; stopping self and retrying");
-                                    stopSelf();
-                                    // Try to restart ourselves in a few seconds
-                                    Utility.runAsync(new Runnable() {
-                                        @Override
-                                        public void run() {
-                                            try {
-                                                Thread.sleep(5000);
-                                            } catch (InterruptedException e) {
-                                            }
-                                            startService(new Intent(
-                                                    EmailServiceProxy.EXCHANGE_INTENT));
-                                        }});
-                                    return;
-                                }
-                            }
-                            // Run the reconciler and clean up mismatched accounts - if we weren't
-                            // running when accounts were deleted, it won't have been called.
-                            runAccountReconcilerSync(ExchangeService.this);
-                            // Update other services depending on final account configuration
-                            maybeStartExchangeServiceThread();
-                            if (sServiceThread == null) {
-                                log("!!! EAS ExchangeService, stopping self");
-                                stopSelf();
-                            } else if (sStop) {
-                                // If we were trying to stop, attempt a restart in 5 secs
-                                setAlarm(EXCHANGE_SERVICE_MAILBOX_ID, 5*SECONDS);
-                            }
-                        }
-                    } finally {
-                        sStartingUp = false;
-                    }
-                }});
-        }
-        return Service.START_STICKY;
-    }
-
-    @Override
-    public void onDestroy() {
-        log("!!! EAS ExchangeService, onDestroy");
-        // Handle shutting down off the UI thread
-        Utility.runAsync(new Runnable() {
-            @Override
-            public void run() {
-                // Quick checks first, before getting the lock
-                if (INSTANCE == null || sServiceThread == null) return;
-                synchronized(sSyncLock) {
-                    // Stop the sync manager thread and return
-                    if (sServiceThread != null) {
-                        sStop = true;
-                        sServiceThread.interrupt();
-                    }
-                }
-            }});
-    }
-
-    void maybeStartExchangeServiceThread() {
-        // Start our thread...
-        // See if there are any EAS accounts; otherwise, just go away
-        if (sServiceThread == null || !sServiceThread.isAlive()) {
-            if (EmailContent.count(this, HostAuth.CONTENT_URI, WHERE_PROTOCOL_EAS, null) > 0) {
-                log(sServiceThread == null ? "Starting thread..." : "Restarting thread...");
-                sServiceThread = new Thread(this, "ExchangeService");
-                INSTANCE = this;
-                sServiceThread.start();
-            }
-        }
-    }
-
-    /**
-     * Start up the ExchangeService service if it's not already running
-     * This is a stopgap for cases in which ExchangeService died (due to a crash somewhere in
-     * com.android.email) and hasn't been restarted. See the comment for onCreate for details
-     */
-    static void checkExchangeServiceServiceRunning() {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService == null) return;
-        if (sServiceThread == null) {
-            log("!!! checkExchangeServiceServiceRunning; starting service...");
-            exchangeService.startService(new Intent(exchangeService, ExchangeService.class));
-        }
-    }
-
-    public void run() {
-        sStop = false;
-        alwaysLog("ExchangeService thread running");
-        // If we're really debugging, turn on all logging
-        if (Eas.DEBUG) {
-            Eas.USER_LOG = true;
-            Eas.PARSER_LOG = true;
-            Eas.FILE_LOG = true;
-        }
-
-        TempDirectory.setTempDirectory(this);
-
-        // If we need to wait for the debugger, do so
-        if (Eas.WAIT_DEBUG) {
-            Debug.waitForDebugger();
-        }
-
-        // Synchronize here to prevent a shutdown from happening while we initialize our observers
-        // and receivers
-        synchronized (sSyncLock) {
-            if (INSTANCE != null) {
-                mResolver = getContentResolver();
-
-                // Set up our observers; we need them to know when to start/stop various syncs based
-                // on the insert/delete/update of mailboxes and accounts
-                // We also observe synced messages to trigger upsyncs at the appropriate time
-                mAccountObserver = new AccountObserver(mHandler);
-                mResolver.registerContentObserver(Account.NOTIFIER_URI, true, mAccountObserver);
-                mMailboxObserver = new MailboxObserver(mHandler);
-                mResolver.registerContentObserver(Mailbox.CONTENT_URI, false, mMailboxObserver);
-                mSyncedMessageObserver = new SyncedMessageObserver(mHandler);
-                mResolver.registerContentObserver(Message.SYNCED_CONTENT_URI, true,
-                        mSyncedMessageObserver);
-
-                // Set up receivers for connectivity and background data setting
-                mConnectivityReceiver = new ConnectivityReceiver();
-                registerReceiver(mConnectivityReceiver, new IntentFilter(
-                        ConnectivityManager.CONNECTIVITY_ACTION));
-
-                mBackgroundDataSettingReceiver = new ConnectivityReceiver();
-                registerReceiver(mBackgroundDataSettingReceiver, new IntentFilter(
-                        ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED));
-                // Save away the current background data setting; we'll keep track of it with the
-                // receiver we just registered
-                ConnectivityManager cm = (ConnectivityManager)getSystemService(
-                        Context.CONNECTIVITY_SERVICE);
-                mBackgroundData = cm.getBackgroundDataSetting();
-
-                // Do any required work to clean up our Mailboxes (this serves to upgrade
-                // mailboxes that existed prior to EmailProvider database version 17)
-                MailboxUtilities.fixupUninitializedParentKeys(this, getEasAccountSelector());
-            }
-        }
-
-        try {
-            // Loop indefinitely until we're shut down
-            while (!sStop) {
-                runAwake(EXTRA_MAILBOX_ID);
-                waitForConnectivity();
-                mNextWaitReason = null;
-                long nextWait = checkMailboxes();
-                try {
-                    synchronized (this) {
-                        if (!mKicked) {
-                            if (nextWait < 0) {
-                                log("Negative wait? Setting to 1s");
-                                nextWait = 1*SECONDS;
-                            }
-                            if (nextWait > 10*SECONDS) {
-                                if (mNextWaitReason != null) {
-                                    log("Next awake " + nextWait / 1000 + "s: " + mNextWaitReason);
-                                }
-                                runAsleep(EXTRA_MAILBOX_ID, nextWait + (3*SECONDS));
-                            }
-                            wait(nextWait);
-                        }
-                    }
-                } catch (InterruptedException e) {
-                    // Needs to be caught, but causes no problem
-                    log("ExchangeService interrupted");
-                } finally {
-                    synchronized (this) {
-                        if (mKicked) {
-                            //log("Wait deferred due to kick");
-                            mKicked = false;
-                        }
-                    }
-                }
-            }
-            log("Shutdown requested");
-        } catch (ProviderUnavailableException pue) {
-            // Shutdown cleanly in this case
-            // NOTE: Sync adapters will also crash with this error, but that is already handled
-            // in the adapters themselves, i.e. they return cleanly via done().  When the Email
-            // process starts running again, the Exchange process will be started again in due
-            // course, assuming there is at least one existing EAS account.
-            Log.e(TAG, "EmailProvider unavailable; shutting down");
-            // Ask for our service to be restarted; this should kick-start the Email process as well
-            startService(new Intent(this, ExchangeService.class));
-        } catch (RuntimeException e) {
-            // Crash; this is a completely unexpected runtime error
-            Log.e(TAG, "RuntimeException in ExchangeService", e);
-            throw e;
-        } finally {
-            shutdown();
-        }
-    }
-
-    private void shutdown() {
-        synchronized (sSyncLock) {
-            // If INSTANCE is null, we've already been shut down
-            if (INSTANCE != null) {
-                log("ExchangeService shutting down...");
-
-                // Stop our running syncs
-                stopServiceThreads();
-
-                // Stop receivers
-                if (mConnectivityReceiver != null) {
-                    unregisterReceiver(mConnectivityReceiver);
-                }
-                if (mBackgroundDataSettingReceiver != null) {
-                    unregisterReceiver(mBackgroundDataSettingReceiver);
-                }
-
-                // Unregister observers
-                ContentResolver resolver = getContentResolver();
-                if (mSyncedMessageObserver != null) {
-                    resolver.unregisterContentObserver(mSyncedMessageObserver);
-                    mSyncedMessageObserver = null;
-                }
-                if (mAccountObserver != null) {
-                    resolver.unregisterContentObserver(mAccountObserver);
-                    mAccountObserver = null;
-                }
-                if (mMailboxObserver != null) {
-                    resolver.unregisterContentObserver(mMailboxObserver);
-                    mMailboxObserver = null;
-                }
-                unregisterCalendarObservers();
-
-                // Clear pending alarms and associated Intents
-                clearAlarms();
-
-                // Release our wake lock, if we have one
-                synchronized (mWakeLocks) {
-                    if (mWakeLock != null) {
-                        mWakeLock.release();
-                        mWakeLock = null;
-                    }
-                }
-
-                INSTANCE = null;
-                sServiceThread = null;
-                sStop = false;
-                log("Goodbye");
-            }
-        }
-    }
-
-    /**
-     * Release a mailbox from the service map and release its wake lock.
-     * NOTE: This method MUST be called while holding sSyncLock!
-     *
-     * @param mailboxId the id of the mailbox to be released
-     */
-    private void releaseMailbox(long mailboxId) {
-        mServiceMap.remove(mailboxId);
-        releaseWakeLock(mailboxId);
-    }
-
-    /**
-     * Check whether an Outbox (referenced by a Cursor) has any messages that can be sent
-     * @param c the cursor to an Outbox
-     * @return true if there is mail to be sent
-     */
-    private boolean hasSendableMessages(Cursor outboxCursor) {
-        Cursor c = mResolver.query(Message.CONTENT_URI, Message.ID_COLUMN_PROJECTION,
-                EasOutboxService.MAILBOX_KEY_AND_NOT_SEND_FAILED,
-                new String[] {Long.toString(outboxCursor.getLong(Mailbox.CONTENT_ID_COLUMN))},
-                null);
-        try {
-            while (c.moveToNext()) {
-                if (!Utility.hasUnloadedAttachments(this, c.getLong(Message.CONTENT_ID_COLUMN))) {
-                    return true;
-                }
-            }
-        } finally {
-            if (c != null) {
-                c.close();
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Determine whether the account is allowed to sync automatically, as opposed to manually, based
-     * on whether the "require manual sync when roaming" policy is in force and applicable
-     * @param account the account
-     * @return whether or not the account can sync automatically
-     */
-    /*package*/ static boolean canAutoSync(Account account) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService == null) {
-            return false;
-        }
-        NetworkInfo networkInfo = exchangeService.mNetworkInfo;
-
-        // Enforce manual sync only while roaming here
-        long policyKey = account.mPolicyKey;
-        // Quick exit from this check
-        if ((policyKey != 0) && (networkInfo != null) &&
-                (ConnectivityManager.isNetworkTypeMobile(networkInfo.getType()))) {
-            // We'll cache the Policy data here
-            Policy policy = account.mPolicy;
-            if (policy == null) {
-                policy = Policy.restorePolicyWithId(INSTANCE, policyKey);
-                account.mPolicy = policy;
-            }
-            if (policy != null && policy.mRequireManualSyncWhenRoaming && networkInfo.isRoaming()) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Convenience method to determine whether Email sync is enabled for a given account
-     * @param account the Account in question
-     * @return whether Email sync is enabled
-     */
-    private boolean canSyncEmail(android.accounts.Account account) {
-        return ContentResolver.getSyncAutomatically(account, EmailContent.AUTHORITY);
-    }
-
-    /**
-     * Determine whether a mailbox of a given type in a given account can be synced automatically
-     * by ExchangeService.  This is an increasingly complex determination, taking into account
-     * security policies and user settings (both within the Email application and in the Settings
-     * application)
-     *
-     * @param account the Account that the mailbox is in
-     * @param type the type of the Mailbox
-     * @return whether or not to start a sync
-     */
-    private boolean isMailboxSyncable(Account account, int type) {
-        // This 'if' statement performs checks to see whether or not a mailbox is a
-        // candidate for syncing based on policies, user settings, & other restrictions
-        if (type == Mailbox.TYPE_OUTBOX || type == Mailbox.TYPE_EAS_ACCOUNT_MAILBOX) {
-            // Outbox and account mailbox are always syncable
-            return true;
-        } else if (type == Mailbox.TYPE_CONTACTS || type == Mailbox.TYPE_CALENDAR) {
-            // Contacts/Calendar obey this setting from ContentResolver
-            if (!ContentResolver.getMasterSyncAutomatically()) {
-                return false;
-            }
-            // Get the right authority for the mailbox
-            String authority;
-            if (type == Mailbox.TYPE_CONTACTS) {
-                authority = ContactsContract.AUTHORITY;
-            } else {
-                authority = CalendarContract.AUTHORITY;
-                if (!mCalendarObservers.containsKey(account.mId)){
-                    // Make sure we have an observer for this Calendar, as
-                    // we need to be able to detect sync state changes, sigh
-                    registerCalendarObserver(account);
-                }
-            }
-            // See if "sync automatically" is set; if not, punt
-            if (!ContentResolver.getSyncAutomatically(account.mAmAccount, authority)) {
-                return false;
-            // See if the calendar is enabled from the Calendar app UI; if not, punt
-            } else if ((type == Mailbox.TYPE_CALENDAR) && !isCalendarEnabled(account.mId)) {
-                return false;
-            }
-        // Never automatically sync trash
-        } else if (type == Mailbox.TYPE_TRASH) {
-            return false;
-        // For non-outbox, non-account mail, we do three checks:
-        // 1) are we restricted by policy (i.e. manual sync only),
-        // 2) has the user checked the "Sync Email" box in Account Settings, and
-        // 3) does the user have the master "background data" box checked in Settings
-        } else if (!canAutoSync(account) || !canSyncEmail(account.mAmAccount) || !mBackgroundData) {
-            return false;
-        }
-        return true;
-    }
-
-    private long checkMailboxes () {
-        // First, see if any running mailboxes have been deleted
-        ArrayList<Long> deletedMailboxes = new ArrayList<Long>();
-        synchronized (sSyncLock) {
-            for (long mailboxId: mServiceMap.keySet()) {
-                Mailbox m = Mailbox.restoreMailboxWithId(this, mailboxId);
-                if (m == null) {
-                    deletedMailboxes.add(mailboxId);
-                }
-            }
-            // If so, stop them or remove them from the map
-            for (Long mailboxId: deletedMailboxes) {
-                AbstractSyncService svc = mServiceMap.get(mailboxId);
-                if (svc == null || svc.mThread == null) {
-                    releaseMailbox(mailboxId);
-                    continue;
-                } else {
-                    boolean alive = svc.mThread.isAlive();
-                    log("Deleted mailbox: " + svc.mMailboxName);
-                    if (alive) {
-                        stopManualSync(mailboxId);
-                    } else {
-                        log("Removing from serviceMap");
-                        releaseMailbox(mailboxId);
-                    }
-                }
-            }
-        }
-
-        long nextWait = EXCHANGE_SERVICE_HEARTBEAT_TIME;
-        long now = System.currentTimeMillis();
-
-        // Start up threads that need it; use a query which finds eas mailboxes where the
-        // the sync interval is not "never".  This is the set of mailboxes that we control
-        if (mAccountObserver == null) {
-            log("mAccountObserver null; service died??");
-            return nextWait;
-        }
-
-        Cursor c = getContentResolver().query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
-                mAccountObserver.getSyncableEasMailboxWhere(), null, null);
-        if (c == null) throw new ProviderUnavailableException();
-        try {
-            while (c.moveToNext()) {
-                long mailboxId = c.getLong(Mailbox.CONTENT_ID_COLUMN);
-                AbstractSyncService service = null;
-                synchronized (sSyncLock) {
-                    service = mServiceMap.get(mailboxId);
-                }
-                if (service == null) {
-                    // Get the cached account
-                    Account account = getAccountById(c.getInt(Mailbox.CONTENT_ACCOUNT_KEY_COLUMN));
-                    if (account == null) continue;
-
-                    // We handle a few types of mailboxes specially
-                    int mailboxType = c.getInt(Mailbox.CONTENT_TYPE_COLUMN);
-                    if (!isMailboxSyncable(account, mailboxType)) {
-                        continue;
-                    }
-
-                    // Check whether we're in a hold (temporary or permanent)
-                    SyncError syncError = mSyncErrorMap.get(mailboxId);
-                    if (syncError != null) {
-                        // Nothing we can do about fatal errors
-                        if (syncError.fatal) continue;
-                        if (now < syncError.holdEndTime) {
-                            // If release time is earlier than next wait time,
-                            // move next wait time up to the release time
-                            if (syncError.holdEndTime < now + nextWait) {
-                                nextWait = syncError.holdEndTime - now;
-                                mNextWaitReason = "Release hold";
-                            }
-                            continue;
-                        } else {
-                            // Keep the error around, but clear the end time
-                            syncError.holdEndTime = 0;
-                        }
-                    }
-
-                    // Otherwise, we use the sync interval
-                    long syncInterval = c.getInt(Mailbox.CONTENT_SYNC_INTERVAL_COLUMN);
-                    if (syncInterval == Mailbox.CHECK_INTERVAL_PUSH) {
-                        Mailbox m = EmailContent.getContent(c, Mailbox.class);
-                        requestSync(m, SYNC_PUSH, null);
-                    } else if (mailboxType == Mailbox.TYPE_OUTBOX) {
-                        if (hasSendableMessages(c)) {
-                            Mailbox m = EmailContent.getContent(c, Mailbox.class);
-                            startServiceThread(new EasOutboxService(this, m), m);
-                        }
-                    } else if (syncInterval > 0 && syncInterval <= ONE_DAY_MINUTES) {
-                        long lastSync = c.getLong(Mailbox.CONTENT_SYNC_TIME_COLUMN);
-                        long sinceLastSync = now - lastSync;
-                        long toNextSync = syncInterval*MINUTES - sinceLastSync;
-                        String name = c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN);
-                        if (toNextSync <= 0) {
-                            Mailbox m = EmailContent.getContent(c, Mailbox.class);
-                            requestSync(m, SYNC_SCHEDULED, null);
-                        } else if (toNextSync < nextWait) {
-                            nextWait = toNextSync;
-                            if (Eas.USER_LOG) {
-                                log("Next sync for " + name + " in " + nextWait/1000 + "s");
-                            }
-                            mNextWaitReason = "Scheduled sync, " + name;
-                        } else if (Eas.USER_LOG) {
-                            log("Next sync for " + name + " in " + toNextSync/1000 + "s");
-                        }
-                    }
-                } else {
-                    Thread thread = service.mThread;
-                    // Look for threads that have died and remove them from the map
-                    if (thread != null && !thread.isAlive()) {
-                        if (Eas.USER_LOG) {
-                            log("Dead thread, mailbox released: " +
-                                    c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN));
-                        }
-                        releaseMailbox(mailboxId);
-                        // Restart this if necessary
-                        if (nextWait > 3*SECONDS) {
-                            nextWait = 3*SECONDS;
-                            mNextWaitReason = "Clean up dead thread(s)";
-                        }
-                    } else {
-                        long requestTime = service.mRequestTime;
-                        if (requestTime > 0) {
-                            long timeToRequest = requestTime - now;
-                            if (timeToRequest <= 0) {
-                                service.mRequestTime = 0;
-                                service.alarm();
-                            } else if (requestTime > 0 && timeToRequest < nextWait) {
-                                if (timeToRequest < 11*MINUTES) {
-                                    nextWait = timeToRequest < 250 ? 250 : timeToRequest;
-                                    mNextWaitReason = "Sync data change";
-                                } else {
-                                    log("Illegal timeToRequest: " + timeToRequest);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        } finally {
-            c.close();
-        }
-        return nextWait;
-    }
-
-    static public void serviceRequest(long mailboxId, int reason) {
-        serviceRequest(mailboxId, 5*SECONDS, reason);
-    }
-
-    /**
-     * Return a boolean indicating whether the mailbox can be synced
-     * @param m the mailbox
-     * @return whether or not the mailbox can be synced
-     */
-    public static boolean isSyncable(Mailbox m) {
-        return m.loadsFromServer(HostAuth.SCHEME_EAS);
-    }
-
-    static public void serviceRequest(long mailboxId, long ms, int reason) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService == null) return;
-        Mailbox m = Mailbox.restoreMailboxWithId(exchangeService, mailboxId);
-        if (m == null || !isSyncable(m)) return;
-        try {
-            AbstractSyncService service = exchangeService.mServiceMap.get(mailboxId);
-            if (service != null) {
-                service.mRequestTime = System.currentTimeMillis() + ms;
-                kick("service request");
-            } else {
-                startManualSync(mailboxId, reason, null);
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-
-    static public void serviceRequestImmediate(long mailboxId) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService == null) return;
-        AbstractSyncService service = exchangeService.mServiceMap.get(mailboxId);
-        if (service != null) {
-            service.mRequestTime = System.currentTimeMillis();
-            Mailbox m = Mailbox.restoreMailboxWithId(exchangeService, mailboxId);
-            if (m != null) {
-                service.mAccount = Account.restoreAccountWithId(exchangeService, m.mAccountKey);
-                service.mMailbox = m;
-                kick("service request immediate");
-            }
-        }
-    }
-
-    static public void sendMessageRequest(Request req) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService == null) return;
-        Message msg = Message.restoreMessageWithId(exchangeService, req.mMessageId);
-        if (msg == null) {
-            return;
-        }
-        long mailboxId = msg.mMailboxKey;
-        AbstractSyncService service = exchangeService.mServiceMap.get(mailboxId);
-
-        if (service == null) {
-            startManualSync(mailboxId, SYNC_SERVICE_PART_REQUEST, req);
-            kick("part request");
-        } else {
-            service.addRequest(req);
-        }
-    }
-
-    /**
-     * Determine whether a given Mailbox can be synced, i.e. is not already syncing and is not in
-     * an error state
-     *
-     * @param mailboxId
-     * @return whether or not the Mailbox is available for syncing (i.e. is a valid push target)
-     */
-    static public int pingStatus(long mailboxId) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService == null) return PING_STATUS_OK;
-        // Already syncing...
-        if (exchangeService.mServiceMap.get(mailboxId) != null) {
-            return PING_STATUS_RUNNING;
-        }
-        // No errors or a transient error, don't ping...
-        SyncError error = exchangeService.mSyncErrorMap.get(mailboxId);
-        if (error != null) {
-            if (error.fatal) {
-                return PING_STATUS_UNABLE;
-            } else if (error.holdEndTime > 0) {
-                return PING_STATUS_WAITING;
-            }
-        }
-        return PING_STATUS_OK;
-    }
-
-    static public void startManualSync(long mailboxId, int reason, Request req) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService == null) return;
-        synchronized (sSyncLock) {
-            AbstractSyncService svc = exchangeService.mServiceMap.get(mailboxId);
-            if (svc == null) {
-                exchangeService.mSyncErrorMap.remove(mailboxId);
-                Mailbox m = Mailbox.restoreMailboxWithId(exchangeService, mailboxId);
-                if (m != null) {
-                    log("Starting sync for " + m.mDisplayName);
-                    exchangeService.requestSync(m, reason, req);
-                }
-            } else {
-                // If this is a ui request, set the sync reason for the service
-                if (reason >= SYNC_CALLBACK_START) {
-                    svc.mSyncReason = reason;
-                }
-            }
-        }
-    }
-
-    // DO NOT CALL THIS IN A LOOP ON THE SERVICEMAP
-    static public void stopManualSync(long mailboxId) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService == null) return;
-        synchronized (sSyncLock) {
-            AbstractSyncService svc = exchangeService.mServiceMap.get(mailboxId);
-            if (svc != null) {
-                log("Stopping sync for " + svc.mMailboxName);
-                svc.stop();
-                svc.mThread.interrupt();
-                exchangeService.releaseWakeLock(mailboxId);
-            }
-        }
-    }
-
-    /**
-     * Wake up ExchangeService to check for mailboxes needing service
-     */
-    static public void kick(String reason) {
-       ExchangeService exchangeService = INSTANCE;
-       if (exchangeService != null) {
-            synchronized (exchangeService) {
-                //INSTANCE.log("Kick: " + reason);
-                exchangeService.mKicked = true;
-                exchangeService.notify();
-            }
-        }
-        if (sConnectivityLock != null) {
-            synchronized (sConnectivityLock) {
-                sConnectivityLock.notify();
-            }
-        }
-    }
-
-    static public void accountUpdated(long acctId) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService == null) return;
-        synchronized (sSyncLock) {
-            for (AbstractSyncService svc : exchangeService.mServiceMap.values()) {
-                if (svc.mAccount.mId == acctId) {
-                    svc.mAccount = Account.restoreAccountWithId(exchangeService, acctId);
-                }
-            }
-        }
-    }
-
-    /**
-     * Tell ExchangeService to remove the mailbox from the map of mailboxes with sync errors
-     * @param mailboxId the id of the mailbox
-     */
-    static public void removeFromSyncErrorMap(long mailboxId) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService != null) {
-            exchangeService.mSyncErrorMap.remove(mailboxId);
-        }
-    }
-
-    private boolean isRunningInServiceThread(long mailboxId) {
-        AbstractSyncService syncService = mServiceMap.get(mailboxId);
-        Thread thisThread = Thread.currentThread();
-        return syncService != null && syncService.mThread != null &&
-            thisThread == syncService.mThread;
-    }
-
-    /**
-     * Sent by services indicating that their thread is finished; action depends on the exitStatus
-     * of the service.
-     *
-     * @param svc the service that is finished
-     */
-    static public void done(AbstractSyncService svc) {
-        ExchangeService exchangeService = INSTANCE;
-        if (exchangeService == null) return;
-        synchronized(sSyncLock) {
-            long mailboxId = svc.mMailboxId;
-            // If we're no longer the syncing thread for the mailbox, just return
-            if (!exchangeService.isRunningInServiceThread(mailboxId)) {
-                return;
-            }
-            exchangeService.releaseMailbox(mailboxId);
-
-            ConcurrentHashMap<Long, SyncError> errorMap = exchangeService.mSyncErrorMap;
-            SyncError syncError = errorMap.get(mailboxId);
-
-            int exitStatus = svc.mExitStatus;
-            Mailbox m = Mailbox.restoreMailboxWithId(exchangeService, mailboxId);
-            if (m == null) return;
-
-            if (exitStatus != AbstractSyncService.EXIT_LOGIN_FAILURE) {
-                long accountId = m.mAccountKey;
-                Account account = Account.restoreAccountWithId(exchangeService, accountId);
-                if (account == null) return;
-                if (exchangeService.releaseSyncHolds(exchangeService,
-                        AbstractSyncService.EXIT_LOGIN_FAILURE, account)) {
-                    new AccountServiceProxy(exchangeService).notifyLoginSucceeded(accountId);
-                }
-            }
-
-            switch (exitStatus) {
-                case AbstractSyncService.EXIT_DONE:
-                    if (svc.hasPendingRequests()) {
-                        // TODO Handle this case
-                    }
-                    errorMap.remove(mailboxId);
-                    // If we've had a successful sync, clear the shutdown count
-                    synchronized (ExchangeService.class) {
-                        sClientConnectionManagerShutdownCount = 0;
-                    }
-                    break;
-                // I/O errors get retried at increasing intervals
-                case AbstractSyncService.EXIT_IO_ERROR:
-                    if (syncError != null) {
-                        syncError.escalate();
-                        log(m.mDisplayName + " held for " + syncError.holdDelay + "ms");
-                    } else {
-                        errorMap.put(mailboxId, exchangeService.new SyncError(exitStatus, false));
-                        log(m.mDisplayName + " added to syncErrorMap, hold for 15s");
-                    }
-                    break;
-                // These errors are not retried automatically
-                case AbstractSyncService.EXIT_LOGIN_FAILURE:
-                    new AccountServiceProxy(exchangeService).notifyLoginFailed(m.mAccountKey);
-                    // Fall through
-                case AbstractSyncService.EXIT_SECURITY_FAILURE:
-                case AbstractSyncService.EXIT_ACCESS_DENIED:
-                case AbstractSyncService.EXIT_EXCEPTION:
-                    errorMap.put(mailboxId, exchangeService.new SyncError(exitStatus, true));
-                    break;
-            }
-            kick("sync completed");
-        }
-    }
-
-    /**
-     * Given the status string from a Mailbox, return the type code for the last sync
-     * @param status the syncStatus column of a Mailbox
-     * @return
-     */
-    static public int getStatusType(String status) {
-        if (status == null) {
-            return -1;
-        } else {
-            return status.charAt(STATUS_TYPE_CHAR) - '0';
-        }
-    }
-
-    /**
-     * Given the status string from a Mailbox, return the change count for the last sync
-     * The change count is the number of adds + deletes + changes in the last sync
-     * @param status the syncStatus column of a Mailbox
-     * @return
-     */
-    static public int getStatusChangeCount(String status) {
-        try {
-            String s = status.substring(STATUS_CHANGE_COUNT_OFFSET);
-            return Integer.parseInt(s);
-        } catch (RuntimeException e) {
-            return -1;
-        }
-    }
-
-    static public Context getContext() {
-        return INSTANCE;
-    }
-}
diff --git a/src/com/android/exchange/IllegalHeartbeatException.java b/src/com/android/exchange/IllegalHeartbeatException.java
deleted file mode 100644
index e480aa7..0000000
--- a/src/com/android/exchange/IllegalHeartbeatException.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- * 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;
-
-public class IllegalHeartbeatException extends EasException {
-    private static final long serialVersionUID = 1L;
-    public final int mLegalHeartbeat;
-
-    public IllegalHeartbeatException(int legalHeartbeat) {
-        mLegalHeartbeat = legalHeartbeat;
-    }
-}
diff --git a/src/com/android/exchange/MailboxAlarmReceiver.java b/src/com/android/exchange/MailboxAlarmReceiver.java
deleted file mode 100644
index 7958c03..0000000
--- a/src/com/android/exchange/MailboxAlarmReceiver.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- *  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;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-/**
- * MailboxAlarmReceiver is used to "wake up" the ExchangeService at the appropriate time(s).  It may
- * also be used for individual sync adapters, but this isn't implemented at the present time.
- *
- */
-public class MailboxAlarmReceiver extends BroadcastReceiver {
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        long mailboxId = intent.getLongExtra("mailbox", ExchangeService.EXTRA_MAILBOX_ID);
-        // EXCHANGE_SERVICE_MAILBOX_ID tells us that the service is asking to be started
-        if (mailboxId == ExchangeService.EXCHANGE_SERVICE_MAILBOX_ID) {
-            context.startService(new Intent(context, ExchangeService.class));
-        } else {
-            ExchangeService.alert(context, mailboxId);
-        }
-    }
-}
-
diff --git a/src/com/android/exchange/MeetingResponseRequest.java b/src/com/android/exchange/MeetingResponseRequest.java
deleted file mode 100644
index ea769e2..0000000
--- a/src/com/android/exchange/MeetingResponseRequest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-/**
- * MeetingResponseRequest is the EAS wrapper for responding to meeting requests.
- */
-public class MeetingResponseRequest extends Request {
-    public final int mResponse;
-
-    MeetingResponseRequest(long messageId, int response) {
-        super(messageId);
-        mResponse = response;
-    }
-
-    // MeetingResponseRequests are unique by their message id (i.e. there's only one response to
-    // a given message)
-    public boolean equals(Object o) {
-        if (!(o instanceof MeetingResponseRequest)) return false;
-        return ((MeetingResponseRequest)o).mMessageId == mMessageId;
-    }
-
-    public int hashCode() {
-        return (int)mMessageId;
-    }
-}
diff --git a/src/com/android/exchange/MessageMoveRequest.java b/src/com/android/exchange/MessageMoveRequest.java
deleted file mode 100644
index a884bda..0000000
--- a/src/com/android/exchange/MessageMoveRequest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-/**
- * MessageMoveRequest is the EAS wrapper for requesting a "move to folder"
- */
-public class MessageMoveRequest extends Request {
-    public final long mMailboxId;
-
-    public MessageMoveRequest(long messageId, long mailboxId) {
-        super(messageId);
-        mMailboxId = mailboxId;
-    }
-
-    // MessageMoveRequests are unique by their message id (i.e. it's meaningless to have two
-    // separate message moves queued at the same time)
-    public boolean equals(Object o) {
-        if (!(o instanceof MessageMoveRequest)) return false;
-        return ((MessageMoveRequest)o).mMessageId == mMessageId;
-    }
-
-    public int hashCode() {
-        return (int)mMessageId;
-    }
-}
diff --git a/src/com/android/exchange/MockParserStream.java b/src/com/android/exchange/MockParserStream.java
deleted file mode 100644
index a518667..0000000
--- a/src/com/android/exchange/MockParserStream.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * MockParserStream is an InputStream that feeds pre-generated data into various EasParser
- * subclasses.
- * 
- * After parsing is done, the result can be obtained with getResult
- *
- */
-public class MockParserStream extends InputStream {
-    int[] array;
-    int pos = 0;
-    Object value;
-
-    MockParserStream (int[] _array) {
-        array = _array;
-    }
-
-    @Override
-    public int read() throws IOException {
-        try {
-            return array[pos++];
-        } catch (IndexOutOfBoundsException e) {
-            throw new IOException("End of stream");
-        }
-    }
-
-    public void setResult(Object _value) {
-        value = _value;
-    }
-
-    public Object getResult() {
-        return value;
-    }
-}
diff --git a/src/com/android/exchange/PartRequest.java b/src/com/android/exchange/PartRequest.java
deleted file mode 100644
index 23b4add..0000000
--- a/src/com/android/exchange/PartRequest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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;
-
-import com.android.emailcommon.provider.EmailContent.Attachment;
-
-/**
- * PartRequest is the EAS wrapper for attachment loading requests.  In addition to information about
- * the attachment to be loaded, it also contains the callback to be used for status/progress
- * updates to the UI.
- */
-public class PartRequest extends Request {
-    public final Attachment mAttachment;
-    public final String mDestination;
-    public final String mContentUriString;
-    public final String mLocation;
-
-    public PartRequest(Attachment _att, String _destination, String _contentUriString) {
-        super(_att.mMessageKey);
-        mAttachment = _att;
-        mLocation = mAttachment.mLocation;
-        mDestination = _destination;
-        mContentUriString = _contentUriString;
-    }
-
-    // PartRequests are unique by their attachment id (i.e. multiple attachments might be queued
-    // for a particular message, but any individual attachment can only be loaded once)
-    public boolean equals(Object o) {
-        if (!(o instanceof PartRequest)) return false;
-        return ((PartRequest)o).mAttachment.mId == mAttachment.mId;
-    }
-
-    public int hashCode() {
-        return (int)mAttachment.mId;
-    }
-}
diff --git a/src/com/android/exchange/Request.java b/src/com/android/exchange/Request.java
deleted file mode 100644
index 280a084..0000000
--- a/src/com/android/exchange/Request.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-/**
- * Requests for mailbox actions are handled by subclasses of this abstract class.
- * Three subclasses are now defined: PartRequest (attachment load), MeetingResponseRequest
- * (respond to a meeting invitation), and MessageMoveRequest (move a message to another folder)
- */
-public abstract class Request {
-    public final long mTimeStamp = System.currentTimeMillis();
-    public final long mMessageId;
-
-    public Request(long messageId) {
-        mMessageId = messageId;
-    }
-
-    // Subclasses of Request may have different semantics regarding equality; therefore,
-    // we force them to implement the equals method
-    public abstract boolean equals(Object o);
-    public abstract int hashCode();
-}
diff --git a/src/com/android/exchange/SecurityPolicyDelegate.java b/src/com/android/exchange/SecurityPolicyDelegate.java
deleted file mode 100644
index 5025c8f..0000000
--- a/src/com/android/exchange/SecurityPolicyDelegate.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-import com.android.emailcommon.provider.Account;
-import com.android.emailcommon.provider.Policy;
-import com.android.emailcommon.service.PolicyServiceProxy;
-
-import android.content.Context;
-import android.os.RemoteException;
-
-public class SecurityPolicyDelegate {
-
-    public static boolean isActive(Context context, Policy policy) {
-        try {
-            return new PolicyServiceProxy(context).isActive(policy);
-        } catch (RemoteException e) {
-        }
-        return false;
-    }
-
-    public static void policiesRequired(Context context, long accountId) {
-        try {
-            new PolicyServiceProxy(context).policiesRequired(accountId);
-        } catch (RemoteException e) {
-            throw new IllegalStateException("PolicyService transaction failed");
-        }
-    }
-
-    public static void policiesUpdated(Context context, long accountId) {
-        try {
-            new PolicyServiceProxy(context).policiesUpdated(accountId);
-        } catch (RemoteException e) {
-            throw new IllegalStateException("PolicyService transaction failed");
-        }
-    }
-
-    public static void setAccountHoldFlag(Context context, Account account, boolean newState) {
-        try {
-            new PolicyServiceProxy(context).setAccountHoldFlag(account.mId, newState);
-        } catch (RemoteException e) {
-            throw new IllegalStateException("PolicyService transaction failed");
-        }
-    }
-
-    public static boolean isActiveAdmin(Context context) {
-        try {
-            return new PolicyServiceProxy(context).isActiveAdmin();
-        } catch (RemoteException e) {
-        }
-        return false;
-    }
-
-    public static void remoteWipe(Context context) {
-        try {
-            new PolicyServiceProxy(context).remoteWipe();
-        } catch (RemoteException e) {
-            throw new IllegalStateException("PolicyService transaction failed");
-        }
-    }
-
-    public static boolean isSupported(Context context, Policy policy) {
-        try {
-            return new PolicyServiceProxy(context).isSupported(policy);
-        } catch (RemoteException e) {
-        }
-        return false;
-     }
-
-    public static Policy clearUnsupportedPolicies(Context context, Policy policy) {
-        try {
-            return new PolicyServiceProxy(context).clearUnsupportedPolicies(policy);
-        } catch (RemoteException e) {
-        }
-        throw new IllegalStateException("PolicyService transaction failed");
-    }
-}
diff --git a/src/com/android/exchange/SettingsRedirector.java b/src/com/android/exchange/SettingsRedirector.java
deleted file mode 100644
index 5a50825..0000000
--- a/src/com/android/exchange/SettingsRedirector.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.android.emailcommon.utility.IntentUtilities;
-
-/**
- * An empty {@link Activity} that simply redirects to the proper settings editor for the Email
- * application.
- * This is needed since the Exchange service runs as a separate UID and is therefore tracked as
- * a separate entity in the framework for things such as data usage. Links from those places to
- * Exchange should really go to Email where the real settings are.
- */
-public class SettingsRedirector extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        Intent intent = getIntent();
-        Intent redirect = new Intent(
-                Intent.ACTION_EDIT,
-                IntentUtilities.createActivityIntentUrlBuilder("settings").build());
-        redirect.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
-        startActivity(redirect);
-        finish();
-    }
-}
diff --git a/src/com/android/exchange/StaleFolderListException.java b/src/com/android/exchange/StaleFolderListException.java
deleted file mode 100644
index 70ac032..0000000
--- a/src/com/android/exchange/StaleFolderListException.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- *  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;
-
-public class StaleFolderListException extends EasException {
-    private static final long serialVersionUID = 1L;
-}
diff --git a/src/com/android/exchange/adapter/AbstractSyncAdapter.java b/src/com/android/exchange/adapter/AbstractSyncAdapter.java
deleted file mode 100644
index 52400c4..0000000
--- a/src/com/android/exchange/adapter/AbstractSyncAdapter.java
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * 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 com.android.emailcommon.provider.Account;
-import com.android.emailcommon.provider.Mailbox;
-import com.android.exchange.CommandStatusException;
-import com.android.exchange.Eas;
-import com.android.exchange.EasSyncService;
-import com.google.common.annotations.VisibleForTesting;
-
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.OperationApplicationException;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.os.TransactionTooLargeException;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-
-/**
- * Parent class of all sync adapters (EasMailbox, EasCalendar, and EasContacts)
- *
- */
-public abstract class AbstractSyncAdapter {
-
-    public static final int SECONDS = 1000;
-    public static final int MINUTES = SECONDS*60;
-    public static final int HOURS = MINUTES*60;
-    public static final int DAYS = HOURS*24;
-    public static final int WEEKS = DAYS*7;
-
-    protected static final String PIM_WINDOW_SIZE = "4";
-
-    private static final long SEPARATOR_ID = Long.MAX_VALUE;
-
-    public Mailbox mMailbox;
-    public EasSyncService mService;
-    public Context mContext;
-    public Account mAccount;
-    public final ContentResolver mContentResolver;
-    public final android.accounts.Account mAccountManagerAccount;
-
-    // Create the data for local changes that need to be sent up to the server
-    public abstract boolean sendLocalChanges(Serializer s) throws IOException;
-    // Parse incoming data from the EAS server, creating, modifying, and deleting objects as
-    // required through the EmailProvider
-    public abstract boolean parse(InputStream is) throws IOException, CommandStatusException;
-    // The name used to specify the collection type of the target (Email, Calendar, or Contacts)
-    public abstract String getCollectionName();
-    public abstract void cleanup();
-    public abstract boolean isSyncable();
-    // Add sync options (filter, body type - html vs plain, and truncation)
-    public abstract void sendSyncOptions(Double protocolVersion, Serializer s) throws IOException;
-    /**
-     * Delete all records of this class in this account
-     */
-    public abstract void wipe();
-
-    public boolean isLooping() {
-        return false;
-    }
-
-    public AbstractSyncAdapter(EasSyncService service) {
-        mService = service;
-        mMailbox = service.mMailbox;
-        mContext = service.mContext;
-        mAccount = service.mAccount;
-        mAccountManagerAccount = new android.accounts.Account(mAccount.mEmailAddress,
-                Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE);
-        mContentResolver = mContext.getContentResolver();
-    }
-
-    public void userLog(String ...strings) {
-        mService.userLog(strings);
-    }
-
-    public void incrementChangeCount() {
-        mService.mChangeCount++;
-    }
-
-    /**
-     * Set sync options common to PIM's (contacts and calendar)
-     * @param protocolVersion the protocol version under which we're syncing
-     * @param the filter to use (or null)
-     * @param s the Serializer
-     * @throws IOException
-     */
-    protected void setPimSyncOptions(Double protocolVersion, String filter, Serializer s)
-            throws IOException {
-        s.tag(Tags.SYNC_DELETES_AS_MOVES);
-        s.tag(Tags.SYNC_GET_CHANGES);
-        s.data(Tags.SYNC_WINDOW_SIZE, PIM_WINDOW_SIZE);
-        s.start(Tags.SYNC_OPTIONS);
-        // Set the filter (lookback), if provided
-        if (filter != null) {
-            s.data(Tags.SYNC_FILTER_TYPE, filter);
-        }
-        // Set the truncation amount and body type
-        if (protocolVersion >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) {
-            s.start(Tags.BASE_BODY_PREFERENCE);
-            // Plain text
-            s.data(Tags.BASE_TYPE, Eas.BODY_PREFERENCE_TEXT);
-            s.data(Tags.BASE_TRUNCATION_SIZE, Eas.EAS12_TRUNCATION_SIZE);
-            s.end();
-        } else {
-            s.data(Tags.SYNC_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE);
-        }
-        s.end();
-    }
-
-    /**
-     * Returns the current SyncKey; override if the SyncKey is stored elsewhere (as for Contacts)
-     * @return the current SyncKey for the Mailbox
-     * @throws IOException
-     */
-    public String getSyncKey() throws IOException {
-        if (mMailbox.mSyncKey == null) {
-            userLog("Reset SyncKey to 0");
-            mMailbox.mSyncKey = "0";
-        }
-        return mMailbox.mSyncKey;
-    }
-
-    public void setSyncKey(String syncKey, boolean inCommands) throws IOException {
-        mMailbox.mSyncKey = syncKey;
-    }
-
-    /**
-     * Operation is our binder-safe ContentProviderOperation (CPO) construct; an Operation can
-     * be created from a CPO, a CPO Builder, or a CPO Builder with a "back reference" column name
-     * and offset (that might be used in Builder.withValueBackReference).  The CPO is not actually
-     * built until it is ready to be executed (with applyBatch); this allows us to recalculate
-     * back reference offsets if we are required to re-send a large batch in smaller chunks.
-     *
-     * NOTE: A failed binder transaction is something of an emergency case, and shouldn't happen
-     * with any frequency.  When it does, and we are forced to re-send the data to the content
-     * provider in smaller chunks, we DO lose the sync-window atomicity, and thereby add another
-     * small risk to the data.  Of course, this is far, far better than dropping the data on the
-     * floor, as was done before the framework implemented TransactionTooLargeException
-     */
-    protected static class Operation {
-        final ContentProviderOperation mOp;
-        final ContentProviderOperation.Builder mBuilder;
-        final String mColumnName;
-        final int mOffset;
-        // Is this Operation a separator? (a good place to break up a large transaction)
-        boolean mSeparator = false;
-
-        // For toString()
-        final String[] TYPES = new String[] {"???", "Ins", "Upd", "Del", "Assert"};
-
-        Operation(ContentProviderOperation.Builder builder, String columnName, int offset) {
-            mOp = null;
-            mBuilder = builder;
-            mColumnName = columnName;
-            mOffset = offset;
-        }
-
-        Operation(ContentProviderOperation.Builder builder) {
-            mOp = null;
-            mBuilder = builder;
-            mColumnName = null;
-            mOffset = 0;
-        }
-
-        Operation(ContentProviderOperation op) {
-            mOp = op;
-            mBuilder = null;
-            mColumnName = null;
-            mOffset = 0;
-        }
-
-        public String toString() {
-            StringBuilder sb = new StringBuilder("Op: ");
-            ContentProviderOperation op = operationToContentProviderOperation(this, 0);
-            int type = 0;
-            //DO NOT SHIP WITH THE FOLLOWING LINE (the API is hidden!)
-            //type = op.getType();
-            sb.append(TYPES[type]);
-            Uri uri = op.getUri();
-            sb.append(' ');
-            sb.append(uri.getPath());
-            if (mColumnName != null) {
-                sb.append(" Back value of " + mColumnName + ": " + mOffset);
-            }
-            return sb.toString();
-        }
-    }
-
-    /**
-     * We apply the batch of CPO's here.  We synchronize on the service to avoid thread-nasties,
-     * and we just return quickly if the service has already been stopped.
-     */
-    private ContentProviderResult[] execute(String authority,
-            ArrayList<ContentProviderOperation> ops)
-            throws RemoteException, OperationApplicationException {
-        synchronized (mService.getSynchronizer()) {
-            if (!mService.isStopped()) {
-                if (!ops.isEmpty()) {
-                    ContentProviderResult[] result = mContentResolver.applyBatch(authority, ops);
-                    mService.userLog("Results: " + result.length);
-                    return result;
-                }
-            }
-        }
-        return new ContentProviderResult[0];
-    }
-
-    /**
-     * Convert an Operation to a CPO; if the Operation has a back reference, apply it with the
-     * passed-in offset
-     */
-    @VisibleForTesting
-    static ContentProviderOperation operationToContentProviderOperation(Operation op, int offset) {
-        if (op.mOp != null) {
-            return op.mOp;
-        } else if (op.mBuilder == null) {
-            throw new IllegalArgumentException("Operation must have CPO.Builder");
-        }
-        ContentProviderOperation.Builder builder = op.mBuilder;
-        if (op.mColumnName != null) {
-            builder.withValueBackReference(op.mColumnName, op.mOffset - offset);
-        }
-        return builder.build();
-    }
-
-    /**
-     * Create a list of CPOs from a list of Operations, and then apply them in a batch
-     */
-    private ContentProviderResult[] applyBatch(String authority, ArrayList<Operation> ops,
-            int offset) throws RemoteException, OperationApplicationException {
-        // Handle the empty case
-        if (ops.isEmpty()) {
-            return new ContentProviderResult[0];
-        }
-        ArrayList<ContentProviderOperation> cpos = new ArrayList<ContentProviderOperation>();
-        for (Operation op: ops) {
-            cpos.add(operationToContentProviderOperation(op, offset));
-        }
-        return execute(authority, cpos);
-    }
-
-    /**
-     * Apply the list of CPO's in the provider and copy the "mini" result into our full result array
-     */
-    private void applyAndCopyResults(String authority, ArrayList<Operation> mini,
-            ContentProviderResult[] result, int offset) throws RemoteException {
-        // Empty lists are ok; we just ignore them
-        if (mini.isEmpty()) return;
-        try {
-            ContentProviderResult[] miniResult = applyBatch(authority, mini, offset);
-            // Copy the results from this mini-batch into our results array
-            System.arraycopy(miniResult, 0, result, offset, miniResult.length);
-        } catch (OperationApplicationException e) {
-            // Not possible since we're building the ops ourselves
-        }
-    }
-
-    /**
-     * Called by a sync adapter to execute a list of Operations in the ContentProvider handling
-     * the passed-in authority.  If the attempt to apply the batch fails due to a too-large
-     * binder transaction, we split the Operations as directed by separators.  If any of the
-     * "mini" batches fails due to a too-large transaction, we're screwed, but this would be
-     * vanishingly rare.  Other, possibly transient, errors are handled by throwing a
-     * RemoteException, which the caller will likely re-throw as an IOException so that the sync
-     * can be attempted again.
-     *
-     * Callers MAY leave a dangling separator at the end of the list; note that the separators
-     * themselves are only markers and are not sent to the provider.
-     */
-    protected ContentProviderResult[] safeExecute(String authority, ArrayList<Operation> ops)
-            throws RemoteException {
-        mService.userLog("Try to execute ", ops.size(), " CPO's for " + authority);
-        ContentProviderResult[] result = null;
-        try {
-            // Try to execute the whole thing
-            return applyBatch(authority, ops, 0);
-        } catch (TransactionTooLargeException e) {
-            // Nope; split into smaller chunks, demarcated by the separator operation
-            mService.userLog("Transaction too large; spliting!");
-            ArrayList<Operation> mini = new ArrayList<Operation>();
-            // Build a result array with the total size we're sending
-            result = new ContentProviderResult[ops.size()];
-            int count = 0;
-            int offset = 0;
-            for (Operation op: ops) {
-                if (op.mSeparator) {
-                    try {
-                        mService.userLog("Try mini-batch of ", mini.size(), " CPO's");
-                        applyAndCopyResults(authority, mini, result, offset);
-                        mini.clear();
-                        // Save away the offset here; this will need to be subtracted out of the
-                        // value originally set by the adapter
-                        offset = count + 1; // Remember to add 1 for the separator!
-                    } catch (TransactionTooLargeException e1) {
-                        throw new RuntimeException("Can't send transaction; sync stopped.");
-                    } catch (RemoteException e1) {
-                        throw e1;
-                    }
-                } else {
-                    mini.add(op);
-                }
-                count++;
-            }
-            // Check out what's left; if it's more than just a separator, apply the batch
-            int miniSize = mini.size();
-            if ((miniSize > 0) && !(miniSize == 1 && mini.get(0).mSeparator)) {
-                applyAndCopyResults(authority, mini, result, offset);
-            }
-        } catch (RemoteException e) {
-            throw e;
-        } catch (OperationApplicationException e) {
-            // Not possible since we're building the ops ourselves
-        }
-        return result;
-    }
-
-    /**
-     * Called by a sync adapter to indicate a relatively safe place to split a batch of CPO's
-     */
-    protected void addSeparatorOperation(ArrayList<Operation> ops, Uri uri) {
-        Operation op = new Operation(
-                ContentProviderOperation.newDelete(ContentUris.withAppendedId(uri, SEPARATOR_ID)));
-        op.mSeparator = true;
-        ops.add(op);
-    }
-}
-
diff --git a/src/com/android/exchange/adapter/AbstractSyncParser.java b/src/com/android/exchange/adapter/AbstractSyncParser.java
deleted file mode 100644
index 1162b66..0000000
--- a/src/com/android/exchange/adapter/AbstractSyncParser.java
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * 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.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-
-import com.android.emailcommon.provider.Account;
-import com.android.emailcommon.provider.EmailContent.MailboxColumns;
-import com.android.emailcommon.provider.Mailbox;
-import com.android.exchange.CommandStatusException;
-import com.android.exchange.CommandStatusException.CommandStatus;
-import com.android.exchange.EasSyncService;
-import com.android.exchange.ExchangeService;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Base class for the Email and PIM sync parsers
- * Handles the basic flow of syncKeys, looping to get more data, handling errors, etc.
- * Each subclass must implement a handful of methods that relate specifically to the data type
- *
- */
-public abstract class AbstractSyncParser extends Parser {
-
-    protected EasSyncService mService;
-    protected Mailbox mMailbox;
-    protected Account mAccount;
-    protected Context mContext;
-    protected ContentResolver mContentResolver;
-    protected AbstractSyncAdapter mAdapter;
-
-    private boolean mLooping;
-
-    public AbstractSyncParser(InputStream in, AbstractSyncAdapter adapter) throws IOException {
-        super(in);
-        init(adapter);
-    }
-
-    public AbstractSyncParser(Parser p, AbstractSyncAdapter adapter) throws IOException {
-        super(p);
-        init(adapter);
-    }
-
-    private void init(AbstractSyncAdapter adapter) {
-        mAdapter = adapter;
-        mService = adapter.mService;
-        mContext = mService.mContext;
-        mContentResolver = mContext.getContentResolver();
-        mMailbox = mService.mMailbox;
-        mAccount = mService.mAccount;
-    }
-
-    /**
-     * Read, parse, and act on incoming commands from the Exchange server
-     * @throws IOException if the connection is broken
-     * @throws CommandStatusException
-     */
-    public abstract void commandsParser() throws IOException, CommandStatusException;
-
-    /**
-     * Read, parse, and act on server responses
-     * @throws IOException
-     */
-    public abstract void responsesParser() throws IOException;
-
-    /**
-     * Commit any changes found during parsing
-     * @throws IOException
-     */
-    public abstract void commit() throws IOException;
-
-    public boolean isLooping() {
-        return mLooping;
-    }
-
-    /**
-     * Skip through tags until we reach the specified end tag
-     * @param endTag the tag we end with
-     * @throws IOException
-     */
-    public void skipParser(int endTag) throws IOException {
-        while (nextTag(endTag) != END) {
-            skipTag();
-        }
-    }
-
-    /**
-     * Loop through the top-level structure coming from the Exchange server
-     * Sync keys and the more available flag are handled here, whereas specific data parsing
-     * is handled by abstract methods implemented for each data class (e.g. Email, Contacts, etc.)
-     * @throws CommandStatusException
-     */
-    @Override
-    public boolean parse() throws IOException, CommandStatusException {
-        int status;
-        boolean moreAvailable = false;
-        boolean newSyncKey = false;
-        int interval = mMailbox.mSyncInterval;
-        mLooping = false;
-        // If we're not at the top of the xml tree, throw an exception
-        if (nextTag(START_DOCUMENT) != Tags.SYNC_SYNC) {
-            throw new EasParserException();
-        }
-
-        boolean mailboxUpdated = false;
-        ContentValues cv = new ContentValues();
-
-        // Loop here through the remaining xml
-        while (nextTag(START_DOCUMENT) != END_DOCUMENT) {
-            if (tag == Tags.SYNC_COLLECTION || tag == Tags.SYNC_COLLECTIONS) {
-                // Ignore these tags, since we've only got one collection syncing in this loop
-            } else if (tag == Tags.SYNC_STATUS) {
-                // Status = 1 is success; everything else is a failure
-                status = getValueInt();
-                if (status != 1) {
-                    mService.errorLog("Sync failed: " + CommandStatus.toString(status));
-                    if (status == 3 || CommandStatus.isBadSyncKey(status)) {
-                        // Must delete all of the data and start over with syncKey of "0"
-                        mAdapter.setSyncKey("0", false);
-                        // Make this a push box through the first sync
-                        // TODO Make frequency conditional on user settings!
-                        mMailbox.mSyncInterval = Mailbox.CHECK_INTERVAL_PUSH;
-                        mService.errorLog("Bad sync key; RESET and delete data");
-                        mAdapter.wipe();
-                        // Indicate there's more so that we'll start syncing again
-                        moreAvailable = true;
-                    } else if (status == 16 || status == 5) {
-                        // Status 16 indicates a transient server error (indeterminate state)
-                        // Status 5 indicates "server error"; this tends to loop for a while so
-                        // throwing IOException will at least provide backoff behavior
-                        throw new IOException();
-                    } else if (status == 8 || status == 12) {
-                        // Status 8 is Bad; it means the server doesn't recognize the serverId it
-                        // sent us.  12 means that we're being asked to refresh the folder list.
-                        // We'll do that with 8 also...
-                        ExchangeService.reloadFolderList(mContext, mAccount.mId, true);
-                        // We don't have any provision for telling the user "wait a minute while
-                        // we sync folders"...
-                        throw new IOException();
-                    } else if (status == 7) {
-                        mService.mUpsyncFailed = true;
-                        moreAvailable = true;
-                    } else {
-                        // Access, provisioning, transient, etc.
-                        throw new CommandStatusException(status);
-                    }
-                }
-            } else if (tag == Tags.SYNC_COMMANDS) {
-                commandsParser();
-            } else if (tag == Tags.SYNC_RESPONSES) {
-                responsesParser();
-            } else if (tag == Tags.SYNC_MORE_AVAILABLE) {
-                moreAvailable = true;
-            } else if (tag == Tags.SYNC_SYNC_KEY) {
-                if (mAdapter.getSyncKey().equals("0")) {
-                    moreAvailable = true;
-                }
-                String newKey = getValue();
-                userLog("Parsed key for ", mMailbox.mDisplayName, ": ", newKey);
-                if (!newKey.equals(mMailbox.mSyncKey)) {
-                    mAdapter.setSyncKey(newKey, true);
-                    cv.put(MailboxColumns.SYNC_KEY, newKey);
-                    mailboxUpdated = true;
-                    newSyncKey = true;
-                }
-                // If we were pushing (i.e. auto-start), now we'll become ping-triggered
-                if (mMailbox.mSyncInterval == Mailbox.CHECK_INTERVAL_PUSH) {
-                    mMailbox.mSyncInterval = Mailbox.CHECK_INTERVAL_PING;
-                }
-           } else {
-                skipTag();
-           }
-        }
-
-        // If we don't have a new sync key, ignore moreAvailable (or we'll loop)
-        if (moreAvailable && !newSyncKey) {
-            mLooping = true;
-        }
-
-        // Commit any changes
-        commit();
-
-        boolean abortSyncs = false;
-
-        // If the sync interval has changed, we need to save it
-        if (mMailbox.mSyncInterval != interval) {
-            cv.put(MailboxColumns.SYNC_INTERVAL, mMailbox.mSyncInterval);
-            mailboxUpdated = true;
-        // If there are changes, and we were bounced from push/ping, try again
-        } else if (mService.mChangeCount > 0 &&
-                mAccount.mSyncInterval == Account.CHECK_INTERVAL_PUSH &&
-                mMailbox.mSyncInterval > 0) {
-            userLog("Changes found to ping loop mailbox ", mMailbox.mDisplayName, ": will ping.");
-            cv.put(MailboxColumns.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PING);
-            mailboxUpdated = true;
-            abortSyncs = true;
-        }
-
-        if (mailboxUpdated) {
-             synchronized (mService.getSynchronizer()) {
-                if (!mService.isStopped()) {
-                     mMailbox.update(mContext, cv);
-                }
-            }
-        }
-
-        if (abortSyncs) {
-            userLog("Aborting account syncs due to mailbox change to ping...");
-            ExchangeService.stopAccountSyncs(mAccount.mId);
-        }
-
-        // Let the caller know that there's more to do
-        if (moreAvailable) {
-            userLog("MoreAvailable");
-        }
-        return moreAvailable;
-    }
-
-    void userLog(String ...strings) {
-        mService.userLog(strings);
-    }
-
-    void userLog(String string, int num, String string2) {
-        mService.userLog(string, num, string2);
-    }
-}
diff --git a/src/com/android/exchange/adapter/AccountSyncAdapter.java b/src/com/android/exchange/adapter/AccountSyncAdapter.java
deleted file mode 100644
index cf54ed5..0000000
--- a/src/com/android/exchange/adapter/AccountSyncAdapter.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.android.exchange.adapter;
-
-import com.android.exchange.EasSyncService;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-public class AccountSyncAdapter extends AbstractSyncAdapter {
-
-    public AccountSyncAdapter(EasSyncService service) {
-        super(service);
-     }
-
-    @Override
-    public void cleanup() {
-    }
-
-    @Override
-    public void wipe() {
-    }
-
-    @Override
-    public String getCollectionName() {
-        return null;
-    }
-
-    @Override
-    public boolean parse(InputStream is) throws IOException {
-        return false;
-    }
-
-    @Override
-    public boolean sendLocalChanges(Serializer s) throws IOException {
-        return false;
-    }
-
-    @Override
-    public boolean isSyncable() {
-        return true;
-    }
-
-    @Override
-    public void sendSyncOptions(Double protocolVersion, Serializer s) throws IOException {
-    }
-}
diff --git a/src/com/android/exchange/adapter/AttachmentLoader.java b/src/com/android/exchange/adapter/AttachmentLoader.java
deleted file mode 100644
index 4d14934..0000000
--- a/src/com/android/exchange/adapter/AttachmentLoader.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/* Copyright (C) 2011 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.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.net.Uri;
-import android.os.RemoteException;
-
-import com.android.emailcommon.provider.EmailContent.Attachment;
-import com.android.emailcommon.provider.EmailContent.AttachmentColumns;
-import com.android.emailcommon.provider.EmailContent.Message;
-import com.android.emailcommon.service.EmailServiceStatus;
-import com.android.emailcommon.utility.AttachmentUtilities;
-import com.android.exchange.Eas;
-import com.android.exchange.EasResponse;
-import com.android.exchange.EasSyncService;
-import com.android.exchange.ExchangeService;
-import com.android.exchange.PartRequest;
-import com.android.exchange.utility.UriCodec;
-import com.google.common.annotations.VisibleForTesting;
-
-import org.apache.http.HttpStatus;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * Handle EAS attachment loading, regardless of protocol version
- */
-public class AttachmentLoader {
-    static private final int CHUNK_SIZE = 16*1024;
-
-    private final EasSyncService mService;
-    private final Context mContext;
-    private final ContentResolver mResolver;
-    private final Attachment mAttachment;
-    private final long mAttachmentId;
-    private final int mAttachmentSize;
-    private final long mMessageId;
-    private final Message mMessage;
-    private final long mAccountId;
-    private final Uri mAttachmentUri;
-
-    public AttachmentLoader(EasSyncService service, PartRequest req) {
-        mService = service;
-        mContext = service.mContext;
-        mResolver = service.mContentResolver;
-        mAttachment = req.mAttachment;
-        mAttachmentId = mAttachment.mId;
-        mAttachmentSize = (int)mAttachment.mSize;
-        mAccountId = mAttachment.mAccountKey;
-        mMessageId = mAttachment.mMessageKey;
-        mMessage = Message.restoreMessageWithId(mContext, mMessageId);
-        mAttachmentUri = AttachmentUtilities.getAttachmentUri(mAccountId, mAttachmentId);
-    }
-
-    private void doStatusCallback(int status) {
-        try {
-            ExchangeService.callback().loadAttachmentStatus(mMessageId, mAttachmentId, status, 0);
-        } catch (RemoteException e) {
-            // No danger if the client is no longer around
-        }
-    }
-
-    private void doProgressCallback(int progress) {
-        try {
-            ExchangeService.callback().loadAttachmentStatus(mMessageId, mAttachmentId,
-                    EmailServiceStatus.IN_PROGRESS, progress);
-        } catch (RemoteException e) {
-            // No danger if the client is no longer around
-        }
-    }
-
-    /**
-     * Save away the contentUri for this Attachment and notify listeners
-     */
-    private void finishLoadAttachment() {
-        ContentValues cv = new ContentValues();
-        cv.put(AttachmentColumns.CONTENT_URI, mAttachmentUri.toString());
-        mAttachment.update(mContext, cv);
-        doStatusCallback(EmailServiceStatus.SUCCESS);
-    }
-
-    /**
-     * Read the attachment data in chunks and write the data back out to our attachment file
-     * @param inputStream the InputStream we're reading the attachment from
-     * @param outputStream the OutputStream the attachment will be written to
-     * @param len the number of expected bytes we're going to read
-     * @throws IOException
-     */
-    public void readChunked(InputStream inputStream, OutputStream outputStream, int len)
-            throws IOException {
-        byte[] bytes = new byte[CHUNK_SIZE];
-        int length = len;
-        // Loop terminates 1) when EOF is reached or 2) IOException occurs
-        // One of these is guaranteed to occur
-        int totalRead = 0;
-        int lastCallbackPct = -1;
-        int lastCallbackTotalRead = 0;
-        mService.userLog("Expected attachment length: ", len);
-        while (true) {
-            int read = inputStream.read(bytes, 0, CHUNK_SIZE);
-            if (read < 0) {
-                // -1 means EOF
-                mService.userLog("Attachment load reached EOF, totalRead: ", totalRead);
-                break;
-            }
-
-            // Keep track of how much we've read for progress callback
-            totalRead += read;
-            // Write these bytes out
-            outputStream.write(bytes, 0, read);
-
-            // We can't report percentage if data is chunked; the length of incoming data is unknown
-            if (length > 0) {
-                int pct = (totalRead * 100) / length;
-                // Callback only if we've read at least 1% more and have read more than CHUNK_SIZE
-                // We don't want to spam the Email app
-                if ((pct > lastCallbackPct) && (totalRead > (lastCallbackTotalRead + CHUNK_SIZE))) {
-                    // Report progress back to the UI
-                    doProgressCallback(pct);
-                    lastCallbackTotalRead = totalRead;
-                    lastCallbackPct = pct;
-                }
-            }
-        }
-        if (totalRead > length) {
-            // Apparently, the length, as reported by EAS, isn't always accurate; let's log it
-            mService.userLog("Read more than expected: ", totalRead);
-        }
-    }
-
-    @VisibleForTesting
-    static String encodeForExchange2003(String str) {
-        AttachmentNameEncoder enc = new AttachmentNameEncoder();
-        StringBuilder sb = new StringBuilder(str.length() + 16);
-        enc.appendPartiallyEncoded(sb, str);
-        return sb.toString();
-    }
-
-    /**
-     * Encoder for Exchange 2003 attachment names.  They come from the server partially encoded,
-     * but there are still possible characters that need to be encoded (Why, MSFT, why?)
-     */
-    private static class AttachmentNameEncoder extends UriCodec {
-        @Override protected boolean isRetained(char c) {
-            // These four characters are commonly received in EAS 2.5 attachment names and are
-            // valid (verified by testing); we won't encode them
-            return c == '_' || c == ':' || c == '/' || c == '.';
-        }
-    }
-
-    /**
-     * Loads an attachment, based on the PartRequest passed in the constructor
-     * @throws IOException
-     */
-    public void loadAttachment() throws IOException {
-        if (mMessage == null) {
-            doStatusCallback(EmailServiceStatus.MESSAGE_NOT_FOUND);
-            return;
-        }
-        // Say we've started loading the attachment
-        doProgressCallback(0);
-
-        EasResponse resp;
-        boolean eas14 = mService.mProtocolVersionDouble >= Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE;
-        // The method of attachment loading is different in EAS 14.0 than in earlier versions
-        if (eas14) {
-            Serializer s = new Serializer();
-            s.start(Tags.ITEMS_ITEMS).start(Tags.ITEMS_FETCH);
-            s.data(Tags.ITEMS_STORE, "Mailbox");
-            s.data(Tags.BASE_FILE_REFERENCE, mAttachment.mLocation);
-            s.end().end().done(); // ITEMS_FETCH, ITEMS_ITEMS
-            resp = mService.sendHttpClientPost("ItemOperations", s.toByteArray());
-        } else {
-            String location = mAttachment.mLocation;
-            // For Exchange 2003 (EAS 2.5), we have to look for illegal characters in the file name
-            // that EAS sent to us!
-            if (mService.mProtocolVersionDouble < Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) {
-                location = encodeForExchange2003(location);
-            }
-            String cmd = "GetAttachment&AttachmentName=" + location;
-            resp = mService.sendHttpClientPost(cmd, null, EasSyncService.COMMAND_TIMEOUT);
-        }
-
-        try {
-            int status = resp.getStatus();
-            if (status == HttpStatus.SC_OK) {
-                if (!resp.isEmpty()) {
-                    InputStream is = resp.getInputStream();
-                    OutputStream os = null;
-                    try {
-                        os = mResolver.openOutputStream(mAttachmentUri);
-                        if (eas14) {
-                            ItemOperationsParser p = new ItemOperationsParser(this, is, os,
-                                    mAttachmentSize);
-                            p.parse();
-                            if (p.getStatusCode() == 1 /* Success */) {
-                                finishLoadAttachment();
-                                return;
-                            }
-                        } else {
-                            int len = resp.getLength();
-                            if (len != 0) {
-                                // len > 0 means that Content-Length was set in the headers
-                                // len < 0 means "chunked" transfer-encoding
-                                readChunked(is, os, (len < 0) ? mAttachmentSize : len);
-                                finishLoadAttachment();
-                                return;
-                            }
-                        }
-                    } catch (FileNotFoundException e) {
-                        mService.errorLog("Can't get attachment; write file not found?");
-                    } finally {
-                        if (os != null) {
-                            os.flush();
-                            os.close();
-                        }
-                    }
-                }
-            }
-        } finally {
-            resp.close();
-        }
-
-        // All errors lead here...
-        doStatusCallback(EmailServiceStatus.ATTACHMENT_NOT_FOUND);
-    }
-}
diff --git a/src/com/android/exchange/adapter/Base64InputStream.java b/src/com/android/exchange/adapter/Base64InputStream.java
deleted file mode 100644
index 5b78c4c..0000000
--- a/src/com/android/exchange/adapter/Base64InputStream.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/****************************************************************

- * Licensed to the Apache Software Foundation (ASF) under one   *

- * or more contributor license agreements.  See the NOTICE file *

- * distributed with this work for additional information        *

- * regarding copyright ownership.  The ASF licenses this file   *

- * to you 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.                                           *

- ****************************************************************/

-

-/**

- * Modified for Exchange attachment decoding 5/12/11.  Changes are bracketed with START EAS CHANGES

- * and END EAS CHANGES

- *

- * Without the included changes, the final bytes of the input stream will be read here and thrown

- * away; in that case, the WBXML parser will lose information necessary to determine that the

- * entire stream has been processed correctly.  Since inline WBXML text is terminated with a zero

- * byte, and since zero is not valid Base64, we terminate reading when we find a zero byte, leaving

- * the remainder of the stream untouched (to be read by the Parser that created the

- * Base64InputStream.

- */

-

-package com.android.exchange.adapter;

-

-import java.io.IOException;

-import java.io.InputStream;

-

-/**

- * Performs Base-64 decoding on an underlying stream.

- *

- *

- * @version $Id: Base64InputStream.java,v 1.3 2004/11/29 13:15:47 ntherning Exp $

- */

-public class Base64InputStream extends InputStream {

-    private final InputStream s;

-    private int outCount = 0;

-    private int outIndex = 0;

-    private final int[] outputBuffer = new int[3];

-    private final byte[] inputBuffer = new byte[4];

-    private boolean done = false;

-    // START EAS CHANGES

-    private boolean padSeen = false;

-    // END EAS CHANGES

-

-    public Base64InputStream(InputStream s) {

-        this.s = s;

-    }

-

-    /**

-     * Closes the underlying stream.

-     *

-     * @throws IOException on I/O errors.

-     */

-    @Override

-    public void close() throws IOException {

-        s.close();

-    }

-

-    @Override

-    public int read() throws IOException {

-        if (outIndex == outCount) {

-            fillBuffer();

-            if (outIndex == outCount) {

-                return -1;

-            }

-        }

-

-        return outputBuffer[outIndex++];

-    }

-

-    /**

-     * Retrieve data from the underlying stream, decode it,

-     * and put the results in the byteq.

-     * @throws IOException

-     */

-    private void fillBuffer() throws IOException {

-        outCount = 0;

-        outIndex = 0;

-        int inCount = 0;

-

-        int i;

-        // "done" is needed for the two successive '=' at the end

-        while (!done) {

-            switch (i = s.read()) {

-                // START EAS CHANGES

-                case 0:

-                    // In EAS, a zero will indicate the end of the (inline) base64 string

-                    // Stop reading at this point, so that we can continue with WBXML

-                    done = true;

-                    return;

-                // END EAS CHANGES

-                case -1:

-                    // No more input - just return, let outputBuffer drain out, and be done

-                    return;

-                case '=':

-                    // START EAS CHANGES

-                    // Allow for a second '=' before we're really done (we should get a zero next)

-                    if (padSeen) {

-                        return;

-                    }

-                    // We've seen a (first) end padding character, flush what's in the buffer

-                    padSeen = true;

-                    // END EAS CHANGES

-                    // PRE-EAS LINE COMMENTED OUT

-                    //done = true;

-                    decodeAndEnqueue(inCount);

-                    return;

-                default:

-                    byte sX = TRANSLATION[i];

-                    if (sX < 0) continue;

-                    inputBuffer[inCount++] = sX;

-                    if (inCount == 4) {

-                        decodeAndEnqueue(inCount);

-                        return;

-                    }

-                    break;

-            }

-        }

-    }

-

-    private void decodeAndEnqueue(int len) {

-        int accum = 0;

-        accum |= inputBuffer[0] << 18;

-        accum |= inputBuffer[1] << 12;

-        accum |= inputBuffer[2] << 6;

-        accum |= inputBuffer[3];

-

-        // There's a bit of duplicated code here because we want to have straight-through operation

-        // for the most common case of len==4

-        if (len == 4) {

-            outputBuffer[0] = (accum >> 16) & 0xFF;

-            outputBuffer[1] = (accum >> 8) & 0xFF;

-            outputBuffer[2] = (accum) & 0xFF;

-            outCount = 3;

-            return;

-        } else if (len == 3) {

-            outputBuffer[0] = (accum >> 16) & 0xFF;

-            outputBuffer[1] = (accum >> 8) & 0xFF;

-            outCount = 2;

-            return;

-        } else {    // len == 2

-            outputBuffer[0] = (accum >> 16) & 0xFF;

-            outCount = 1;

-            return;

-        }

-    }

-

-    private static byte[] TRANSLATION = {

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 */

-        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 */

-        -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, /* 0x40 */

-        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 */

-        -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 */

-        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 0x70 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xA0 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB0 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC0 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD0 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xE0 */

-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1  /* 0xF0 */

-    };

-}

diff --git a/src/com/android/exchange/adapter/CalendarSyncAdapter.java b/src/com/android/exchange/adapter/CalendarSyncAdapter.java
deleted file mode 100644
index c8ab766..0000000
--- a/src/com/android/exchange/adapter/CalendarSyncAdapter.java
+++ /dev/null
@@ -1,2163 +0,0 @@
-/*
- * 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.ContentProviderClient;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Entity;
-import android.content.Entity.NamedContentValues;
-import android.content.EntityIterator;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.provider.CalendarContract;
-import android.provider.CalendarContract.Attendees;
-import android.provider.CalendarContract.Calendars;
-import android.provider.CalendarContract.Events;
-import android.provider.CalendarContract.EventsEntity;
-import android.provider.CalendarContract.ExtendedProperties;
-import android.provider.CalendarContract.Reminders;
-import android.provider.CalendarContract.SyncState;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.SyncStateContract;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.emailcommon.AccountManagerTypes;
-import com.android.emailcommon.provider.EmailContent;
-import com.android.emailcommon.provider.EmailContent.Message;
-import com.android.emailcommon.utility.Utility;
-import com.android.exchange.CommandStatusException;
-import com.android.exchange.Eas;
-import com.android.exchange.EasOutboxService;
-import com.android.exchange.EasSyncService;
-import com.android.exchange.ExchangeService;
-import com.android.exchange.utility.CalendarUtilities;
-import com.android.exchange.utility.Duration;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.GregorianCalendar;
-import java.util.Map.Entry;
-import java.util.StringTokenizer;
-import java.util.TimeZone;
-import java.util.UUID;
-
-/**
- * Sync adapter class for EAS calendars
- *
- */
-public class CalendarSyncAdapter extends AbstractSyncAdapter {
-
-    private static final String TAG = "EasCalendarSyncAdapter";
-
-    private static final String EVENT_SAVED_TIMEZONE_COLUMN = Events.SYNC_DATA1;
-    /**
-     * Used to keep track of exception vs parent event dirtiness.
-     */
-    private static final String EVENT_SYNC_MARK = Events.SYNC_DATA8;
-    private static final String EVENT_SYNC_VERSION = Events.SYNC_DATA4;
-    // Since exceptions will have the same _SYNC_ID as the original event we have to check that
-    // there's no original event when finding an item by _SYNC_ID
-    private static final String SERVER_ID_AND_CALENDAR_ID = Events._SYNC_ID + "=? AND " +
-        Events.ORIGINAL_SYNC_ID + " ISNULL AND " + Events.CALENDAR_ID + "=?";
-    private static final String EVENT_ID_AND_CALENDAR_ID = Events._ID + "=? AND " +
-        Events.ORIGINAL_SYNC_ID + " ISNULL AND " + Events.CALENDAR_ID + "=?";
-    private static final String DIRTY_OR_MARKED_TOP_LEVEL_IN_CALENDAR = "(" + Events.DIRTY
-            + "=1 OR " + EVENT_SYNC_MARK + "= 1) AND " +
-        Events.ORIGINAL_ID + " ISNULL AND " + Events.CALENDAR_ID + "=?";
-    private static final String DIRTY_EXCEPTION_IN_CALENDAR =
-        Events.DIRTY + "=1 AND " + Events.ORIGINAL_ID + " NOTNULL AND " +
-        Events.CALENDAR_ID + "=?";
-    private static final String CLIENT_ID_SELECTION = Events.SYNC_DATA2 + "=?";
-    private static final String ORIGINAL_EVENT_AND_CALENDAR =
-        Events.ORIGINAL_SYNC_ID + "=? AND " + Events.CALENDAR_ID + "=?";
-    private static final String ATTENDEES_EXCEPT_ORGANIZER = Attendees.EVENT_ID + "=? AND " +
-        Attendees.ATTENDEE_RELATIONSHIP + "!=" + Attendees.RELATIONSHIP_ORGANIZER;
-    private static final String[] ID_PROJECTION = new String[] {Events._ID};
-    private static final String[] ORIGINAL_EVENT_PROJECTION =
-        new String[] {Events.ORIGINAL_ID, Events._ID};
-    private static final String EVENT_ID_AND_NAME =
-        ExtendedProperties.EVENT_ID + "=? AND " + ExtendedProperties.NAME + "=?";
-
-    // Note that we use LIKE below for its case insensitivity
-    private static final String EVENT_AND_EMAIL  =
-        Attendees.EVENT_ID + "=? AND "+ Attendees.ATTENDEE_EMAIL + " LIKE ?";
-    private static final int ATTENDEE_STATUS_COLUMN_STATUS = 0;
-    private static final String[] ATTENDEE_STATUS_PROJECTION =
-        new String[] {Attendees.ATTENDEE_STATUS};
-
-    public static final String CALENDAR_SELECTION =
-        Calendars.ACCOUNT_NAME + "=? AND " + Calendars.ACCOUNT_TYPE + "=?";
-    private static final int CALENDAR_SELECTION_ID = 0;
-
-    private static final String[] EXTENDED_PROPERTY_PROJECTION =
-        new String[] {ExtendedProperties._ID};
-    private static final int EXTENDED_PROPERTY_ID = 0;
-
-    private static final String CATEGORY_TOKENIZER_DELIMITER = "\\";
-    private static final String ATTENDEE_TOKENIZER_DELIMITER = CATEGORY_TOKENIZER_DELIMITER;
-
-    private static final String EXTENDED_PROPERTY_USER_ATTENDEE_STATUS = "userAttendeeStatus";
-    private static final String EXTENDED_PROPERTY_ATTENDEES = "attendees";
-    private static final String EXTENDED_PROPERTY_DTSTAMP = "dtstamp";
-    private static final String EXTENDED_PROPERTY_MEETING_STATUS = "meeting_status";
-    private static final String EXTENDED_PROPERTY_CATEGORIES = "categories";
-    // Used to indicate that we removed the attendee list because it was too large
-    private static final String EXTENDED_PROPERTY_ATTENDEES_REDACTED = "attendeesRedacted";
-    // Used to indicate that upsyncs aren't allowed (we catch this in sendLocalChanges)
-    private static final String EXTENDED_PROPERTY_UPSYNC_PROHIBITED = "upsyncProhibited";
-
-    private static final Operation PLACEHOLDER_OPERATION =
-        new Operation(ContentProviderOperation.newInsert(Uri.EMPTY));
-
-    private static final Object sSyncKeyLock = new Object();
-
-    private static final TimeZone UTC_TIMEZONE = TimeZone.getTimeZone("UTC");
-    private final TimeZone mLocalTimeZone = TimeZone.getDefault();
-
-
-    // Maximum number of allowed attendees; above this number, we mark the Event with the
-    // attendeesRedacted extended property and don't allow the event to be upsynced to the server
-    private static final int MAX_SYNCED_ATTENDEES = 50;
-    // We set the organizer to this when the user is the organizer and we've redacted the
-    // attendee list.  By making the meeting organizer OTHER than the user, we cause the UI to
-    // prevent edits to this event (except local changes like reminder).
-    private static final String BOGUS_ORGANIZER_EMAIL = "upload_disallowed@uploadisdisallowed.aaa";
-    // Maximum number of CPO's before we start redacting attendees in exceptions
-    // The number 500 has been determined empirically; 1500 CPOs appears to be the limit before
-    // binder failures occur, but we need room at any point for additional events/exceptions so
-    // we set our limit at 1/3 of the apparent maximum for extra safety
-    // TODO Find a better solution to this workaround
-    private static final int MAX_OPS_BEFORE_EXCEPTION_ATTENDEE_REDACTION = 500;
-
-    private long mCalendarId = -1;
-    private String mCalendarIdString;
-    private String[] mCalendarIdArgument;
-    /*package*/ String mEmailAddress;
-
-    private ArrayList<Long> mDeletedIdList = new ArrayList<Long>();
-    private ArrayList<Long> mUploadedIdList = new ArrayList<Long>();
-    private ArrayList<Long> mSendCancelIdList = new ArrayList<Long>();
-    private ArrayList<Message> mOutgoingMailList = new ArrayList<Message>();
-
-    private final Uri mAsSyncAdapterAttendees;
-    private final Uri mAsSyncAdapterEvents;
-    private final Uri mAsSyncAdapterReminders;
-    private final Uri mAsSyncAdapterExtendedProperties;
-
-    public CalendarSyncAdapter(EasSyncService service) {
-        super(service);
-        mEmailAddress = mAccount.mEmailAddress;
-
-        String amType = Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE;
-        mAsSyncAdapterAttendees =
-                asSyncAdapter(Attendees.CONTENT_URI, mEmailAddress, amType);
-        mAsSyncAdapterEvents =
-                asSyncAdapter(Events.CONTENT_URI, mEmailAddress, amType);
-        mAsSyncAdapterReminders =
-                asSyncAdapter(Reminders.CONTENT_URI, mEmailAddress, amType);
-        mAsSyncAdapterExtendedProperties =
-                asSyncAdapter(ExtendedProperties.CONTENT_URI, mEmailAddress, amType);
-
-        Cursor c = mService.mContentResolver.query(Calendars.CONTENT_URI,
-                new String[] {Calendars._ID}, CALENDAR_SELECTION,
-                new String[] {mEmailAddress, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE}, null);
-        if (c == null) return;
-        try {
-            if (c.moveToFirst()) {
-                mCalendarId = c.getLong(CALENDAR_SELECTION_ID);
-            } else {
-                mCalendarId = CalendarUtilities.createCalendar(mService, mAccount, mMailbox);
-            }
-            mCalendarIdString = Long.toString(mCalendarId);
-            mCalendarIdArgument = new String[] {mCalendarIdString};
-        } finally {
-            c.close();
-        }
-        }
-
-    @Override
-    public String getCollectionName() {
-        return "Calendar";
-    }
-
-    @Override
-    public void cleanup() {
-    }
-
-    @Override
-    public void wipe() {
-        // Delete the calendar associated with this account
-        // CalendarProvider2 does NOT handle selection arguments in deletions
-        mContentResolver.delete(
-                asSyncAdapter(Calendars.CONTENT_URI, mEmailAddress,
-                        Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE),
-                Calendars.ACCOUNT_NAME + "=" + DatabaseUtils.sqlEscapeString(mEmailAddress)
-                        + " AND " + Calendars.ACCOUNT_TYPE + "="
-                        + DatabaseUtils.sqlEscapeString(AccountManagerTypes.TYPE_EXCHANGE), null);
-        // Invalidate our calendar observers
-        ExchangeService.unregisterCalendarObservers();
-    }
-
-    @Override
-    public void sendSyncOptions(Double protocolVersion, Serializer s) throws IOException  {
-        setPimSyncOptions(protocolVersion, Eas.FILTER_2_WEEKS, s);
-    }
-
-    @Override
-    public boolean isSyncable() {
-        return ContentResolver.getSyncAutomatically(mAccountManagerAccount,
-                CalendarContract.AUTHORITY);
-    }
-
-    @Override
-    public boolean parse(InputStream is) throws IOException, CommandStatusException {
-        EasCalendarSyncParser p = new EasCalendarSyncParser(is, this);
-        return p.parse();
-    }
-
-    public static Uri asSyncAdapter(Uri uri, String account, String accountType) {
-        return uri.buildUpon().appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
-                .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
-                .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
-    }
-
-    /**
-     * Generate the uri for the data row associated with this NamedContentValues object
-     * @param ncv the NamedContentValues object
-     * @return a uri that can be used to refer to this row
-     */
-    public Uri dataUriFromNamedContentValues(NamedContentValues ncv) {
-        long id = ncv.values.getAsLong(RawContacts._ID);
-        Uri dataUri = ContentUris.withAppendedId(ncv.uri, id);
-        return dataUri;
-    }
-
-    /**
-     * We get our SyncKey from CalendarProvider.  If there's not one, we set it to "0" (the reset
-     * state) and save that away.
-     */
-    @Override
-    public String getSyncKey() throws IOException {
-        synchronized (sSyncKeyLock) {
-            ContentProviderClient client = mService.mContentResolver
-                    .acquireContentProviderClient(CalendarContract.CONTENT_URI);
-            try {
-                byte[] data = SyncStateContract.Helpers.get(
-                        client,
-                        asSyncAdapter(SyncState.CONTENT_URI, mEmailAddress,
-                                Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), mAccountManagerAccount);
-                if (data == null || data.length == 0) {
-                    // Initialize the SyncKey
-                    setSyncKey("0", false);
-                    return "0";
-                } else {
-                    String syncKey = new String(data);
-                    userLog("SyncKey retrieved as ", syncKey, " from CalendarProvider");
-                    return syncKey;
-                }
-            } catch (RemoteException e) {
-                throw new IOException("Can't get SyncKey from CalendarProvider");
-            }
-        }
-    }
-
-    /**
-     * We only need to set this when we're forced to make the SyncKey "0" (a reset).  In all other
-     * cases, the SyncKey is set within Calendar
-     */
-    @Override
-    public void setSyncKey(String syncKey, boolean inCommands) throws IOException {
-        synchronized (sSyncKeyLock) {
-            if ("0".equals(syncKey) || !inCommands) {
-                ContentProviderClient client = mService.mContentResolver
-                        .acquireContentProviderClient(CalendarContract.CONTENT_URI);
-                try {
-                    SyncStateContract.Helpers.set(
-                            client,
-                            asSyncAdapter(SyncState.CONTENT_URI, mEmailAddress,
-                                    Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), mAccountManagerAccount,
-                            syncKey.getBytes());
-                    userLog("SyncKey set to ", syncKey, " in CalendarProvider");
-                } catch (RemoteException e) {
-                    throw new IOException("Can't set SyncKey in CalendarProvider");
-                }
-            }
-            mMailbox.mSyncKey = syncKey;
-        }
-    }
-
-    public class EasCalendarSyncParser extends AbstractSyncParser {
-
-        String[] mBindArgument = new String[1];
-        Uri mAccountUri;
-        CalendarOperations mOps = new CalendarOperations();
-
-        public EasCalendarSyncParser(InputStream in, CalendarSyncAdapter adapter)
-                throws IOException {
-            super(in, adapter);
-            setLoggingTag("CalendarParser");
-            mAccountUri = Events.CONTENT_URI;
-        }
-
-        private void addOrganizerToAttendees(CalendarOperations ops, long eventId,
-                String organizerName, String organizerEmail) {
-            // Handle the organizer (who IS an attendee on device, but NOT in EAS)
-            if (organizerName != null || organizerEmail != null) {
-                ContentValues attendeeCv = new ContentValues();
-                if (organizerName != null) {
-                    attendeeCv.put(Attendees.ATTENDEE_NAME, organizerName);
-                }
-                if (organizerEmail != null) {
-                    attendeeCv.put(Attendees.ATTENDEE_EMAIL, organizerEmail);
-                }
-                attendeeCv.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ORGANIZER);
-                attendeeCv.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_REQUIRED);
-                attendeeCv.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_ACCEPTED);
-                if (eventId < 0) {
-                    ops.newAttendee(attendeeCv);
-                } else {
-                    ops.updatedAttendee(attendeeCv, eventId);
-                }
-            }
-        }
-
-        /**
-         * Set DTSTART, DTEND, DURATION and EVENT_TIMEZONE as appropriate for the given Event
-         * The follow rules are enforced by CalendarProvider2:
-         *   Events that aren't exceptions MUST have either 1) a DTEND or 2) a DURATION
-         *   Recurring events (i.e. events with RRULE) must have a DURATION
-         *   All-day recurring events MUST have a DURATION that is in the form P<n>D
-         *   Other events MAY have a DURATION in any valid form (we use P<n>M)
-         *   All-day events MUST have hour, minute, and second = 0; in addition, they must have
-         *   the EVENT_TIMEZONE set to UTC
-         *   Also, exceptions to all-day events need to have an ORIGINAL_INSTANCE_TIME that has
-         *   hour, minute, and second = 0 and be set in UTC
-         * @param cv the ContentValues for the Event
-         * @param startTime the start time for the Event
-         * @param endTime the end time for the Event
-         * @param allDayEvent whether this is an all day event (1) or not (0)
-         */
-        /*package*/ void setTimeRelatedValues(ContentValues cv, long startTime, long endTime,
-                int allDayEvent) {
-            // If there's no startTime, the event will be found to be invalid, so return
-            if (startTime < 0) return;
-            // EAS events can arrive without an end time, but CalendarProvider requires them
-            // so we'll default to 30 minutes; this will be superceded if this is an all-day event
-            if (endTime < 0) endTime = startTime + (30*MINUTES);
-
-            // If this is an all-day event, set hour, minute, and second to zero, and use UTC
-            if (allDayEvent != 0) {
-                startTime = CalendarUtilities.getUtcAllDayCalendarTime(startTime, mLocalTimeZone);
-                endTime = CalendarUtilities.getUtcAllDayCalendarTime(endTime, mLocalTimeZone);
-                String originalTimeZone = cv.getAsString(Events.EVENT_TIMEZONE);
-                cv.put(EVENT_SAVED_TIMEZONE_COLUMN, originalTimeZone);
-                cv.put(Events.EVENT_TIMEZONE, UTC_TIMEZONE.getID());
-            }
-
-            // If this is an exception, and the original was an all-day event, make sure the
-            // original instance time has hour, minute, and second set to zero, and is in UTC
-            if (cv.containsKey(Events.ORIGINAL_INSTANCE_TIME) &&
-                    cv.containsKey(Events.ORIGINAL_ALL_DAY)) {
-                Integer ade = cv.getAsInteger(Events.ORIGINAL_ALL_DAY);
-                if (ade != null && ade != 0) {
-                    long exceptionTime = cv.getAsLong(Events.ORIGINAL_INSTANCE_TIME);
-                    GregorianCalendar cal = new GregorianCalendar(UTC_TIMEZONE);
-                    cal.setTimeInMillis(exceptionTime);
-                    cal.set(GregorianCalendar.HOUR_OF_DAY, 0);
-                    cal.set(GregorianCalendar.MINUTE, 0);
-                    cal.set(GregorianCalendar.SECOND, 0);
-                    cv.put(Events.ORIGINAL_INSTANCE_TIME, cal.getTimeInMillis());
-                }
-            }
-
-            // Always set DTSTART
-            cv.put(Events.DTSTART, startTime);
-            // For recurring events, set DURATION.  Use P<n>D format for all day events
-            if (cv.containsKey(Events.RRULE)) {
-                if (allDayEvent != 0) {
-                    cv.put(Events.DURATION, "P" + ((endTime - startTime) / DAYS) + "D");
-                }
-                else {
-                    cv.put(Events.DURATION, "P" + ((endTime - startTime) / MINUTES) + "M");
-                }
-            // For other events, set DTEND and LAST_DATE
-            } else {
-                cv.put(Events.DTEND, endTime);
-                cv.put(Events.LAST_DATE, endTime);
-            }
-        }
-
-        public void addEvent(CalendarOperations ops, String serverId, boolean update)
-                throws IOException {
-            ContentValues cv = new ContentValues();
-            cv.put(Events.CALENDAR_ID, mCalendarId);
-            cv.put(Events._SYNC_ID, serverId);
-            cv.put(Events.HAS_ATTENDEE_DATA, 1);
-            cv.put(Events.SYNC_DATA2, "0");
-
-            int allDayEvent = 0;
-            String organizerName = null;
-            String organizerEmail = null;
-            int eventOffset = -1;
-            int deleteOffset = -1;
-            int busyStatus = CalendarUtilities.BUSY_STATUS_TENTATIVE;
-            int responseType = CalendarUtilities.RESPONSE_TYPE_NONE;
-
-            boolean firstTag = true;
-            long eventId = -1;
-            long startTime = -1;
-            long endTime = -1;
-            TimeZone timeZone = null;
-
-            // Keep track of the attendees; exceptions will need them
-            ArrayList<ContentValues> attendeeValues = new ArrayList<ContentValues>();
-            int reminderMins = -1;
-            String dtStamp = null;
-            boolean organizerAdded = false;
-
-            while (nextTag(Tags.SYNC_APPLICATION_DATA) != END) {
-                if (update && firstTag) {
-                    // Find the event that's being updated
-                    Cursor c = getServerIdCursor(serverId);
-                    long id = -1;
-                    try {
-                        if (c != null && c.moveToFirst()) {
-                            id = c.getLong(0);
-                        }
-                    } finally {
-                        if (c != null) c.close();
-                    }
-                    if (id > 0) {
-                        // DTSTAMP can come first, and we simply need to track it
-                        if (tag == Tags.CALENDAR_DTSTAMP) {
-                            dtStamp = getValue();
-                            continue;
-                        } else if (tag == Tags.CALENDAR_ATTENDEES) {
-                            // This is an attendees-only update; just
-                            // delete/re-add attendees
-                            mBindArgument[0] = Long.toString(id);
-                            ops.add(new Operation(ContentProviderOperation
-                                    .newDelete(mAsSyncAdapterAttendees)
-                                    .withSelection(ATTENDEES_EXCEPT_ORGANIZER, mBindArgument)));
-                            eventId = id;
-                        } else {
-                            // Otherwise, delete the original event and recreate it
-                            userLog("Changing (delete/add) event ", serverId);
-                            deleteOffset = ops.newDelete(id, serverId);
-                            // Add a placeholder event so that associated tables can reference
-                            // this as a back reference.  We add the event at the end of the method
-                            eventOffset = ops.newEvent(PLACEHOLDER_OPERATION);
-                        }
-                    } else {
-                        // The changed item isn't found. We'll treat this as a new item
-                        eventOffset = ops.newEvent(PLACEHOLDER_OPERATION);
-                        userLog(TAG, "Changed item not found; treating as new.");
-                    }
-                } else if (firstTag) {
-                    // Add a placeholder event so that associated tables can reference
-                    // this as a back reference.  We add the event at the end of the method
-                   eventOffset = ops.newEvent(PLACEHOLDER_OPERATION);
-                }
-                firstTag = false;
-                switch (tag) {
-                    case Tags.CALENDAR_ALL_DAY_EVENT:
-                        allDayEvent = getValueInt();
-                        if (allDayEvent != 0 && timeZone != null) {
-                            // If the event doesn't start at midnight local time, we won't consider
-                            // this an all-day event in the local time zone (this is what OWA does)
-                            GregorianCalendar cal = new GregorianCalendar(mLocalTimeZone);
-                            cal.setTimeInMillis(startTime);
-                            userLog("All-day event arrived in: " + timeZone.getID());
-                            if (cal.get(GregorianCalendar.HOUR_OF_DAY) != 0 ||
-                                    cal.get(GregorianCalendar.MINUTE) != 0) {
-                                allDayEvent = 0;
-                                userLog("Not an all-day event locally: " + mLocalTimeZone.getID());
-                            }
-                        }
-                        cv.put(Events.ALL_DAY, allDayEvent);
-                        break;
-                    case Tags.CALENDAR_ATTACHMENTS:
-                        attachmentsParser();
-                        break;
-                    case Tags.CALENDAR_ATTENDEES:
-                        // If eventId >= 0, this is an update; otherwise, a new Event
-                        attendeeValues = attendeesParser(ops, eventId);
-                        break;
-                    case Tags.BASE_BODY:
-                        cv.put(Events.DESCRIPTION, bodyParser());
-                        break;
-                    case Tags.CALENDAR_BODY:
-                        cv.put(Events.DESCRIPTION, getValue());
-                        break;
-                    case Tags.CALENDAR_TIME_ZONE:
-                        timeZone = CalendarUtilities.tziStringToTimeZone(getValue());
-                        if (timeZone == null) {
-                            timeZone = mLocalTimeZone;
-                        }
-                        cv.put(Events.EVENT_TIMEZONE, timeZone.getID());
-                        break;
-                    case Tags.CALENDAR_START_TIME:
-                        startTime = Utility.parseDateTimeToMillis(getValue());
-                        break;
-                    case Tags.CALENDAR_END_TIME:
-                        endTime = Utility.parseDateTimeToMillis(getValue());
-                        break;
-                    case Tags.CALENDAR_EXCEPTIONS:
-                        // For exceptions to show the organizer, the organizer must be added before
-                        // we call exceptionsParser
-                        addOrganizerToAttendees(ops, eventId, organizerName, organizerEmail);
-                        organizerAdded = true;
-                        exceptionsParser(ops, cv, attendeeValues, reminderMins, busyStatus,
-                                startTime, endTime);
-                        break;
-                    case Tags.CALENDAR_LOCATION:
-                        cv.put(Events.EVENT_LOCATION, getValue());
-                        break;
-                    case Tags.CALENDAR_RECURRENCE:
-                        String rrule = recurrenceParser();
-                        if (rrule != null) {
-                            cv.put(Events.RRULE, rrule);
-                        }
-                        break;
-                    case Tags.CALENDAR_ORGANIZER_EMAIL:
-                        organizerEmail = getValue();
-                        cv.put(Events.ORGANIZER, organizerEmail);
-                        break;
-                    case Tags.CALENDAR_SUBJECT:
-                        cv.put(Events.TITLE, getValue());
-                        break;
-                    case Tags.CALENDAR_SENSITIVITY:
-                        cv.put(Events.ACCESS_LEVEL, encodeVisibility(getValueInt()));
-                        break;
-                    case Tags.CALENDAR_ORGANIZER_NAME:
-                        organizerName = getValue();
-                        break;
-                    case Tags.CALENDAR_REMINDER_MINS_BEFORE:
-                        // Save away whether this tag has content; Exchange 2010 sends an empty tag
-                        // rather than not sending one (as with Ex07 and Ex03)
-                        boolean hasContent = !noContent;
-                        reminderMins = getValueInt();
-                        if (hasContent) {
-                            ops.newReminder(reminderMins);
-                            cv.put(Events.HAS_ALARM, 1);
-                        }
-                        break;
-                    // The following are fields we should save (for changes), though they don't
-                    // relate to data used by CalendarProvider at this point
-                    case Tags.CALENDAR_UID:
-                        cv.put(Events.SYNC_DATA2, getValue());
-                        break;
-                    case Tags.CALENDAR_DTSTAMP:
-                        dtStamp = getValue();
-                        break;
-                    case Tags.CALENDAR_MEETING_STATUS:
-                        ops.newExtendedProperty(EXTENDED_PROPERTY_MEETING_STATUS, getValue());
-                        break;
-                    case Tags.CALENDAR_BUSY_STATUS:
-                        // We'll set the user's status in the Attendees table below
-                        // Don't set selfAttendeeStatus or CalendarProvider will create a duplicate
-                        // attendee!
-                        busyStatus = getValueInt();
-                        break;
-                    case Tags.CALENDAR_RESPONSE_TYPE:
-                        // EAS 14+ uses this for the user's response status; we'll use this instead
-                        // of busy status, if it appears
-                        responseType = getValueInt();
-                        break;
-                    case Tags.CALENDAR_CATEGORIES:
-                        String categories = categoriesParser(ops);
-                        if (categories.length() > 0) {
-                            ops.newExtendedProperty(EXTENDED_PROPERTY_CATEGORIES, categories);
-                        }
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-
-            // Enforce CalendarProvider required properties
-            setTimeRelatedValues(cv, startTime, endTime, allDayEvent);
-
-            // If we haven't added the organizer to attendees, do it now
-            if (!organizerAdded) {
-                addOrganizerToAttendees(ops, eventId, organizerName, organizerEmail);
-            }
-
-            // Note that organizerEmail can be null with a DTSTAMP only change from the server
-            boolean selfOrganizer = (mEmailAddress.equals(organizerEmail));
-
-            // Store email addresses of attendees (in a tokenizable string) in ExtendedProperties
-            // If the user is an attendee, set the attendee status using busyStatus (note that the
-            // busyStatus is inherited from the parent unless it's specified in the exception)
-            // Add the insert/update operation for each attendee (based on whether it's add/change)
-            int numAttendees = attendeeValues.size();
-            if (numAttendees > MAX_SYNCED_ATTENDEES) {
-                // Indicate that we've redacted attendees.  If we're the organizer, disable edit
-                // by setting organizerEmail to a bogus value and by setting the upsync prohibited
-                // extended properly.
-                // Note that we don't set ANY attendees if we're in this branch; however, the
-                // organizer has already been included above, and WILL show up (which is good)
-                if (eventId < 0) {
-                    ops.newExtendedProperty(EXTENDED_PROPERTY_ATTENDEES_REDACTED, "1");
-                    if (selfOrganizer) {
-                        ops.newExtendedProperty(EXTENDED_PROPERTY_UPSYNC_PROHIBITED, "1");
-                    }
-                } else {
-                    ops.updatedExtendedProperty(EXTENDED_PROPERTY_ATTENDEES_REDACTED, "1", eventId);
-                    if (selfOrganizer) {
-                        ops.updatedExtendedProperty(EXTENDED_PROPERTY_UPSYNC_PROHIBITED, "1",
-                                eventId);
-                    }
-                }
-                if (selfOrganizer) {
-                    organizerEmail = BOGUS_ORGANIZER_EMAIL;
-                    cv.put(Events.ORGANIZER, organizerEmail);
-                }
-                // Tell UI that we don't have any attendees
-                cv.put(Events.HAS_ATTENDEE_DATA, "0");
-                mService.userLog("Maximum number of attendees exceeded; redacting");
-            } else if (numAttendees > 0) {
-                StringBuilder sb = new StringBuilder();
-                for (ContentValues attendee: attendeeValues) {
-                    String attendeeEmail = attendee.getAsString(Attendees.ATTENDEE_EMAIL);
-                    sb.append(attendeeEmail);
-                    sb.append(ATTENDEE_TOKENIZER_DELIMITER);
-                    if (mEmailAddress.equalsIgnoreCase(attendeeEmail)) {
-                        int attendeeStatus;
-                        // We'll use the response type (EAS 14), if we've got one; otherwise, we'll
-                        // try to infer it from busy status
-                        if (responseType != CalendarUtilities.RESPONSE_TYPE_NONE) {
-                            attendeeStatus =
-                                CalendarUtilities.attendeeStatusFromResponseType(responseType);
-                        } else if (!update) {
-                            // For new events in EAS < 14, we have no idea what the busy status
-                            // means, so we show "none", allowing the user to select an option.
-                            attendeeStatus = Attendees.ATTENDEE_STATUS_NONE;
-                        } else {
-                            // For updated events, we'll try to infer the attendee status from the
-                            // busy status
-                            attendeeStatus =
-                                CalendarUtilities.attendeeStatusFromBusyStatus(busyStatus);
-                        }
-                        attendee.put(Attendees.ATTENDEE_STATUS, attendeeStatus);
-                        // If we're an attendee, save away our initial attendee status in the
-                        // event's ExtendedProperties (we look for differences between this and
-                        // the user's current attendee status to determine whether an email needs
-                        // to be sent to the organizer)
-                        // organizerEmail will be null in the case that this is an attendees-only
-                        // change from the server
-                        if (organizerEmail == null ||
-                                !organizerEmail.equalsIgnoreCase(attendeeEmail)) {
-                            if (eventId < 0) {
-                                ops.newExtendedProperty(EXTENDED_PROPERTY_USER_ATTENDEE_STATUS,
-                                        Integer.toString(attendeeStatus));
-                            } else {
-                                ops.updatedExtendedProperty(EXTENDED_PROPERTY_USER_ATTENDEE_STATUS,
-                                        Integer.toString(attendeeStatus), eventId);
-
-                            }
-                        }
-                    }
-                    if (eventId < 0) {
-                        ops.newAttendee(attendee);
-                    } else {
-                        ops.updatedAttendee(attendee, eventId);
-                    }
-                }
-                if (eventId < 0) {
-                    ops.newExtendedProperty(EXTENDED_PROPERTY_ATTENDEES, sb.toString());
-                    ops.newExtendedProperty(EXTENDED_PROPERTY_ATTENDEES_REDACTED, "0");
-                    ops.newExtendedProperty(EXTENDED_PROPERTY_UPSYNC_PROHIBITED, "0");
-                } else {
-                    ops.updatedExtendedProperty(EXTENDED_PROPERTY_ATTENDEES, sb.toString(),
-                            eventId);
-                    ops.updatedExtendedProperty(EXTENDED_PROPERTY_ATTENDEES_REDACTED, "0", eventId);
-                    ops.updatedExtendedProperty(EXTENDED_PROPERTY_UPSYNC_PROHIBITED, "0", eventId);
-                }
-            }
-
-            // Put the real event in the proper place in the ops ArrayList
-            if (eventOffset >= 0) {
-                // Store away the DTSTAMP here
-                if (dtStamp != null) {
-                    ops.newExtendedProperty(EXTENDED_PROPERTY_DTSTAMP, dtStamp);
-                }
-
-                if (isValidEventValues(cv)) {
-                    ops.set(eventOffset,
-                            new Operation(ContentProviderOperation
-                                    .newInsert(mAsSyncAdapterEvents).withValues(cv)));
-                } else {
-                    // If we can't add this event (it's invalid), remove all of the inserts
-                    // we've built for it
-                    int cnt = ops.mCount - eventOffset;
-                    userLog(TAG, "Removing " + cnt + " inserts from mOps");
-                    for (int i = 0; i < cnt; i++) {
-                        ops.remove(eventOffset);
-                    }
-                    ops.mCount = eventOffset;
-                    // If this is a change, we need to also remove the deletion that comes
-                    // before the addition
-                    if (deleteOffset >= 0) {
-                        // Remove the deletion
-                        ops.remove(deleteOffset);
-                        // And the deletion of exceptions
-                        ops.remove(deleteOffset);
-                        userLog(TAG, "Removing deletion ops from mOps");
-                        ops.mCount = deleteOffset;
-                    }
-                }
-            }
-            // Mark the end of the event
-            addSeparatorOperation(ops, Events.CONTENT_URI);
-        }
-
-        private void logEventColumns(ContentValues cv, String reason) {
-            if (Eas.USER_LOG) {
-                StringBuilder sb =
-                    new StringBuilder("Event invalid, " + reason + ", skipping: Columns = ");
-                for (Entry<String, Object> entry: cv.valueSet()) {
-                    sb.append(entry.getKey());
-                    sb.append('/');
-                }
-                userLog(TAG, sb.toString());
-            }
-        }
-
-        /*package*/ boolean isValidEventValues(ContentValues cv) {
-            boolean isException = cv.containsKey(Events.ORIGINAL_INSTANCE_TIME);
-            // All events require DTSTART
-            if (!cv.containsKey(Events.DTSTART)) {
-                logEventColumns(cv, "DTSTART missing");
-                return false;
-            // If we're a top-level event, we must have _SYNC_DATA (uid)
-            } else if (!isException && !cv.containsKey(Events.SYNC_DATA2)) {
-                logEventColumns(cv, "_SYNC_DATA missing");
-                return false;
-            // We must also have DTEND or DURATION if we're not an exception
-            } else if (!isException && !cv.containsKey(Events.DTEND) &&
-                    !cv.containsKey(Events.DURATION)) {
-                logEventColumns(cv, "DTEND/DURATION missing");
-                return false;
-            // Exceptions require DTEND
-            } else if (isException && !cv.containsKey(Events.DTEND)) {
-                logEventColumns(cv, "Exception missing DTEND");
-                return false;
-            // If this is a recurrence, we need a DURATION (in days if an all-day event)
-            } else if (cv.containsKey(Events.RRULE)) {
-                String duration = cv.getAsString(Events.DURATION);
-                if (duration == null) return false;
-                if (cv.containsKey(Events.ALL_DAY)) {
-                    Integer ade = cv.getAsInteger(Events.ALL_DAY);
-                    if (ade != null && ade != 0 && !duration.endsWith("D")) {
-                        return false;
-                    }
-                }
-            }
-            return true;
-        }
-
-        public String recurrenceParser() throws IOException {
-            // Turn this information into an RRULE
-            int type = -1;
-            int occurrences = -1;
-            int interval = -1;
-            int dow = -1;
-            int dom = -1;
-            int wom = -1;
-            int moy = -1;
-            String until = null;
-
-            while (nextTag(Tags.CALENDAR_RECURRENCE) != END) {
-                switch (tag) {
-                    case Tags.CALENDAR_RECURRENCE_TYPE:
-                        type = getValueInt();
-                        break;
-                    case Tags.CALENDAR_RECURRENCE_INTERVAL:
-                        interval = getValueInt();
-                        break;
-                    case Tags.CALENDAR_RECURRENCE_OCCURRENCES:
-                        occurrences = getValueInt();
-                        break;
-                    case Tags.CALENDAR_RECURRENCE_DAYOFWEEK:
-                        dow = getValueInt();
-                        break;
-                    case Tags.CALENDAR_RECURRENCE_DAYOFMONTH:
-                        dom = getValueInt();
-                        break;
-                    case Tags.CALENDAR_RECURRENCE_WEEKOFMONTH:
-                        wom = getValueInt();
-                        break;
-                    case Tags.CALENDAR_RECURRENCE_MONTHOFYEAR:
-                        moy = getValueInt();
-                        break;
-                    case Tags.CALENDAR_RECURRENCE_UNTIL:
-                        until = getValue();
-                        break;
-                    default:
-                       skipTag();
-                }
-            }
-
-            return CalendarUtilities.rruleFromRecurrence(type, occurrences, interval,
-                    dow, dom, wom, moy, until);
-        }
-
-        private void exceptionParser(CalendarOperations ops, ContentValues parentCv,
-                ArrayList<ContentValues> attendeeValues, int reminderMins, int busyStatus,
-                long startTime, long endTime) throws IOException {
-            ContentValues cv = new ContentValues();
-            cv.put(Events.CALENDAR_ID, mCalendarId);
-
-            // It appears that these values have to be copied from the parent if they are to appear
-            // Note that they can be overridden below
-            cv.put(Events.ORGANIZER, parentCv.getAsString(Events.ORGANIZER));
-            cv.put(Events.TITLE, parentCv.getAsString(Events.TITLE));
-            cv.put(Events.DESCRIPTION, parentCv.getAsString(Events.DESCRIPTION));
-            cv.put(Events.ORIGINAL_ALL_DAY, parentCv.getAsInteger(Events.ALL_DAY));
-            cv.put(Events.EVENT_LOCATION, parentCv.getAsString(Events.EVENT_LOCATION));
-            cv.put(Events.ACCESS_LEVEL, parentCv.getAsString(Events.ACCESS_LEVEL));
-            cv.put(Events.EVENT_TIMEZONE, parentCv.getAsString(Events.EVENT_TIMEZONE));
-            // Exceptions should always have this set to zero, since EAS has no concept of
-            // separate attendee lists for exceptions; if we fail to do this, then the UI will
-            // allow the user to change attendee data, and this change would never get reflected
-            // on the server.
-            cv.put(Events.HAS_ATTENDEE_DATA, 0);
-
-            int allDayEvent = 0;
-
-            // This column is the key that links the exception to the serverId
-            cv.put(Events.ORIGINAL_SYNC_ID, parentCv.getAsString(Events._SYNC_ID));
-
-            String exceptionStartTime = "_noStartTime";
-            while (nextTag(Tags.SYNC_APPLICATION_DATA) != END) {
-                switch (tag) {
-                    case Tags.CALENDAR_ATTACHMENTS:
-                        attachmentsParser();
-                        break;
-                    case Tags.CALENDAR_EXCEPTION_START_TIME:
-                        exceptionStartTime = getValue();
-                        cv.put(Events.ORIGINAL_INSTANCE_TIME,
-                                Utility.parseDateTimeToMillis(exceptionStartTime));
-                        break;
-                    case Tags.CALENDAR_EXCEPTION_IS_DELETED:
-                        if (getValueInt() == 1) {
-                            cv.put(Events.STATUS, Events.STATUS_CANCELED);
-                        }
-                        break;
-                    case Tags.CALENDAR_ALL_DAY_EVENT:
-                        allDayEvent = getValueInt();
-                        cv.put(Events.ALL_DAY, allDayEvent);
-                        break;
-                    case Tags.BASE_BODY:
-                        cv.put(Events.DESCRIPTION, bodyParser());
-                        break;
-                    case Tags.CALENDAR_BODY:
-                        cv.put(Events.DESCRIPTION, getValue());
-                        break;
-                    case Tags.CALENDAR_START_TIME:
-                        startTime = Utility.parseDateTimeToMillis(getValue());
-                        break;
-                    case Tags.CALENDAR_END_TIME:
-                        endTime = Utility.parseDateTimeToMillis(getValue());
-                        break;
-                    case Tags.CALENDAR_LOCATION:
-                        cv.put(Events.EVENT_LOCATION, getValue());
-                        break;
-                    case Tags.CALENDAR_RECURRENCE:
-                        String rrule = recurrenceParser();
-                        if (rrule != null) {
-                            cv.put(Events.RRULE, rrule);
-                        }
-                        break;
-                    case Tags.CALENDAR_SUBJECT:
-                        cv.put(Events.TITLE, getValue());
-                        break;
-                    case Tags.CALENDAR_SENSITIVITY:
-                        cv.put(Events.ACCESS_LEVEL, encodeVisibility(getValueInt()));
-                        break;
-                    case Tags.CALENDAR_BUSY_STATUS:
-                        busyStatus = getValueInt();
-                        // Don't set selfAttendeeStatus or CalendarProvider will create a duplicate
-                        // attendee!
-                        break;
-                        // TODO How to handle these items that are linked to event id!
-//                    case Tags.CALENDAR_DTSTAMP:
-//                        ops.newExtendedProperty("dtstamp", getValue());
-//                        break;
-//                    case Tags.CALENDAR_REMINDER_MINS_BEFORE:
-//                        ops.newReminder(getValueInt());
-//                        break;
-                    default:
-                        skipTag();
-                }
-            }
-
-            // We need a _sync_id, but it can't be the parent's id, so we generate one
-            cv.put(Events._SYNC_ID, parentCv.getAsString(Events._SYNC_ID) + '_' +
-                    exceptionStartTime);
-
-            // Enforce CalendarProvider required properties
-            setTimeRelatedValues(cv, startTime, endTime, allDayEvent);
-
-            // Don't insert an invalid exception event
-            if (!isValidEventValues(cv)) return;
-
-            // Add the exception insert
-            int exceptionStart = ops.mCount;
-            ops.newException(cv);
-            // Also add the attendees, because they need to be copied over from the parent event
-            boolean attendeesRedacted = false;
-            if (attendeeValues != null) {
-                for (ContentValues attValues: attendeeValues) {
-                    // If this is the user, use his busy status for attendee status
-                    String attendeeEmail = attValues.getAsString(Attendees.ATTENDEE_EMAIL);
-                    // Note that the exception at which we surpass the redaction limit might have
-                    // any number of attendees shown; since this is an edge case and a workaround,
-                    // it seems to be an acceptable implementation
-                    if (mEmailAddress.equalsIgnoreCase(attendeeEmail)) {
-                        attValues.put(Attendees.ATTENDEE_STATUS,
-                                CalendarUtilities.attendeeStatusFromBusyStatus(busyStatus));
-                        ops.newAttendee(attValues, exceptionStart);
-                    } else if (ops.size() < MAX_OPS_BEFORE_EXCEPTION_ATTENDEE_REDACTION) {
-                        ops.newAttendee(attValues, exceptionStart);
-                    } else {
-                        attendeesRedacted = true;
-                    }
-                }
-            }
-            // And add the parent's reminder value
-            if (reminderMins > 0) {
-                ops.newReminder(reminderMins, exceptionStart);
-            }
-            if (attendeesRedacted) {
-                mService.userLog("Attendees redacted in this exception");
-            }
-        }
-
-        private int encodeVisibility(int easVisibility) {
-            int visibility = 0;
-            switch(easVisibility) {
-                case 0:
-                    visibility = Events.ACCESS_DEFAULT;
-                    break;
-                case 1:
-                    visibility = Events.ACCESS_PUBLIC;
-                    break;
-                case 2:
-                    visibility = Events.ACCESS_PRIVATE;
-                    break;
-                case 3:
-                    visibility = Events.ACCESS_CONFIDENTIAL;
-                    break;
-            }
-            return visibility;
-        }
-
-        private void exceptionsParser(CalendarOperations ops, ContentValues cv,
-                ArrayList<ContentValues> attendeeValues, int reminderMins, int busyStatus,
-                long startTime, long endTime) throws IOException {
-            while (nextTag(Tags.CALENDAR_EXCEPTIONS) != END) {
-                switch (tag) {
-                    case Tags.CALENDAR_EXCEPTION:
-                        exceptionParser(ops, cv, attendeeValues, reminderMins, busyStatus,
-                                startTime, endTime);
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-        }
-
-        private String categoriesParser(CalendarOperations ops) throws IOException {
-            StringBuilder categories = new StringBuilder();
-            while (nextTag(Tags.CALENDAR_CATEGORIES) != END) {
-                switch (tag) {
-                    case Tags.CALENDAR_CATEGORY:
-                        // TODO Handle categories (there's no similar concept for gdata AFAIK)
-                        // We need to save them and spit them back when we update the event
-                        categories.append(getValue());
-                        categories.append(CATEGORY_TOKENIZER_DELIMITER);
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-            return categories.toString();
-        }
-
-        /**
-         * For now, we ignore (but still have to parse) event attachments; these are new in EAS 14
-         */
-        private void attachmentsParser() throws IOException {
-            while (nextTag(Tags.CALENDAR_ATTACHMENTS) != END) {
-                switch (tag) {
-                    case Tags.CALENDAR_ATTACHMENT:
-                        skipParser(Tags.CALENDAR_ATTACHMENT);
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-        }
-
-        private ArrayList<ContentValues> attendeesParser(CalendarOperations ops, long eventId)
-                throws IOException {
-            int attendeeCount = 0;
-            ArrayList<ContentValues> attendeeValues = new ArrayList<ContentValues>();
-            while (nextTag(Tags.CALENDAR_ATTENDEES) != END) {
-                switch (tag) {
-                    case Tags.CALENDAR_ATTENDEE:
-                        ContentValues cv = attendeeParser(ops, eventId);
-                        // If we're going to redact these attendees anyway, let's avoid unnecessary
-                        // memory pressure, and not keep them around
-                        // We still need to parse them all, however
-                        attendeeCount++;
-                        // Allow one more than MAX_ATTENDEES, so that the check for "too many" will
-                        // succeed in addEvent
-                        if (attendeeCount <= (MAX_SYNCED_ATTENDEES+1)) {
-                            attendeeValues.add(cv);
-                        }
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-            return attendeeValues;
-        }
-
-        private ContentValues attendeeParser(CalendarOperations ops, long eventId)
-                throws IOException {
-            ContentValues cv = new ContentValues();
-            while (nextTag(Tags.CALENDAR_ATTENDEE) != END) {
-                switch (tag) {
-                    case Tags.CALENDAR_ATTENDEE_EMAIL:
-                        cv.put(Attendees.ATTENDEE_EMAIL, getValue());
-                        break;
-                    case Tags.CALENDAR_ATTENDEE_NAME:
-                        cv.put(Attendees.ATTENDEE_NAME, getValue());
-                        break;
-                    case Tags.CALENDAR_ATTENDEE_STATUS:
-                        int status = getValueInt();
-                        cv.put(Attendees.ATTENDEE_STATUS,
-                                (status == 2) ? Attendees.ATTENDEE_STATUS_TENTATIVE :
-                                (status == 3) ? Attendees.ATTENDEE_STATUS_ACCEPTED :
-                                (status == 4) ? Attendees.ATTENDEE_STATUS_DECLINED :
-                                (status == 5) ? Attendees.ATTENDEE_STATUS_INVITED :
-                                    Attendees.ATTENDEE_STATUS_NONE);
-                        break;
-                    case Tags.CALENDAR_ATTENDEE_TYPE:
-                        int type = Attendees.TYPE_NONE;
-                        // EAS types: 1 = req'd, 2 = opt, 3 = resource
-                        switch (getValueInt()) {
-                            case 1:
-                                type = Attendees.TYPE_REQUIRED;
-                                break;
-                            case 2:
-                                type = Attendees.TYPE_OPTIONAL;
-                                break;
-                        }
-                        cv.put(Attendees.ATTENDEE_TYPE, type);
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-            cv.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
-            return cv;
-        }
-
-        private String bodyParser() throws IOException {
-            String body = null;
-            while (nextTag(Tags.BASE_BODY) != END) {
-                switch (tag) {
-                    case Tags.BASE_DATA:
-                        body = getValue();
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-
-            // Handle null data without error
-            if (body == null) return "";
-            // Remove \r's from any body text
-            return body.replace("\r\n", "\n");
-        }
-
-        public void addParser(CalendarOperations ops) throws IOException {
-            String serverId = null;
-            while (nextTag(Tags.SYNC_ADD) != END) {
-                switch (tag) {
-                    case Tags.SYNC_SERVER_ID: // same as
-                        serverId = getValue();
-                        break;
-                    case Tags.SYNC_APPLICATION_DATA:
-                        addEvent(ops, serverId, false);
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-        }
-
-        private Cursor getServerIdCursor(String serverId) {
-            return mContentResolver.query(mAccountUri, ID_PROJECTION, SERVER_ID_AND_CALENDAR_ID,
-                    new String[] {serverId, mCalendarIdString}, null);
-        }
-
-        private Cursor getClientIdCursor(String clientId) {
-            mBindArgument[0] = clientId;
-            return mContentResolver.query(mAccountUri, ID_PROJECTION, CLIENT_ID_SELECTION,
-                    mBindArgument, null);
-        }
-
-        public void deleteParser(CalendarOperations ops) throws IOException {
-            while (nextTag(Tags.SYNC_DELETE) != END) {
-                switch (tag) {
-                    case Tags.SYNC_SERVER_ID:
-                        String serverId = getValue();
-                        // Find the event with the given serverId
-                        Cursor c = getServerIdCursor(serverId);
-                        try {
-                            if (c.moveToFirst()) {
-                                userLog("Deleting ", serverId);
-                                ops.delete(c.getLong(0), serverId);
-                            }
-                        } finally {
-                            c.close();
-                        }
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-        }
-
-        /**
-         * A change is handled as a delete (including all exceptions) and an add
-         * This isn't as efficient as attempting to traverse the original and all of its exceptions,
-         * but changes happen infrequently and this code is both simpler and easier to maintain
-         * @param ops the array of pending ContactProviderOperations.
-         * @throws IOException
-         */
-        public void changeParser(CalendarOperations ops) throws IOException {
-            String serverId = null;
-            while (nextTag(Tags.SYNC_CHANGE) != END) {
-                switch (tag) {
-                    case Tags.SYNC_SERVER_ID:
-                        serverId = getValue();
-                        break;
-                    case Tags.SYNC_APPLICATION_DATA:
-                        userLog("Changing " + serverId);
-                        addEvent(ops, serverId, true);
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-        }
-
-        @Override
-        public void commandsParser() throws IOException {
-            while (nextTag(Tags.SYNC_COMMANDS) != END) {
-                if (tag == Tags.SYNC_ADD) {
-                    addParser(mOps);
-                    incrementChangeCount();
-                } else if (tag == Tags.SYNC_DELETE) {
-                    deleteParser(mOps);
-                    incrementChangeCount();
-                } else if (tag == Tags.SYNC_CHANGE) {
-                    changeParser(mOps);
-                    incrementChangeCount();
-                } else
-                    skipTag();
-            }
-        }
-
-        @Override
-        public void commit() throws IOException {
-            userLog("Calendar SyncKey saved as: ", mMailbox.mSyncKey);
-            // Save the syncKey here, using the Helper provider by Calendar provider
-            mOps.add(new Operation(SyncStateContract.Helpers.newSetOperation(
-                    asSyncAdapter(SyncState.CONTENT_URI, mEmailAddress,
-                            Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE),
-                    mAccountManagerAccount,
-                    mMailbox.mSyncKey.getBytes())));
-
-            // We need to send cancellations now, because the Event won't exist after the commit
-            for (long eventId: mSendCancelIdList) {
-                EmailContent.Message msg;
-                try {
-                    msg = CalendarUtilities.createMessageForEventId(mContext, eventId,
-                            EmailContent.Message.FLAG_OUTGOING_MEETING_CANCEL, null,
-                            mAccount);
-                } catch (RemoteException e) {
-                    // Nothing to do here; the Event may no longer exist
-                    continue;
-                }
-                if (msg != null) {
-                    EasOutboxService.sendMessage(mContext, mAccount.mId, msg);
-                }
-            }
-
-            // Execute our CPO's safely
-            try {
-                mOps.mResults = safeExecute(CalendarContract.AUTHORITY, mOps);
-            } catch (RemoteException e) {
-                throw new IOException("Remote exception caught; will retry");
-            }
-
-            if (mOps.mResults != null) {
-                // Clear dirty and mark flags for updates sent to server
-                if (!mUploadedIdList.isEmpty())  {
-                    ContentValues cv = new ContentValues();
-                    cv.put(Events.DIRTY, 0);
-                    cv.put(EVENT_SYNC_MARK, "0");
-                    for (long eventId : mUploadedIdList) {
-                        mContentResolver.update(
-                                asSyncAdapter(
-                                        ContentUris.withAppendedId(Events.CONTENT_URI, eventId),
-                                        mEmailAddress, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), cv,
-                                null, null);
-                    }
-                }
-                // Delete events marked for deletion
-                if (!mDeletedIdList.isEmpty()) {
-                    for (long eventId : mDeletedIdList) {
-                        mContentResolver.delete(
-                                asSyncAdapter(
-                                        ContentUris.withAppendedId(Events.CONTENT_URI, eventId),
-                                        mEmailAddress, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), null,
-                                null);
-                    }
-                }
-                // Send any queued up email (invitations replies, etc.)
-                for (Message msg: mOutgoingMailList) {
-                    EasOutboxService.sendMessage(mContext, mAccount.mId, msg);
-                }
-            }
-        }
-
-        public void addResponsesParser() throws IOException {
-            String serverId = null;
-            String clientId = null;
-            int status = -1;
-            ContentValues cv = new ContentValues();
-            while (nextTag(Tags.SYNC_ADD) != END) {
-                switch (tag) {
-                    case Tags.SYNC_SERVER_ID:
-                        serverId = getValue();
-                        break;
-                    case Tags.SYNC_CLIENT_ID:
-                        clientId = getValue();
-                        break;
-                    case Tags.SYNC_STATUS:
-                        status = getValueInt();
-                        if (status != 1) {
-                            userLog("Attempt to add event failed with status: " + status);
-                        }
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-
-            if (clientId == null) return;
-            if (serverId == null) {
-                // TODO Reconsider how to handle this
-                serverId = "FAIL:" + status;
-            }
-
-            Cursor c = getClientIdCursor(clientId);
-            try {
-                if (c.moveToFirst()) {
-                    cv.put(Events._SYNC_ID, serverId);
-                    cv.put(Events.SYNC_DATA2, clientId);
-                    long id = c.getLong(0);
-                    // Write the serverId into the Event
-                    mOps.add(new Operation(ContentProviderOperation
-                            .newUpdate(ContentUris.withAppendedId(mAsSyncAdapterEvents, id))
-                            .withValues(cv)));
-                    userLog("New event " + clientId + " was given serverId: " + serverId);
-                }
-            } finally {
-                c.close();
-            }
-        }
-
-        public void changeResponsesParser() throws IOException {
-            String serverId = null;
-            String status = null;
-            while (nextTag(Tags.SYNC_CHANGE) != END) {
-                switch (tag) {
-                    case Tags.SYNC_SERVER_ID:
-                        serverId = getValue();
-                        break;
-                    case Tags.SYNC_STATUS:
-                        status = getValue();
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-            if (serverId != null && status != null) {
-                userLog("Changed event " + serverId + " failed with status: " + status);
-            }
-        }
-
-
-        @Override
-        public void responsesParser() throws IOException {
-            // Handle server responses here (for Add and Change)
-            while (nextTag(Tags.SYNC_RESPONSES) != END) {
-                if (tag == Tags.SYNC_ADD) {
-                    addResponsesParser();
-                } else if (tag == Tags.SYNC_CHANGE) {
-                    changeResponsesParser();
-                } else
-                    skipTag();
-            }
-        }
-    }
-
-    protected class CalendarOperations extends ArrayList<Operation> {
-        private static final long serialVersionUID = 1L;
-        public int mCount = 0;
-        private ContentProviderResult[] mResults = null;
-        private int mEventStart = 0;
-
-        @Override
-        public boolean add(Operation op) {
-            super.add(op);
-            mCount++;
-            return true;
-        }
-
-        public int newEvent(Operation op) {
-            mEventStart = mCount;
-            add(op);
-            return mEventStart;
-        }
-
-        public int newDelete(long id, String serverId) {
-            int offset = mCount;
-            delete(id, serverId);
-            return offset;
-        }
-
-        public void newAttendee(ContentValues cv) {
-            newAttendee(cv, mEventStart);
-        }
-
-        public void newAttendee(ContentValues cv, int eventStart) {
-            add(new Operation(ContentProviderOperation.newInsert(mAsSyncAdapterAttendees)
-                    .withValues(cv),
-                    Attendees.EVENT_ID,
-                    eventStart));
-        }
-
-        public void updatedAttendee(ContentValues cv, long id) {
-            cv.put(Attendees.EVENT_ID, id);
-            add(new Operation(ContentProviderOperation.newInsert(mAsSyncAdapterAttendees)
-                    .withValues(cv)));
-        }
-
-        public void newException(ContentValues cv) {
-            add(new Operation(ContentProviderOperation.newInsert(mAsSyncAdapterEvents)
-                    .withValues(cv)));
-        }
-
-        public void newExtendedProperty(String name, String value) {
-            add(new Operation(ContentProviderOperation.newInsert(mAsSyncAdapterExtendedProperties)
-                    .withValue(ExtendedProperties.NAME, name)
-                    .withValue(ExtendedProperties.VALUE, value),
-                    ExtendedProperties.EVENT_ID,
-                    mEventStart));
-        }
-
-        public void updatedExtendedProperty(String name, String value, long id) {
-            // Find an existing ExtendedProperties row for this event and property name
-            Cursor c = mService.mContentResolver.query(ExtendedProperties.CONTENT_URI,
-                    EXTENDED_PROPERTY_PROJECTION, EVENT_ID_AND_NAME,
-                    new String[] {Long.toString(id), name}, null);
-            long extendedPropertyId = -1;
-            // If there is one, capture its _id
-            if (c != null) {
-                try {
-                    if (c.moveToFirst()) {
-                        extendedPropertyId = c.getLong(EXTENDED_PROPERTY_ID);
-                    }
-                } finally {
-                    c.close();
-                }
-            }
-            // Either do an update or an insert, depending on whether one
-            // already exists
-            if (extendedPropertyId >= 0) {
-                add(new Operation(ContentProviderOperation
-                        .newUpdate(
-                                ContentUris.withAppendedId(mAsSyncAdapterExtendedProperties,
-                                        extendedPropertyId))
-                        .withValue(ExtendedProperties.VALUE, value)));
-            } else {
-                newExtendedProperty(name, value);
-            }
-        }
-
-        public void newReminder(int mins, int eventStart) {
-            add(new Operation(ContentProviderOperation.newInsert(mAsSyncAdapterReminders)
-                    .withValue(Reminders.MINUTES, mins)
-                    .withValue(Reminders.METHOD, Reminders.METHOD_ALERT),
-                    ExtendedProperties.EVENT_ID,
-                    eventStart));
-        }
-
-        public void newReminder(int mins) {
-            newReminder(mins, mEventStart);
-        }
-
-        public void delete(long id, String syncId) {
-            add(new Operation(ContentProviderOperation.newDelete(
-                    ContentUris.withAppendedId(mAsSyncAdapterEvents, id))));
-            // Delete the exceptions for this Event (CalendarProvider doesn't do this)
-            add(new Operation(ContentProviderOperation
-                    .newDelete(mAsSyncAdapterEvents)
-                    .withSelection(Events.ORIGINAL_SYNC_ID + "=?", new String[] {syncId})));
-        }
-    }
-
-    private String decodeVisibility(int visibility) {
-        int easVisibility = 0;
-        switch(visibility) {
-            case Events.ACCESS_DEFAULT:
-                easVisibility = 0;
-                break;
-            case Events.ACCESS_PUBLIC:
-                easVisibility = 1;
-                break;
-            case Events.ACCESS_PRIVATE:
-                easVisibility = 2;
-                break;
-            case Events.ACCESS_CONFIDENTIAL:
-                easVisibility = 3;
-                break;
-        }
-        return Integer.toString(easVisibility);
-    }
-
-    private int getInt(ContentValues cv, String column) {
-        Integer i = cv.getAsInteger(column);
-        if (i == null) return 0;
-        return i;
-    }
-
-    private void sendEvent(Entity entity, String clientId, Serializer s)
-            throws IOException {
-        // Serialize for EAS here
-        // Set uid with the client id we created
-        // 1) Serialize the top-level event
-        // 2) Serialize attendees and reminders from subvalues
-        // 3) Look for exceptions and serialize with the top-level event
-        ContentValues entityValues = entity.getEntityValues();
-        final boolean isException = (clientId == null);
-        boolean hasAttendees = false;
-        final boolean isChange = entityValues.containsKey(Events._SYNC_ID);
-        final Double version = mService.mProtocolVersionDouble;
-        final boolean allDay =
-            CalendarUtilities.getIntegerValueAsBoolean(entityValues, Events.ALL_DAY);
-
-        // NOTE: Exchange 2003 (EAS 2.5) seems to require the "exception deleted" and "exception
-        // start time" data before other data in exceptions.  Failure to do so results in a
-        // status 6 error during sync
-        if (isException) {
-           // Send exception deleted flag if necessary
-            Integer deleted = entityValues.getAsInteger(Events.DELETED);
-            boolean isDeleted = deleted != null && deleted == 1;
-            Integer eventStatus = entityValues.getAsInteger(Events.STATUS);
-            boolean isCanceled = eventStatus != null && eventStatus.equals(Events.STATUS_CANCELED);
-            if (isDeleted || isCanceled) {
-                s.data(Tags.CALENDAR_EXCEPTION_IS_DELETED, "1");
-                // If we're deleted, the UI will continue to show this exception until we mark
-                // it canceled, so we'll do that here...
-                if (isDeleted && !isCanceled) {
-                    final long eventId = entityValues.getAsLong(Events._ID);
-                    ContentValues cv = new ContentValues();
-                    cv.put(Events.STATUS, Events.STATUS_CANCELED);
-                    mService.mContentResolver.update(
-                            asSyncAdapter(ContentUris.withAppendedId(Events.CONTENT_URI, eventId),
-                                    mEmailAddress, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), cv, null,
-                            null);
-                }
-            } else {
-                s.data(Tags.CALENDAR_EXCEPTION_IS_DELETED, "0");
-            }
-
-            // TODO Add reminders to exceptions (allow them to be specified!)
-            Long originalTime = entityValues.getAsLong(Events.ORIGINAL_INSTANCE_TIME);
-            if (originalTime != null) {
-                final boolean originalAllDay =
-                    CalendarUtilities.getIntegerValueAsBoolean(entityValues,
-                            Events.ORIGINAL_ALL_DAY);
-                if (originalAllDay) {
-                    // For all day events, we need our local all-day time
-                    originalTime =
-                        CalendarUtilities.getLocalAllDayCalendarTime(originalTime, mLocalTimeZone);
-                }
-                s.data(Tags.CALENDAR_EXCEPTION_START_TIME,
-                        CalendarUtilities.millisToEasDateTime(originalTime));
-            } else {
-                // Illegal; what should we do?
-            }
-        }
-
-        // Get the event's time zone
-        String timeZoneName =
-            entityValues.getAsString(allDay ? EVENT_SAVED_TIMEZONE_COLUMN : Events.EVENT_TIMEZONE);
-        if (timeZoneName == null) {
-            timeZoneName = mLocalTimeZone.getID();
-        }
-        TimeZone eventTimeZone = TimeZone.getTimeZone(timeZoneName);
-
-        if (!isException) {
-            // A time zone is required in all EAS events; we'll use the default if none is set
-            // Exchange 2003 seems to require this first... :-)
-            String timeZone = CalendarUtilities.timeZoneToTziString(eventTimeZone);
-            s.data(Tags.CALENDAR_TIME_ZONE, timeZone);
-        }
-
-        s.data(Tags.CALENDAR_ALL_DAY_EVENT, allDay ? "1" : "0");
-
-        // DTSTART is always supplied
-        long startTime = entityValues.getAsLong(Events.DTSTART);
-        // Determine endTime; it's either provided as DTEND or we calculate using DURATION
-        // If no DURATION is provided, we default to one hour
-        long endTime;
-        if (entityValues.containsKey(Events.DTEND)) {
-            endTime = entityValues.getAsLong(Events.DTEND);
-        } else {
-            long durationMillis = HOURS;
-            if (entityValues.containsKey(Events.DURATION)) {
-                Duration duration = new Duration();
-                try {
-                    duration.parse(entityValues.getAsString(Events.DURATION));
-                    durationMillis = duration.getMillis();
-                } catch (ParseException e) {
-                    // Can't do much about this; use the default (1 hour)
-                }
-            }
-            endTime = startTime + durationMillis;
-        }
-        if (allDay) {
-            TimeZone tz = mLocalTimeZone;
-            startTime = CalendarUtilities.getLocalAllDayCalendarTime(startTime, tz);
-            endTime = CalendarUtilities.getLocalAllDayCalendarTime(endTime, tz);
-        }
-        s.data(Tags.CALENDAR_START_TIME, CalendarUtilities.millisToEasDateTime(startTime));
-        s.data(Tags.CALENDAR_END_TIME, CalendarUtilities.millisToEasDateTime(endTime));
-
-        s.data(Tags.CALENDAR_DTSTAMP,
-                CalendarUtilities.millisToEasDateTime(System.currentTimeMillis()));
-
-        String loc = entityValues.getAsString(Events.EVENT_LOCATION);
-        if (!TextUtils.isEmpty(loc)) {
-            if (version < Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) {
-                // EAS 2.5 doesn't like bare line feeds
-                loc = Utility.replaceBareLfWithCrlf(loc);
-            }
-            s.data(Tags.CALENDAR_LOCATION, loc);
-        }
-        s.writeStringValue(entityValues, Events.TITLE, Tags.CALENDAR_SUBJECT);
-
-        if (version >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) {
-            s.start(Tags.BASE_BODY);
-            s.data(Tags.BASE_TYPE, "1");
-            s.writeStringValue(entityValues, Events.DESCRIPTION, Tags.BASE_DATA);
-            s.end();
-        } else {
-            // EAS 2.5 doesn't like bare line feeds
-            s.writeStringValue(entityValues, Events.DESCRIPTION, Tags.CALENDAR_BODY);
-        }
-
-        if (!isException) {
-            // For Exchange 2003, only upsync if the event is new
-            if ((version >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) || !isChange) {
-                s.writeStringValue(entityValues, Events.ORGANIZER, Tags.CALENDAR_ORGANIZER_EMAIL);
-            }
-
-            String rrule = entityValues.getAsString(Events.RRULE);
-            if (rrule != null) {
-                CalendarUtilities.recurrenceFromRrule(rrule, startTime, s);
-            }
-
-            // Handle associated data EXCEPT for attendees, which have to be grouped
-            ArrayList<NamedContentValues> subValues = entity.getSubValues();
-            // The earliest of the reminders for this Event; we can only send one reminder...
-            int earliestReminder = -1;
-            for (NamedContentValues ncv: subValues) {
-                Uri ncvUri = ncv.uri;
-                ContentValues ncvValues = ncv.values;
-                if (ncvUri.equals(ExtendedProperties.CONTENT_URI)) {
-                    String propertyName =
-                        ncvValues.getAsString(ExtendedProperties.NAME);
-                    String propertyValue =
-                        ncvValues.getAsString(ExtendedProperties.VALUE);
-                    if (TextUtils.isEmpty(propertyValue)) {
-                        continue;
-                    }
-                    if (propertyName.equals(EXTENDED_PROPERTY_CATEGORIES)) {
-                        // Send all the categories back to the server
-                        // We've saved them as a String of delimited tokens
-                        StringTokenizer st =
-                            new StringTokenizer(propertyValue, CATEGORY_TOKENIZER_DELIMITER);
-                        if (st.countTokens() > 0) {
-                            s.start(Tags.CALENDAR_CATEGORIES);
-                            while (st.hasMoreTokens()) {
-                                String category = st.nextToken();
-                                s.data(Tags.CALENDAR_CATEGORY, category);
-                            }
-                            s.end();
-                        }
-                    }
-                } else if (ncvUri.equals(Reminders.CONTENT_URI)) {
-                    Integer mins = ncvValues.getAsInteger(Reminders.MINUTES);
-                    if (mins != null) {
-                        // -1 means "default", which for Exchange, is 30
-                        if (mins < 0) {
-                            mins = 30;
-                        }
-                        // Save this away if it's the earliest reminder (greatest minutes)
-                        if (mins > earliestReminder) {
-                            earliestReminder = mins;
-                        }
-                    }
-                }
-            }
-
-            // If we have a reminder, send it to the server
-            if (earliestReminder >= 0) {
-                s.data(Tags.CALENDAR_REMINDER_MINS_BEFORE, Integer.toString(earliestReminder));
-            }
-
-            // We've got to send a UID, unless this is an exception.  If the event is new, we've
-            // generated one; if not, we should have gotten one from extended properties.
-            if (clientId != null) {
-                s.data(Tags.CALENDAR_UID, clientId);
-            }
-
-            // Handle attendee data here; keep track of organizer and stream it afterward
-            String organizerName = null;
-            String organizerEmail = null;
-            for (NamedContentValues ncv: subValues) {
-                Uri ncvUri = ncv.uri;
-                ContentValues ncvValues = ncv.values;
-                if (ncvUri.equals(Attendees.CONTENT_URI)) {
-                    Integer relationship = ncvValues.getAsInteger(Attendees.ATTENDEE_RELATIONSHIP);
-                    // If there's no relationship, we can't create this for EAS
-                    // Similarly, we need an attendee email for each invitee
-                    if (relationship != null && ncvValues.containsKey(Attendees.ATTENDEE_EMAIL)) {
-                        // Organizer isn't among attendees in EAS
-                        if (relationship == Attendees.RELATIONSHIP_ORGANIZER) {
-                            organizerName = ncvValues.getAsString(Attendees.ATTENDEE_NAME);
-                            organizerEmail = ncvValues.getAsString(Attendees.ATTENDEE_EMAIL);
-                            continue;
-                        }
-                        if (!hasAttendees) {
-                            s.start(Tags.CALENDAR_ATTENDEES);
-                            hasAttendees = true;
-                        }
-                        s.start(Tags.CALENDAR_ATTENDEE);
-                        String attendeeEmail = ncvValues.getAsString(Attendees.ATTENDEE_EMAIL);
-                        String attendeeName = ncvValues.getAsString(Attendees.ATTENDEE_NAME);
-                        if (attendeeName == null) {
-                            attendeeName = attendeeEmail;
-                        }
-                        s.data(Tags.CALENDAR_ATTENDEE_NAME, attendeeName);
-                        s.data(Tags.CALENDAR_ATTENDEE_EMAIL, attendeeEmail);
-                        if (version >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) {
-                            s.data(Tags.CALENDAR_ATTENDEE_TYPE, "1"); // Required
-                        }
-                        s.end(); // Attendee
-                     }
-                }
-            }
-            if (hasAttendees) {
-                s.end();  // Attendees
-            }
-
-            // Get busy status from Attendees table
-            long eventId = entityValues.getAsLong(Events._ID);
-            int busyStatus = CalendarUtilities.BUSY_STATUS_TENTATIVE;
-            Cursor c = mService.mContentResolver.query(
-                    asSyncAdapter(Attendees.CONTENT_URI, mEmailAddress,
-                            Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE),
-                    ATTENDEE_STATUS_PROJECTION, EVENT_AND_EMAIL,
-                    new String[] {Long.toString(eventId), mEmailAddress}, null);
-            if (c != null) {
-                try {
-                    if (c.moveToFirst()) {
-                        busyStatus = CalendarUtilities.busyStatusFromAttendeeStatus(
-                                c.getInt(ATTENDEE_STATUS_COLUMN_STATUS));
-                    }
-                } finally {
-                    c.close();
-                }
-            }
-            s.data(Tags.CALENDAR_BUSY_STATUS, Integer.toString(busyStatus));
-
-            // Meeting status, 0 = appointment, 1 = meeting, 3 = attendee
-            if (mEmailAddress.equalsIgnoreCase(organizerEmail)) {
-                s.data(Tags.CALENDAR_MEETING_STATUS, hasAttendees ? "1" : "0");
-            } else {
-                s.data(Tags.CALENDAR_MEETING_STATUS, "3");
-            }
-
-            // For Exchange 2003, only upsync if the event is new
-            if (((version >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) || !isChange) &&
-                    organizerName != null) {
-                s.data(Tags.CALENDAR_ORGANIZER_NAME, organizerName);
-            }
-
-            // NOTE: Sensitivity must NOT be sent to the server for exceptions in Exchange 2003
-            // The result will be a status 6 failure during sync
-            Integer visibility = entityValues.getAsInteger(Events.ACCESS_LEVEL);
-            if (visibility != null) {
-                s.data(Tags.CALENDAR_SENSITIVITY, decodeVisibility(visibility));
-            } else {
-                // Default to private if not set
-                s.data(Tags.CALENDAR_SENSITIVITY, "1");
-            }
-        }
-    }
-
-    /**
-     * Convenience method for sending an email to the organizer declining the meeting
-     * @param entity
-     * @param clientId
-     */
-    private void sendDeclinedEmail(Entity entity, String clientId) {
-        Message msg =
-            CalendarUtilities.createMessageForEntity(mContext, entity,
-                    Message.FLAG_OUTGOING_MEETING_DECLINE, clientId, mAccount);
-        if (msg != null) {
-            userLog("Queueing declined response to " + msg.mTo);
-            mOutgoingMailList.add(msg);
-        }
-    }
-
-    @Override
-    public boolean sendLocalChanges(Serializer s) throws IOException {
-        ContentResolver cr = mService.mContentResolver;
-
-        if (getSyncKey().equals("0")) {
-            return false;
-        }
-
-        try {
-            // We've got to handle exceptions as part of the parent when changes occur, so we need
-            // to find new/changed exceptions and mark the parent dirty
-            ArrayList<Long> orphanedExceptions = new ArrayList<Long>();
-            Cursor c = cr.query(Events.CONTENT_URI, ORIGINAL_EVENT_PROJECTION,
-                    DIRTY_EXCEPTION_IN_CALENDAR, mCalendarIdArgument, null);
-            try {
-                ContentValues cv = new ContentValues();
-                // We use _sync_mark here to distinguish dirty parents from parents with dirty
-                // exceptions
-                cv.put(EVENT_SYNC_MARK, "1");
-                while (c.moveToNext()) {
-                    // Mark the parents of dirty exceptions
-                    long parentId = c.getLong(0);
-                    int cnt = cr.update(
-                            asSyncAdapter(Events.CONTENT_URI, mEmailAddress,
-                                    Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), cv,
-                            EVENT_ID_AND_CALENDAR_ID, new String[] {
-                                    Long.toString(parentId), mCalendarIdString
-                            });
-                    // Keep track of any orphaned exceptions
-                    if (cnt == 0) {
-                        orphanedExceptions.add(c.getLong(1));
-                    }
-                }
-            } finally {
-                c.close();
-            }
-
-            // Delete any orphaned exceptions
-            for (long orphan : orphanedExceptions) {
-                userLog(TAG, "Deleted orphaned exception: " + orphan);
-                cr.delete(
-                        asSyncAdapter(ContentUris.withAppendedId(Events.CONTENT_URI, orphan),
-                                mEmailAddress, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), null, null);
-            }
-            orphanedExceptions.clear();
-
-            // Now we can go through dirty/marked top-level events and send them
-            // back to the server
-            EntityIterator eventIterator = EventsEntity.newEntityIterator(cr.query(
-                    asSyncAdapter(Events.CONTENT_URI, mEmailAddress,
-                            Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), null,
-                    DIRTY_OR_MARKED_TOP_LEVEL_IN_CALENDAR, mCalendarIdArgument, null), cr);
-            ContentValues cidValues = new ContentValues();
-
-            try {
-                boolean first = true;
-                while (eventIterator.hasNext()) {
-                    Entity entity = eventIterator.next();
-
-                    // For each of these entities, create the change commands
-                    ContentValues entityValues = entity.getEntityValues();
-                    String serverId = entityValues.getAsString(Events._SYNC_ID);
-
-                    // We first need to check whether we can upsync this event; our test for this
-                    // is currently the value of EXTENDED_PROPERTY_ATTENDEES_REDACTED
-                    // If this is set to "1", we can't upsync the event
-                    for (NamedContentValues ncv: entity.getSubValues()) {
-                        if (ncv.uri.equals(ExtendedProperties.CONTENT_URI)) {
-                            ContentValues ncvValues = ncv.values;
-                            if (ncvValues.getAsString(ExtendedProperties.NAME).equals(
-                                    EXTENDED_PROPERTY_UPSYNC_PROHIBITED)) {
-                                if ("1".equals(ncvValues.getAsString(ExtendedProperties.VALUE))) {
-                                    // Make sure we mark this to clear the dirty flag
-                                    mUploadedIdList.add(entityValues.getAsLong(Events._ID));
-                                    continue;
-                                }
-                            }
-                        }
-                    }
-
-                    // Find our uid in the entity; otherwise create one
-                    String clientId = entityValues.getAsString(Events.SYNC_DATA2);
-                    if (clientId == null) {
-                        clientId = UUID.randomUUID().toString();
-                    }
-
-                    // EAS 2.5 needs: BusyStatus DtStamp EndTime Sensitivity StartTime TimeZone UID
-                    // We can generate all but what we're testing for below
-                    String organizerEmail = entityValues.getAsString(Events.ORGANIZER);
-                    boolean selfOrganizer = organizerEmail.equalsIgnoreCase(mEmailAddress);
-
-                    if (!entityValues.containsKey(Events.DTSTART)
-                            || (!entityValues.containsKey(Events.DURATION) &&
-                                    !entityValues.containsKey(Events.DTEND))
-                                    || organizerEmail == null) {
-                        continue;
-                    }
-
-                    if (first) {
-                        s.start(Tags.SYNC_COMMANDS);
-                        userLog("Sending Calendar changes to the server");
-                        first = false;
-                    }
-                    long eventId = entityValues.getAsLong(Events._ID);
-                    if (serverId == null) {
-                        // This is a new event; create a clientId
-                        userLog("Creating new event with clientId: ", clientId);
-                        s.start(Tags.SYNC_ADD).data(Tags.SYNC_CLIENT_ID, clientId);
-                        // And save it in the Event as the local id
-                        cidValues.put(Events.SYNC_DATA2, clientId);
-                        cidValues.put(EVENT_SYNC_VERSION, "0");
-                        cr.update(
-                                asSyncAdapter(
-                                        ContentUris.withAppendedId(Events.CONTENT_URI, eventId),
-                                        mEmailAddress, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE),
-                                cidValues, null, null);
-                    } else {
-                        if (entityValues.getAsInteger(Events.DELETED) == 1) {
-                            userLog("Deleting event with serverId: ", serverId);
-                            s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, serverId).end();
-                            mDeletedIdList.add(eventId);
-                            if (selfOrganizer) {
-                                mSendCancelIdList.add(eventId);
-                            } else {
-                                sendDeclinedEmail(entity, clientId);
-                            }
-                            continue;
-                        }
-                        userLog("Upsync change to event with serverId: " + serverId);
-                        // Get the current version
-                        String version = entityValues.getAsString(EVENT_SYNC_VERSION);
-                        // This should never be null, but catch this error anyway
-                        // Version should be "0" when we create the event, so use that
-                        if (version == null) {
-                            version = "0";
-                        } else {
-                            // Increment and save
-                            try {
-                                version = Integer.toString((Integer.parseInt(version) + 1));
-                            } catch (Exception e) {
-                                // Handle the case in which someone writes a non-integer here;
-                                // shouldn't happen, but we don't want to kill the sync for his
-                                version = "0";
-                            }
-                        }
-                        cidValues.put(EVENT_SYNC_VERSION, version);
-                        // Also save in entityValues so that we send it this time around
-                        entityValues.put(EVENT_SYNC_VERSION, version);
-                        cr.update(
-                                asSyncAdapter(
-                                        ContentUris.withAppendedId(Events.CONTENT_URI, eventId),
-                                        mEmailAddress, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE),
-                                cidValues, null, null);
-                        s.start(Tags.SYNC_CHANGE).data(Tags.SYNC_SERVER_ID, serverId);
-                    }
-                    s.start(Tags.SYNC_APPLICATION_DATA);
-
-                    sendEvent(entity, clientId, s);
-
-                    // Now, the hard part; find exceptions for this event
-                    if (serverId != null) {
-                        EntityIterator exIterator = EventsEntity.newEntityIterator(cr.query(
-                                asSyncAdapter(Events.CONTENT_URI, mEmailAddress,
-                                        Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), null,
-                                ORIGINAL_EVENT_AND_CALENDAR, new String[] {
-                                        serverId, mCalendarIdString
-                                }, null), cr);
-                        boolean exFirst = true;
-                        while (exIterator.hasNext()) {
-                            Entity exEntity = exIterator.next();
-                            if (exFirst) {
-                                s.start(Tags.CALENDAR_EXCEPTIONS);
-                                exFirst = false;
-                            }
-                            s.start(Tags.CALENDAR_EXCEPTION);
-                            sendEvent(exEntity, null, s);
-                            ContentValues exValues = exEntity.getEntityValues();
-                            if (getInt(exValues, Events.DIRTY) == 1) {
-                                // This is a new/updated exception, so we've got to notify our
-                                // attendees about it
-                                long exEventId = exValues.getAsLong(Events._ID);
-                                int flag;
-
-                                // Copy subvalues into the exception; otherwise, we won't see the
-                                // attendees when preparing the message
-                                for (NamedContentValues ncv: entity.getSubValues()) {
-                                    exEntity.addSubValue(ncv.uri, ncv.values);
-                                }
-
-                                if ((getInt(exValues, Events.DELETED) == 1) ||
-                                        (getInt(exValues, Events.STATUS) ==
-                                            Events.STATUS_CANCELED)) {
-                                    flag = Message.FLAG_OUTGOING_MEETING_CANCEL;
-                                    if (!selfOrganizer) {
-                                        // Send a cancellation notice to the organizer
-                                        // Since CalendarProvider2 sets the organizer of exceptions
-                                        // to the user, we have to reset it first to the original
-                                        // organizer
-                                        exValues.put(Events.ORGANIZER,
-                                                entityValues.getAsString(Events.ORGANIZER));
-                                        sendDeclinedEmail(exEntity, clientId);
-                                    }
-                                } else {
-                                    flag = Message.FLAG_OUTGOING_MEETING_INVITE;
-                                }
-                                // Add the eventId of the exception to the uploaded id list, so that
-                                // the dirty/mark bits are cleared
-                                mUploadedIdList.add(exEventId);
-
-                                // Copy version so the ics attachment shows the proper sequence #
-                                exValues.put(EVENT_SYNC_VERSION,
-                                        entityValues.getAsString(EVENT_SYNC_VERSION));
-                                // Copy location so that it's included in the outgoing email
-                                if (entityValues.containsKey(Events.EVENT_LOCATION)) {
-                                    exValues.put(Events.EVENT_LOCATION,
-                                            entityValues.getAsString(Events.EVENT_LOCATION));
-                                }
-
-                                if (selfOrganizer) {
-                                    Message msg =
-                                        CalendarUtilities.createMessageForEntity(mContext,
-                                                exEntity, flag, clientId, mAccount);
-                                    if (msg != null) {
-                                        userLog("Queueing exception update to " + msg.mTo);
-                                        mOutgoingMailList.add(msg);
-                                    }
-                                }
-                            }
-                            s.end(); // EXCEPTION
-                        }
-                        if (!exFirst) {
-                            s.end(); // EXCEPTIONS
-                        }
-                    }
-
-                    s.end().end(); // ApplicationData & Change
-                    mUploadedIdList.add(eventId);
-
-                    // Go through the extended properties of this Event and pull out our tokenized
-                    // attendees list and the user attendee status; we will need them later
-                    String attendeeString = null;
-                    long attendeeStringId = -1;
-                    String userAttendeeStatus = null;
-                    long userAttendeeStatusId = -1;
-                    for (NamedContentValues ncv: entity.getSubValues()) {
-                        if (ncv.uri.equals(ExtendedProperties.CONTENT_URI)) {
-                            ContentValues ncvValues = ncv.values;
-                            String propertyName =
-                                ncvValues.getAsString(ExtendedProperties.NAME);
-                            if (propertyName.equals(EXTENDED_PROPERTY_ATTENDEES)) {
-                                attendeeString =
-                                    ncvValues.getAsString(ExtendedProperties.VALUE);
-                                attendeeStringId =
-                                    ncvValues.getAsLong(ExtendedProperties._ID);
-                            } else if (propertyName.equals(
-                                    EXTENDED_PROPERTY_USER_ATTENDEE_STATUS)) {
-                                userAttendeeStatus =
-                                    ncvValues.getAsString(ExtendedProperties.VALUE);
-                                userAttendeeStatusId =
-                                    ncvValues.getAsLong(ExtendedProperties._ID);
-                            }
-                        }
-                    }
-
-                    // Send the meeting invite if there are attendees and we're the organizer AND
-                    // if the Event itself is dirty (we might be syncing only because an exception
-                    // is dirty, in which case we DON'T send email about the Event)
-                    if (selfOrganizer &&
-                            (getInt(entityValues, Events.DIRTY) == 1)) {
-                        EmailContent.Message msg =
-                            CalendarUtilities.createMessageForEventId(mContext, eventId,
-                                    EmailContent.Message.FLAG_OUTGOING_MEETING_INVITE, clientId,
-                                    mAccount);
-                        if (msg != null) {
-                            userLog("Queueing invitation to ", msg.mTo);
-                            mOutgoingMailList.add(msg);
-                        }
-                        // Make a list out of our tokenized attendees, if we have any
-                        ArrayList<String> originalAttendeeList = new ArrayList<String>();
-                        if (attendeeString != null) {
-                            StringTokenizer st =
-                                new StringTokenizer(attendeeString, ATTENDEE_TOKENIZER_DELIMITER);
-                            while (st.hasMoreTokens()) {
-                                originalAttendeeList.add(st.nextToken());
-                            }
-                        }
-                        StringBuilder newTokenizedAttendees = new StringBuilder();
-                        // See if any attendees have been dropped and while we're at it, build
-                        // an updated String with tokenized attendee addresses
-                        for (NamedContentValues ncv: entity.getSubValues()) {
-                            if (ncv.uri.equals(Attendees.CONTENT_URI)) {
-                                String attendeeEmail =
-                                    ncv.values.getAsString(Attendees.ATTENDEE_EMAIL);
-                                // Remove all found attendees
-                                originalAttendeeList.remove(attendeeEmail);
-                                newTokenizedAttendees.append(attendeeEmail);
-                                newTokenizedAttendees.append(ATTENDEE_TOKENIZER_DELIMITER);
-                            }
-                        }
-                        // Update extended properties with the new attendee list, if we have one
-                        // Otherwise, create one (this would be the case for Events created on
-                        // device or "legacy" events (before this code was added)
-                        ContentValues cv = new ContentValues();
-                        cv.put(ExtendedProperties.VALUE, newTokenizedAttendees.toString());
-                        if (attendeeString != null) {
-                            cr.update(asSyncAdapter(ContentUris.withAppendedId(
-                                    ExtendedProperties.CONTENT_URI, attendeeStringId),
-                                    mEmailAddress, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE),
-                                    cv, null, null);
-                        } else {
-                            // If there wasn't an "attendees" property, insert one
-                            cv.put(ExtendedProperties.NAME, EXTENDED_PROPERTY_ATTENDEES);
-                            cv.put(ExtendedProperties.EVENT_ID, eventId);
-                            cr.insert(asSyncAdapter(ExtendedProperties.CONTENT_URI,
-                                    mEmailAddress, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), cv);
-                        }
-                        // Whoever is left has been removed from the attendee list; send them
-                        // a cancellation
-                        for (String removedAttendee: originalAttendeeList) {
-                            // Send a cancellation message to each of them
-                            msg = CalendarUtilities.createMessageForEventId(mContext, eventId,
-                                    Message.FLAG_OUTGOING_MEETING_CANCEL, clientId, mAccount,
-                                    removedAttendee);
-                            if (msg != null) {
-                                // Just send it to the removed attendee
-                                userLog("Queueing cancellation to removed attendee " + msg.mTo);
-                                mOutgoingMailList.add(msg);
-                            }
-                        }
-                    } else if (!selfOrganizer) {
-                        // If we're not the organizer, see if we've changed our attendee status
-                        // Our last synced attendee status is in ExtendedProperties, and we've
-                        // retrieved it above as userAttendeeStatus
-                        int currentStatus = entityValues.getAsInteger(Events.SELF_ATTENDEE_STATUS);
-                        int syncStatus = Attendees.ATTENDEE_STATUS_NONE;
-                        if (userAttendeeStatus != null) {
-                            try {
-                                syncStatus = Integer.parseInt(userAttendeeStatus);
-                            } catch (NumberFormatException e) {
-                                // Just in case somebody else mucked with this and it's not Integer
-                            }
-                        }
-                        if ((currentStatus != syncStatus) &&
-                                (currentStatus != Attendees.ATTENDEE_STATUS_NONE)) {
-                            // If so, send a meeting reply
-                            int messageFlag = 0;
-                            switch (currentStatus) {
-                                case Attendees.ATTENDEE_STATUS_ACCEPTED:
-                                    messageFlag = Message.FLAG_OUTGOING_MEETING_ACCEPT;
-                                    break;
-                                case Attendees.ATTENDEE_STATUS_DECLINED:
-                                    messageFlag = Message.FLAG_OUTGOING_MEETING_DECLINE;
-                                    break;
-                                case Attendees.ATTENDEE_STATUS_TENTATIVE:
-                                    messageFlag = Message.FLAG_OUTGOING_MEETING_TENTATIVE;
-                                    break;
-                            }
-                            // Make sure we have a valid status (messageFlag should never be zero)
-                            if (messageFlag != 0 && userAttendeeStatusId >= 0) {
-                                // Save away the new status
-                                cidValues.clear();
-                                cidValues.put(ExtendedProperties.VALUE,
-                                        Integer.toString(currentStatus));
-                                cr.update(asSyncAdapter(ContentUris.withAppendedId(
-                                        ExtendedProperties.CONTENT_URI, userAttendeeStatusId),
-                                        mEmailAddress, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE),
-                                        cidValues, null, null);
-                                // Send mail to the organizer advising of the new status
-                                EmailContent.Message msg =
-                                    CalendarUtilities.createMessageForEventId(mContext, eventId,
-                                            messageFlag, clientId, mAccount);
-                                if (msg != null) {
-                                    userLog("Queueing invitation reply to " + msg.mTo);
-                                    mOutgoingMailList.add(msg);
-                                }
-                            }
-                        }
-                    }
-                }
-                if (!first) {
-                    s.end(); // Commands
-                }
-            } finally {
-                eventIterator.close();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Could not read dirty events.");
-        }
-
-        return false;
-    }
-}
diff --git a/src/com/android/exchange/adapter/ContactsSyncAdapter.java b/src/com/android/exchange/adapter/ContactsSyncAdapter.java
deleted file mode 100644
index ec0299b..0000000
--- a/src/com/android/exchange/adapter/ContactsSyncAdapter.java
+++ /dev/null
@@ -1,1928 +0,0 @@
-/*
- * 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 com.android.exchange.CommandStatusException;
-import com.android.exchange.Eas;
-import com.android.exchange.EasSyncService;
-
-import android.content.ContentProviderClient;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderOperation.Builder;
-import android.content.ContentProviderResult;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Entity;
-import android.content.Entity.NamedContentValues;
-import android.content.EntityIterator;
-import android.content.OperationApplicationException;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Event;
-import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
-import android.provider.ContactsContract.CommonDataKinds.Im;
-import android.provider.ContactsContract.CommonDataKinds.Nickname;
-import android.provider.ContactsContract.CommonDataKinds.Note;
-import android.provider.ContactsContract.CommonDataKinds.Organization;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.CommonDataKinds.Relation;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.provider.ContactsContract.CommonDataKinds.Website;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.Groups;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.RawContactsEntity;
-import android.provider.ContactsContract.Settings;
-import android.provider.ContactsContract.SyncState;
-import android.provider.SyncStateContract;
-import android.text.TextUtils;
-import android.text.util.Rfc822Token;
-import android.text.util.Rfc822Tokenizer;
-import android.util.Base64;
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-
-/**
- * Sync adapter for EAS Contacts
- *
- */
-public class ContactsSyncAdapter extends AbstractSyncAdapter {
-
-    private static final String TAG = "EasContactsSyncAdapter";
-    private static final String SERVER_ID_SELECTION = RawContacts.SOURCE_ID + "=?";
-    private static final String CLIENT_ID_SELECTION = RawContacts.SYNC1 + "=?";
-    private static final String[] ID_PROJECTION = new String[] {RawContacts._ID};
-    private static final String[] GROUP_TITLE_PROJECTION = new String[] {Groups.TITLE};
-    private static final String MIMETYPE_GROUP_MEMBERSHIP_AND_ID_EQUALS = Data.MIMETYPE + "='" +
-        GroupMembership.CONTENT_ITEM_TYPE + "' AND " + GroupMembership.GROUP_ROW_ID + "=?";
-    private static final String[] GROUPS_ID_PROJECTION = new String[] {Groups._ID};
-
-    private static final ArrayList<NamedContentValues> EMPTY_ARRAY_NAMEDCONTENTVALUES
-        = new ArrayList<NamedContentValues>();
-
-    private static final String FOUND_DATA_ROW = "com.android.exchange.FOUND_ROW";
-
-    private static final int[] HOME_ADDRESS_TAGS = new int[] {Tags.CONTACTS_HOME_ADDRESS_CITY,
-        Tags.CONTACTS_HOME_ADDRESS_COUNTRY,
-        Tags.CONTACTS_HOME_ADDRESS_POSTAL_CODE,
-        Tags.CONTACTS_HOME_ADDRESS_STATE,
-        Tags.CONTACTS_HOME_ADDRESS_STREET};
-
-    private static final int[] WORK_ADDRESS_TAGS = new int[] {Tags.CONTACTS_BUSINESS_ADDRESS_CITY,
-        Tags.CONTACTS_BUSINESS_ADDRESS_COUNTRY,
-        Tags.CONTACTS_BUSINESS_ADDRESS_POSTAL_CODE,
-        Tags.CONTACTS_BUSINESS_ADDRESS_STATE,
-        Tags.CONTACTS_BUSINESS_ADDRESS_STREET};
-
-    private static final int[] OTHER_ADDRESS_TAGS = new int[] {Tags.CONTACTS_HOME_ADDRESS_CITY,
-        Tags.CONTACTS_OTHER_ADDRESS_COUNTRY,
-        Tags.CONTACTS_OTHER_ADDRESS_POSTAL_CODE,
-        Tags.CONTACTS_OTHER_ADDRESS_STATE,
-        Tags.CONTACTS_OTHER_ADDRESS_STREET};
-
-    private static final int MAX_IM_ROWS = 3;
-    private static final int MAX_EMAIL_ROWS = 3;
-    private static final int MAX_PHONE_ROWS = 2;
-    private static final String COMMON_DATA_ROW = Im.DATA;  // Could have been Email.DATA, etc.
-    private static final String COMMON_TYPE_ROW = Phone.TYPE; // Could have been any typed row
-
-    private static final int[] IM_TAGS = new int[] {Tags.CONTACTS2_IM_ADDRESS,
-        Tags.CONTACTS2_IM_ADDRESS_2, Tags.CONTACTS2_IM_ADDRESS_3};
-
-    private static final int[] EMAIL_TAGS = new int[] {Tags.CONTACTS_EMAIL1_ADDRESS,
-        Tags.CONTACTS_EMAIL2_ADDRESS, Tags.CONTACTS_EMAIL3_ADDRESS};
-
-    private static final int[] WORK_PHONE_TAGS = new int[] {Tags.CONTACTS_BUSINESS_TELEPHONE_NUMBER,
-        Tags.CONTACTS_BUSINESS2_TELEPHONE_NUMBER};
-
-    private static final int[] HOME_PHONE_TAGS = new int[] {Tags.CONTACTS_HOME_TELEPHONE_NUMBER,
-        Tags.CONTACTS_HOME2_TELEPHONE_NUMBER};
-
-    private static final Object sSyncKeyLock = new Object();
-
-    ArrayList<Long> mDeletedIdList = new ArrayList<Long>();
-    ArrayList<Long> mUpdatedIdList = new ArrayList<Long>();
-
-    private final Uri mAccountUri;
-    private final ContentResolver mContentResolver;
-    private boolean mGroupsUsed = false;
-
-    public ContactsSyncAdapter(EasSyncService service) {
-        super(service);
-        mAccountUri = uriWithAccountAndIsSyncAdapter(RawContacts.CONTENT_URI);
-        mContentResolver = mContext.getContentResolver();
-    }
-
-    static Uri addCallerIsSyncAdapterParameter(Uri uri) {
-        return uri.buildUpon()
-                .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
-                .build();
-    }
-
-    @Override
-    public void sendSyncOptions(Double protocolVersion, Serializer s) throws IOException  {
-        setPimSyncOptions(protocolVersion, null, s);
-    }
-
-    @Override
-    public boolean isSyncable() {
-        return ContentResolver.getSyncAutomatically(
-                mAccountManagerAccount, ContactsContract.AUTHORITY);
-    }
-
-    @Override
-    public boolean parse(InputStream is) throws IOException, CommandStatusException {
-        EasContactsSyncParser p = new EasContactsSyncParser(is, this);
-        return p.parse();
-    }
-
-
-    @Override
-    public void wipe() {
-        mContentResolver.delete(mAccountUri, null, null);
-    }
-
-    interface UntypedRow {
-        public void addValues(RowBuilder builder);
-        public boolean isSameAs(int type, String value);
-    }
-
-    /**
-     * We get our SyncKey from ContactsProvider.  If there's not one, we set it to "0" (the reset
-     * state) and save that away.
-     */
-    @Override
-    public String getSyncKey() throws IOException {
-        synchronized (sSyncKeyLock) {
-            ContentProviderClient client = mService.mContentResolver
-                    .acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
-            try {
-                byte[] data = SyncStateContract.Helpers.get(client,
-                        ContactsContract.SyncState.CONTENT_URI, mAccountManagerAccount);
-                if (data == null || data.length == 0) {
-                    // Initialize the SyncKey
-                    setSyncKey("0", false);
-                    // Make sure ungrouped contacts for Exchange are defaultly visible
-                    ContentValues cv = new ContentValues();
-                    cv.put(Groups.ACCOUNT_NAME, mAccount.mEmailAddress);
-                    cv.put(Groups.ACCOUNT_TYPE, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE);
-                    cv.put(Settings.UNGROUPED_VISIBLE, true);
-                    client.insert(addCallerIsSyncAdapterParameter(Settings.CONTENT_URI), cv);
-                    return "0";
-                } else {
-                    return new String(data);
-                }
-            } catch (RemoteException e) {
-                throw new IOException("Can't get SyncKey from ContactsProvider");
-            }
-        }
-    }
-
-    /**
-     * We only need to set this when we're forced to make the SyncKey "0" (a reset).  In all other
-     * cases, the SyncKey is set within ContactOperations
-     */
-    @Override
-    public void setSyncKey(String syncKey, boolean inCommands) throws IOException {
-        synchronized (sSyncKeyLock) {
-            if ("0".equals(syncKey) || !inCommands) {
-                ContentProviderClient client = mService.mContentResolver
-                        .acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
-                try {
-                    SyncStateContract.Helpers.set(client, ContactsContract.SyncState.CONTENT_URI,
-                            mAccountManagerAccount, syncKey.getBytes());
-                    userLog("SyncKey set to ", syncKey, " in ContactsProvider");
-                } catch (RemoteException e) {
-                    throw new IOException("Can't set SyncKey in ContactsProvider");
-                }
-            }
-            mMailbox.mSyncKey = syncKey;
-        }
-    }
-
-    public static final class EasChildren {
-        private EasChildren() {}
-
-        /** MIME type used when storing this in data table. */
-        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/eas_children";
-        public static final int MAX_CHILDREN = 8;
-        public static final String[] ROWS =
-            new String[] {"data2", "data3", "data4", "data5", "data6", "data7", "data8", "data9"};
-    }
-
-    public static final class EasPersonal {
-        String anniversary;
-        String fileAs;
-
-            /** MIME type used when storing this in data table. */
-        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/eas_personal";
-        public static final String ANNIVERSARY = "data2";
-        public static final String FILE_AS = "data4";
-
-        boolean hasData() {
-            return anniversary != null || fileAs != null;
-        }
-    }
-
-    public static final class EasBusiness {
-        String customerId;
-        String governmentId;
-        String accountName;
-
-        /** MIME type used when storing this in data table. */
-        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/eas_business";
-        public static final String CUSTOMER_ID = "data6";
-        public static final String GOVERNMENT_ID = "data7";
-        public static final String ACCOUNT_NAME = "data8";
-
-        boolean hasData() {
-            return customerId != null || governmentId != null || accountName != null;
-        }
-    }
-
-    public static final class Address {
-        String city;
-        String country;
-        String code;
-        String street;
-        String state;
-
-        boolean hasData() {
-            return city != null || country != null || code != null || state != null
-                || street != null;
-        }
-    }
-
-    class EmailRow implements UntypedRow {
-        String email;
-        String displayName;
-
-        public EmailRow(String _email) {
-            Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(_email);
-            // Can't happen, but belt & suspenders
-            if (tokens.length == 0) {
-                email = "";
-                displayName = "";
-            } else {
-                Rfc822Token token = tokens[0];
-                email = token.getAddress();
-                displayName = token.getName();
-            }
-        }
-
-        public void addValues(RowBuilder builder) {
-            builder.withValue(Email.DATA, email);
-            builder.withValue(Email.DISPLAY_NAME, displayName);
-        }
-
-        public boolean isSameAs(int type, String value) {
-            return email.equalsIgnoreCase(value);
-        }
-    }
-
-    class ImRow implements UntypedRow {
-        String im;
-
-        public ImRow(String _im) {
-            im = _im;
-        }
-
-        public void addValues(RowBuilder builder) {
-            builder.withValue(Im.DATA, im);
-        }
-
-        public boolean isSameAs(int type, String value) {
-            return im.equalsIgnoreCase(value);
-        }
-    }
-
-    class PhoneRow implements UntypedRow {
-        String phone;
-        int type;
-
-        public PhoneRow(String _phone, int _type) {
-            phone = _phone;
-            type = _type;
-        }
-
-        public void addValues(RowBuilder builder) {
-            builder.withValue(Im.DATA, phone);
-            builder.withValue(Phone.TYPE, type);
-        }
-
-        public boolean isSameAs(int _type, String value) {
-            return type == _type && phone.equalsIgnoreCase(value);
-        }
-    }
-
-   class EasContactsSyncParser extends AbstractSyncParser {
-
-        String[] mBindArgument = new String[1];
-        String mMailboxIdAsString;
-        ContactOperations ops = new ContactOperations();
-
-        public EasContactsSyncParser(InputStream in, ContactsSyncAdapter adapter)
-                throws IOException {
-            super(in, adapter);
-        }
-
-        public void addData(String serverId, ContactOperations ops, Entity entity)
-                throws IOException {
-            String fileAs = null;
-            String prefix = null;
-            String firstName = null;
-            String lastName = null;
-            String middleName = null;
-            String suffix = null;
-            String companyName = null;
-            String yomiFirstName = null;
-            String yomiLastName = null;
-            String yomiCompanyName = null;
-            String title = null;
-            String department = null;
-            String officeLocation = null;
-            Address home = new Address();
-            Address work = new Address();
-            Address other = new Address();
-            EasBusiness business = new EasBusiness();
-            EasPersonal personal = new EasPersonal();
-            ArrayList<String> children = new ArrayList<String>();
-            ArrayList<UntypedRow> emails = new ArrayList<UntypedRow>();
-            ArrayList<UntypedRow> ims = new ArrayList<UntypedRow>();
-            ArrayList<UntypedRow> homePhones = new ArrayList<UntypedRow>();
-            ArrayList<UntypedRow> workPhones = new ArrayList<UntypedRow>();
-            if (entity == null) {
-                ops.newContact(serverId);
-            }
-
-            while (nextTag(Tags.SYNC_APPLICATION_DATA) != END) {
-                switch (tag) {
-                    case Tags.CONTACTS_FIRST_NAME:
-                        firstName = getValue();
-                        break;
-                    case Tags.CONTACTS_LAST_NAME:
-                        lastName = getValue();
-                        break;
-                    case Tags.CONTACTS_MIDDLE_NAME:
-                        middleName = getValue();
-                        break;
-                    case Tags.CONTACTS_FILE_AS:
-                        fileAs = getValue();
-                        break;
-                    case Tags.CONTACTS_SUFFIX:
-                        suffix = getValue();
-                        break;
-                    case Tags.CONTACTS_COMPANY_NAME:
-                        companyName = getValue();
-                        break;
-                    case Tags.CONTACTS_JOB_TITLE:
-                        title = getValue();
-                        break;
-                    case Tags.CONTACTS_EMAIL1_ADDRESS:
-                    case Tags.CONTACTS_EMAIL2_ADDRESS:
-                    case Tags.CONTACTS_EMAIL3_ADDRESS:
-                        emails.add(new EmailRow(getValue()));
-                        break;
-                    case Tags.CONTACTS_BUSINESS2_TELEPHONE_NUMBER:
-                    case Tags.CONTACTS_BUSINESS_TELEPHONE_NUMBER:
-                        workPhones.add(new PhoneRow(getValue(), Phone.TYPE_WORK));
-                        break;
-                    case Tags.CONTACTS2_MMS:
-                        ops.addPhone(entity, Phone.TYPE_MMS, getValue());
-                        break;
-                    case Tags.CONTACTS_BUSINESS_FAX_NUMBER:
-                        ops.addPhone(entity, Phone.TYPE_FAX_WORK, getValue());
-                        break;
-                    case Tags.CONTACTS2_COMPANY_MAIN_PHONE:
-                        ops.addPhone(entity, Phone.TYPE_COMPANY_MAIN, getValue());
-                        break;
-                    case Tags.CONTACTS_HOME_FAX_NUMBER:
-                        ops.addPhone(entity, Phone.TYPE_FAX_HOME, getValue());
-                        break;
-                    case Tags.CONTACTS_HOME_TELEPHONE_NUMBER:
-                    case Tags.CONTACTS_HOME2_TELEPHONE_NUMBER:
-                        homePhones.add(new PhoneRow(getValue(), Phone.TYPE_HOME));
-                        break;
-                    case Tags.CONTACTS_MOBILE_TELEPHONE_NUMBER:
-                        ops.addPhone(entity, Phone.TYPE_MOBILE, getValue());
-                        break;
-                    case Tags.CONTACTS_CAR_TELEPHONE_NUMBER:
-                        ops.addPhone(entity, Phone.TYPE_CAR, getValue());
-                        break;
-                    case Tags.CONTACTS_RADIO_TELEPHONE_NUMBER:
-                        ops.addPhone(entity, Phone.TYPE_RADIO, getValue());
-                        break;
-                    case Tags.CONTACTS_PAGER_NUMBER:
-                        ops.addPhone(entity, Phone.TYPE_PAGER, getValue());
-                        break;
-                    case Tags.CONTACTS_ASSISTANT_TELEPHONE_NUMBER:
-                        ops.addPhone(entity, Phone.TYPE_ASSISTANT, getValue());
-                        break;
-                    case Tags.CONTACTS2_IM_ADDRESS:
-                    case Tags.CONTACTS2_IM_ADDRESS_2:
-                    case Tags.CONTACTS2_IM_ADDRESS_3:
-                        ims.add(new ImRow(getValue()));
-                        break;
-                    case Tags.CONTACTS_BUSINESS_ADDRESS_CITY:
-                        work.city = getValue();
-                        break;
-                    case Tags.CONTACTS_BUSINESS_ADDRESS_COUNTRY:
-                        work.country = getValue();
-                        break;
-                    case Tags.CONTACTS_BUSINESS_ADDRESS_POSTAL_CODE:
-                        work.code = getValue();
-                        break;
-                    case Tags.CONTACTS_BUSINESS_ADDRESS_STATE:
-                        work.state = getValue();
-                        break;
-                    case Tags.CONTACTS_BUSINESS_ADDRESS_STREET:
-                        work.street = getValue();
-                        break;
-                    case Tags.CONTACTS_HOME_ADDRESS_CITY:
-                        home.city = getValue();
-                        break;
-                    case Tags.CONTACTS_HOME_ADDRESS_COUNTRY:
-                        home.country = getValue();
-                        break;
-                    case Tags.CONTACTS_HOME_ADDRESS_POSTAL_CODE:
-                        home.code = getValue();
-                        break;
-                    case Tags.CONTACTS_HOME_ADDRESS_STATE:
-                        home.state = getValue();
-                        break;
-                    case Tags.CONTACTS_HOME_ADDRESS_STREET:
-                        home.street = getValue();
-                        break;
-                    case Tags.CONTACTS_OTHER_ADDRESS_CITY:
-                        other.city = getValue();
-                        break;
-                    case Tags.CONTACTS_OTHER_ADDRESS_COUNTRY:
-                        other.country = getValue();
-                        break;
-                    case Tags.CONTACTS_OTHER_ADDRESS_POSTAL_CODE:
-                        other.code = getValue();
-                        break;
-                    case Tags.CONTACTS_OTHER_ADDRESS_STATE:
-                        other.state = getValue();
-                        break;
-                    case Tags.CONTACTS_OTHER_ADDRESS_STREET:
-                        other.street = getValue();
-                        break;
-
-                    case Tags.CONTACTS_CHILDREN:
-                        childrenParser(children);
-                        break;
-
-                    case Tags.CONTACTS_YOMI_COMPANY_NAME:
-                        yomiCompanyName = getValue();
-                        break;
-                    case Tags.CONTACTS_YOMI_FIRST_NAME:
-                        yomiFirstName = getValue();
-                        break;
-                    case Tags.CONTACTS_YOMI_LAST_NAME:
-                        yomiLastName = getValue();
-                        break;
-
-                    case Tags.CONTACTS2_NICKNAME:
-                        ops.addNickname(entity, getValue());
-                        break;
-
-                    case Tags.CONTACTS_ASSISTANT_NAME:
-                        ops.addRelation(entity, Relation.TYPE_ASSISTANT, getValue());
-                        break;
-                    case Tags.CONTACTS2_MANAGER_NAME:
-                        ops.addRelation(entity, Relation.TYPE_MANAGER, getValue());
-                        break;
-                    case Tags.CONTACTS_SPOUSE:
-                        ops.addRelation(entity, Relation.TYPE_SPOUSE, getValue());
-                        break;
-                    case Tags.CONTACTS_DEPARTMENT:
-                        department = getValue();
-                        break;
-                    case Tags.CONTACTS_TITLE:
-                        prefix = getValue();
-                        break;
-
-                    // EAS Business
-                    case Tags.CONTACTS_OFFICE_LOCATION:
-                        officeLocation = getValue();
-                        break;
-                    case Tags.CONTACTS2_CUSTOMER_ID:
-                        business.customerId = getValue();
-                        break;
-                    case Tags.CONTACTS2_GOVERNMENT_ID:
-                        business.governmentId = getValue();
-                        break;
-                    case Tags.CONTACTS2_ACCOUNT_NAME:
-                        business.accountName = getValue();
-                        break;
-
-                    // EAS Personal
-                    case Tags.CONTACTS_ANNIVERSARY:
-                        personal.anniversary = getValue();
-                        break;
-                    case Tags.CONTACTS_BIRTHDAY:
-                        ops.addBirthday(entity, getValue());
-                        break;
-                    case Tags.CONTACTS_WEBPAGE:
-                        ops.addWebpage(entity, getValue());
-                        break;
-
-                    case Tags.CONTACTS_PICTURE:
-                        ops.addPhoto(entity, getValue());
-                        break;
-
-                    case Tags.BASE_BODY:
-                        ops.addNote(entity, bodyParser());
-                        break;
-                    case Tags.CONTACTS_BODY:
-                        ops.addNote(entity, getValue());
-                        break;
-
-                    case Tags.CONTACTS_CATEGORIES:
-                        mGroupsUsed = true;
-                        categoriesParser(ops, entity);
-                        break;
-
-                    case Tags.CONTACTS_COMPRESSED_RTF:
-                        // We don't use this, and it isn't necessary to upload, so we'll ignore it
-                        skipTag();
-                        break;
-
-                    default:
-                        skipTag();
-                }
-            }
-
-            // We must have first name, last name, or company name
-            String name = null;
-            if (firstName != null || lastName != null) {
-                if (firstName == null) {
-                    name = lastName;
-                } else if (lastName == null) {
-                    name = firstName;
-                } else {
-                    name = firstName + ' ' + lastName;
-                }
-            } else if (companyName != null) {
-                name = companyName;
-            }
-
-            ops.addName(entity, prefix, firstName, lastName, middleName, suffix, name,
-                    yomiFirstName, yomiLastName, fileAs);
-            ops.addBusiness(entity, business);
-            ops.addPersonal(entity, personal);
-
-            ops.addUntyped(entity, emails, Email.CONTENT_ITEM_TYPE, -1, MAX_EMAIL_ROWS);
-            ops.addUntyped(entity, ims, Im.CONTENT_ITEM_TYPE, -1, MAX_IM_ROWS);
-            ops.addUntyped(entity, homePhones, Phone.CONTENT_ITEM_TYPE, Phone.TYPE_HOME,
-                    MAX_PHONE_ROWS);
-            ops.addUntyped(entity, workPhones, Phone.CONTENT_ITEM_TYPE, Phone.TYPE_WORK,
-                    MAX_PHONE_ROWS);
-
-            if (!children.isEmpty()) {
-                ops.addChildren(entity, children);
-            }
-
-            if (work.hasData()) {
-                ops.addPostal(entity, StructuredPostal.TYPE_WORK, work.street, work.city,
-                        work.state, work.country, work.code);
-            }
-            if (home.hasData()) {
-                ops.addPostal(entity, StructuredPostal.TYPE_HOME, home.street, home.city,
-                        home.state, home.country, home.code);
-            }
-            if (other.hasData()) {
-                ops.addPostal(entity, StructuredPostal.TYPE_OTHER, other.street, other.city,
-                        other.state, other.country, other.code);
-            }
-
-            if (companyName != null) {
-                ops.addOrganization(entity, Organization.TYPE_WORK, companyName, title, department,
-                        yomiCompanyName, officeLocation);
-            }
-
-            if (entity != null) {
-                // We've been removing rows from the list as they've been found in the xml
-                // Any that are left must have been deleted on the server
-                ArrayList<NamedContentValues> ncvList = entity.getSubValues();
-                for (NamedContentValues ncv: ncvList) {
-                    // These rows need to be deleted...
-                    Uri u = dataUriFromNamedContentValues(ncv);
-                    ops.add(ContentProviderOperation.newDelete(addCallerIsSyncAdapterParameter(u))
-                            .build());
-                }
-            }
-        }
-
-        private void categoriesParser(ContactOperations ops, Entity entity) throws IOException {
-            while (nextTag(Tags.CONTACTS_CATEGORIES) != END) {
-                switch (tag) {
-                    case Tags.CONTACTS_CATEGORY:
-                        ops.addGroup(entity, getValue());
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-        }
-
-        private void childrenParser(ArrayList<String> children) throws IOException {
-            while (nextTag(Tags.CONTACTS_CHILDREN) != END) {
-                switch (tag) {
-                    case Tags.CONTACTS_CHILD:
-                        if (children.size() < EasChildren.MAX_CHILDREN) {
-                            children.add(getValue());
-                        }
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-        }
-
-        private String bodyParser() throws IOException {
-            String body = null;
-            while (nextTag(Tags.BASE_BODY) != END) {
-                switch (tag) {
-                    case Tags.BASE_DATA:
-                        body = getValue();
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-            return body;
-        }
-
-        public void addParser(ContactOperations ops) throws IOException {
-            String serverId = null;
-            while (nextTag(Tags.SYNC_ADD) != END) {
-                switch (tag) {
-                    case Tags.SYNC_SERVER_ID: // same as
-                        serverId = getValue();
-                        break;
-                    case Tags.SYNC_APPLICATION_DATA:
-                        addData(serverId, ops, null);
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-        }
-
-        private Cursor getServerIdCursor(String serverId) {
-            mBindArgument[0] = serverId;
-            return mContentResolver.query(mAccountUri, ID_PROJECTION, SERVER_ID_SELECTION,
-                    mBindArgument, null);
-        }
-
-        private Cursor getClientIdCursor(String clientId) {
-            mBindArgument[0] = clientId;
-            return mContentResolver.query(mAccountUri, ID_PROJECTION, CLIENT_ID_SELECTION,
-                    mBindArgument, null);
-        }
-
-        public void deleteParser(ContactOperations ops) throws IOException {
-            while (nextTag(Tags.SYNC_DELETE) != END) {
-                switch (tag) {
-                    case Tags.SYNC_SERVER_ID:
-                        String serverId = getValue();
-                        // Find the message in this mailbox with the given serverId
-                        Cursor c = getServerIdCursor(serverId);
-                        try {
-                            if (c.moveToFirst()) {
-                                userLog("Deleting ", serverId);
-                                ops.delete(c.getLong(0));
-                            }
-                        } finally {
-                            c.close();
-                        }
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-        }
-
-        class ServerChange {
-            long id;
-            boolean read;
-
-            ServerChange(long _id, boolean _read) {
-                id = _id;
-                read = _read;
-            }
-        }
-
-        /**
-         * Changes are handled row by row, and only changed/new rows are acted upon
-         * @param ops the array of pending ContactProviderOperations.
-         * @throws IOException
-         */
-        public void changeParser(ContactOperations ops) throws IOException {
-            String serverId = null;
-            Entity entity = null;
-            while (nextTag(Tags.SYNC_CHANGE) != END) {
-                switch (tag) {
-                    case Tags.SYNC_SERVER_ID:
-                        serverId = getValue();
-                        Cursor c = getServerIdCursor(serverId);
-                        try {
-                            if (c.moveToFirst()) {
-                                // TODO Handle deleted individual rows...
-                                Uri uri = ContentUris.withAppendedId(
-                                        RawContacts.CONTENT_URI, c.getLong(0));
-                                uri = Uri.withAppendedPath(
-                                        uri, RawContacts.Entity.CONTENT_DIRECTORY);
-                                EntityIterator entityIterator = RawContacts.newEntityIterator(
-                                    mContentResolver.query(uri, null, null, null, null));
-                                if (entityIterator.hasNext()) {
-                                    entity = entityIterator.next();
-                                }
-                                userLog("Changing contact ", serverId);
-                            }
-                        } finally {
-                            c.close();
-                        }
-                        break;
-                    case Tags.SYNC_APPLICATION_DATA:
-                        addData(serverId, ops, entity);
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-        }
-
-        @Override
-        public void commandsParser() throws IOException {
-            while (nextTag(Tags.SYNC_COMMANDS) != END) {
-                if (tag == Tags.SYNC_ADD) {
-                    addParser(ops);
-                    incrementChangeCount();
-                } else if (tag == Tags.SYNC_DELETE) {
-                    deleteParser(ops);
-                    incrementChangeCount();
-                } else if (tag == Tags.SYNC_CHANGE) {
-                    changeParser(ops);
-                    incrementChangeCount();
-                } else
-                    skipTag();
-            }
-        }
-
-        @Override
-        public void commit() throws IOException {
-           // Save the syncKey here, using the Helper provider by Contacts provider
-            userLog("Contacts SyncKey saved as: ", mMailbox.mSyncKey);
-            ops.add(SyncStateContract.Helpers.newSetOperation(SyncState.CONTENT_URI,
-                    mAccountManagerAccount, mMailbox.mSyncKey.getBytes()));
-
-            // Execute these all at once...
-            ops.execute();
-
-            if (ops.mResults != null) {
-                ContentValues cv = new ContentValues();
-                cv.put(RawContacts.DIRTY, 0);
-                for (int i = 0; i < ops.mContactIndexCount; i++) {
-                    int index = ops.mContactIndexArray[i];
-                    Uri u = ops.mResults[index].uri;
-                    if (u != null) {
-                        String idString = u.getLastPathSegment();
-                        mContentResolver.update(
-                                addCallerIsSyncAdapterParameter(RawContacts.CONTENT_URI), cv,
-                                RawContacts._ID + "=" + idString, null);
-                    }
-                }
-            }
-        }
-
-        public void addResponsesParser() throws IOException {
-            String serverId = null;
-            String clientId = null;
-            ContentValues cv = new ContentValues();
-            while (nextTag(Tags.SYNC_ADD) != END) {
-                switch (tag) {
-                    case Tags.SYNC_SERVER_ID:
-                        serverId = getValue();
-                        break;
-                    case Tags.SYNC_CLIENT_ID:
-                        clientId = getValue();
-                        break;
-                    case Tags.SYNC_STATUS:
-                        getValue();
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-
-            // This is theoretically impossible, but...
-            if (clientId == null || serverId == null) return;
-
-            Cursor c = getClientIdCursor(clientId);
-            try {
-                if (c.moveToFirst()) {
-                    cv.put(RawContacts.SOURCE_ID, serverId);
-                    cv.put(RawContacts.DIRTY, 0);
-                    ops.add(ContentProviderOperation.newUpdate(
-                            ContentUris.withAppendedId(
-                                    addCallerIsSyncAdapterParameter(RawContacts.CONTENT_URI),
-                                    c.getLong(0)))
-                            .withValues(cv)
-                            .build());
-                    userLog("New contact " + clientId + " was given serverId: " + serverId);
-                }
-            } finally {
-                c.close();
-            }
-        }
-
-        public void changeResponsesParser() throws IOException {
-            String serverId = null;
-            String status = null;
-            while (nextTag(Tags.SYNC_CHANGE) != END) {
-                switch (tag) {
-                    case Tags.SYNC_SERVER_ID:
-                        serverId = getValue();
-                        break;
-                    case Tags.SYNC_STATUS:
-                        status = getValue();
-                        break;
-                    default:
-                        skipTag();
-                }
-            }
-            if (serverId != null && status != null) {
-                userLog("Changed contact " + serverId + " failed with status: " + status);
-            }
-        }
-
-
-        @Override
-        public void responsesParser() throws IOException {
-            // Handle server responses here (for Add and Change)
-            while (nextTag(Tags.SYNC_RESPONSES) != END) {
-                if (tag == Tags.SYNC_ADD) {
-                    addResponsesParser();
-                } else if (tag == Tags.SYNC_CHANGE) {
-                    changeResponsesParser();
-                } else
-                    skipTag();
-            }
-        }
-    }
-
-
-    private Uri uriWithAccountAndIsSyncAdapter(Uri uri) {
-        return uri.buildUpon()
-            .appendQueryParameter(RawContacts.ACCOUNT_NAME, mAccount.mEmailAddress)
-            .appendQueryParameter(RawContacts.ACCOUNT_TYPE, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE)
-            .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
-            .build();
-    }
-
-    /**
-     * SmartBuilder is a wrapper for the Builder class that is used to create/update rows for a
-     * ContentProvider.  It has, in addition to the Builder, ContentValues which, if present,
-     * represent the current values of that row, that can be compared against current values to
-     * see whether an update is even necessary.  The methods on SmartBuilder are delegated to
-     * the Builder.
-     */
-    private class RowBuilder {
-        Builder builder;
-        ContentValues cv;
-
-        public RowBuilder(Builder _builder) {
-            builder = _builder;
-        }
-
-        public RowBuilder(Builder _builder, NamedContentValues _ncv) {
-            builder = _builder;
-            cv = _ncv.values;
-        }
-
-        RowBuilder withValues(ContentValues values) {
-            builder.withValues(values);
-            return this;
-        }
-
-        RowBuilder withValueBackReference(String key, int previousResult) {
-            builder.withValueBackReference(key, previousResult);
-            return this;
-        }
-
-        ContentProviderOperation build() {
-            return builder.build();
-        }
-
-        RowBuilder withValue(String key, Object value) {
-            builder.withValue(key, value);
-            return this;
-        }
-    }
-
-    private class ContactOperations extends ArrayList<ContentProviderOperation> {
-        private static final long serialVersionUID = 1L;
-        private int mCount = 0;
-        private int mContactBackValue = mCount;
-        // Make an array big enough for the PIM window (max items we can get)
-        private int[] mContactIndexArray =
-            new int[Integer.parseInt(AbstractSyncAdapter.PIM_WINDOW_SIZE)];
-        private int mContactIndexCount = 0;
-        private ContentProviderResult[] mResults = null;
-
-        @Override
-        public boolean add(ContentProviderOperation op) {
-            super.add(op);
-            mCount++;
-            return true;
-        }
-
-        public void newContact(String serverId) {
-            Builder builder = ContentProviderOperation
-                .newInsert(uriWithAccountAndIsSyncAdapter(RawContacts.CONTENT_URI));
-            ContentValues values = new ContentValues();
-            values.put(RawContacts.SOURCE_ID, serverId);
-            builder.withValues(values);
-            mContactBackValue = mCount;
-            mContactIndexArray[mContactIndexCount++] = mCount;
-            add(builder.build());
-        }
-
-        public void delete(long id) {
-            add(ContentProviderOperation
-                    .newDelete(ContentUris.withAppendedId(RawContacts.CONTENT_URI, id)
-                            .buildUpon()
-                            .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
-                            .build())
-                    .build());
-        }
-
-        public void execute() {
-            synchronized (mService.getSynchronizer()) {
-                if (!mService.isStopped()) {
-                    try {
-                        if (!isEmpty()) {
-                            mService.userLog("Executing ", size(), " CPO's");
-                            mResults = mContext.getContentResolver().applyBatch(
-                                    ContactsContract.AUTHORITY, this);
-                        }
-                    } catch (RemoteException e) {
-                        // There is nothing sensible to be done here
-                        Log.e(TAG, "problem inserting contact during server update", e);
-                    } catch (OperationApplicationException e) {
-                        // There is nothing sensible to be done here
-                        Log.e(TAG, "problem inserting contact during server update", e);
-                    }
-                }
-            }
-        }
-
-        /**
-         * Given the list of NamedContentValues for an entity, a mime type, and a subtype,
-         * tries to find a match, returning it
-         * @param list the list of NCV's from the contact entity
-         * @param contentItemType the mime type we're looking for
-         * @param type the subtype (e.g. HOME, WORK, etc.)
-         * @return the matching NCV or null if not found
-         */
-        private NamedContentValues findTypedData(ArrayList<NamedContentValues> list,
-                String contentItemType, int type, String stringType) {
-            NamedContentValues result = null;
-
-            // Loop through the ncv's, looking for an existing row
-            for (NamedContentValues namedContentValues: list) {
-                Uri uri = namedContentValues.uri;
-                ContentValues cv = namedContentValues.values;
-                if (Data.CONTENT_URI.equals(uri)) {
-                    String mimeType = cv.getAsString(Data.MIMETYPE);
-                    if (mimeType.equals(contentItemType)) {
-                        if (stringType != null) {
-                            if (cv.getAsString(GroupMembership.GROUP_ROW_ID).equals(stringType)) {
-                                result = namedContentValues;
-                            }
-                        // Note Email.TYPE could be ANY type column; they are all defined in
-                        // the private CommonColumns class in ContactsContract
-                        // We'll accept either type < 0 (don't care), cv doesn't have a type,
-                        // or the types are equal
-                        } else if (type < 0 || !cv.containsKey(Email.TYPE) ||
-                                cv.getAsInteger(Email.TYPE) == type) {
-                            result = namedContentValues;
-                        }
-                    }
-                }
-            }
-
-            // If we've found an existing data row, we'll delete it.  Any rows left at the
-            // end should be deleted...
-            if (result != null) {
-                list.remove(result);
-            }
-
-            // Return the row found (or null)
-            return result;
-        }
-
-        /**
-         * Given the list of NamedContentValues for an entity and a mime type
-         * gather all of the matching NCV's, returning them
-         * @param list the list of NCV's from the contact entity
-         * @param contentItemType the mime type we're looking for
-         * @param type the subtype (e.g. HOME, WORK, etc.)
-         * @return the matching NCVs
-         */
-        private ArrayList<NamedContentValues> findUntypedData(ArrayList<NamedContentValues> list,
-                int type, String contentItemType) {
-            ArrayList<NamedContentValues> result = new ArrayList<NamedContentValues>();
-
-            // Loop through the ncv's, looking for an existing row
-            for (NamedContentValues namedContentValues: list) {
-                Uri uri = namedContentValues.uri;
-                ContentValues cv = namedContentValues.values;
-                if (Data.CONTENT_URI.equals(uri)) {
-                    String mimeType = cv.getAsString(Data.MIMETYPE);
-                    if (mimeType.equals(contentItemType)) {
-                        if (type != -1) {
-                            int subtype = cv.getAsInteger(Phone.TYPE);
-                            if (type != subtype) {
-                                continue;
-                            }
-                        }
-                        result.add(namedContentValues);
-                    }
-                }
-            }
-
-            // If we've found an existing data row, we'll delete it.  Any rows left at the
-            // end should be deleted...
-            for (NamedContentValues values : result) {
-                list.remove(values);
-            }
-
-            // Return the row found (or null)
-            return result;
-        }
-
-        /**
-         * Create a wrapper for a builder (insert or update) that also includes the NCV for
-         * an existing row of this type.   If the SmartBuilder's cv field is not null, then
-         * it represents the current (old) values of this field.  The caller can then check
-         * whether the field is now different and needs to be updated; if it's not different,
-         * the caller will simply return and not generate a new CPO.  Otherwise, the builder
-         * should have its content values set, and the built CPO should be added to the
-         * ContactOperations list.
-         *
-         * @param entity the contact entity (or null if this is a new contact)
-         * @param mimeType the mime type of this row
-         * @param type the subtype of this row
-         * @param stringType for groups, the name of the group (type will be ignored), or null
-         * @return the created SmartBuilder
-         */
-        public RowBuilder createBuilder(Entity entity, String mimeType, int type,
-                String stringType) {
-            RowBuilder builder = null;
-
-            if (entity != null) {
-                NamedContentValues ncv =
-                    findTypedData(entity.getSubValues(), mimeType, type, stringType);
-                if (ncv != null) {
-                    builder = new RowBuilder(
-                            ContentProviderOperation
-                                .newUpdate(addCallerIsSyncAdapterParameter(
-                                    dataUriFromNamedContentValues(ncv))),
-                            ncv);
-                }
-            }
-
-            if (builder == null) {
-                builder = newRowBuilder(entity, mimeType);
-            }
-
-            // Return the appropriate builder (insert or update)
-            // Caller will fill in the appropriate values; 4 MIMETYPE is already set
-            return builder;
-        }
-
-        private RowBuilder typedRowBuilder(Entity entity, String mimeType, int type) {
-            return createBuilder(entity, mimeType, type, null);
-        }
-
-        private RowBuilder untypedRowBuilder(Entity entity, String mimeType) {
-            return createBuilder(entity, mimeType, -1, null);
-        }
-
-        private RowBuilder newRowBuilder(Entity entity, String mimeType) {
-            // This is a new row; first get the contactId
-            // If the Contact is new, use the saved back value; otherwise the value in the entity
-            int contactId = mContactBackValue;
-            if (entity != null) {
-                contactId = entity.getEntityValues().getAsInteger(RawContacts._ID);
-            }
-
-            // Create an insert operation with the proper contactId reference
-            RowBuilder builder =
-                new RowBuilder(ContentProviderOperation.newInsert(
-                        addCallerIsSyncAdapterParameter(Data.CONTENT_URI)));
-            if (entity == null) {
-                builder.withValueBackReference(Data.RAW_CONTACT_ID, contactId);
-            } else {
-                builder.withValue(Data.RAW_CONTACT_ID, contactId);
-            }
-
-            // Set the mime type of the row
-            builder.withValue(Data.MIMETYPE, mimeType);
-            return builder;
-        }
-
-        /**
-         * Compare a column in a ContentValues with an (old) value, and see if they are the
-         * same.  For this purpose, null and an empty string are considered the same.
-         * @param cv a ContentValues object, from a NamedContentValues
-         * @param column a column that might be in the ContentValues
-         * @param oldValue an old value (or null) to check against
-         * @return whether the column's value in the ContentValues matches oldValue
-         */
-        private boolean cvCompareString(ContentValues cv, String column, String oldValue) {
-            if (cv.containsKey(column)) {
-                if (oldValue != null && cv.getAsString(column).equals(oldValue)) {
-                    return true;
-                }
-            } else if (oldValue == null || oldValue.length() == 0) {
-                return true;
-            }
-            return false;
-        }
-
-        public void addChildren(Entity entity, ArrayList<String> children) {
-            RowBuilder builder = untypedRowBuilder(entity, EasChildren.CONTENT_ITEM_TYPE);
-            int i = 0;
-            for (String child: children) {
-                builder.withValue(EasChildren.ROWS[i++], child);
-            }
-            add(builder.build());
-        }
-
-        public void addGroup(Entity entity, String group) {
-            RowBuilder builder =
-                createBuilder(entity, GroupMembership.CONTENT_ITEM_TYPE, -1, group);
-            builder.withValue(GroupMembership.GROUP_SOURCE_ID, group);
-            add(builder.build());
-        }
-
-        public void addBirthday(Entity entity, String birthday) {
-            RowBuilder builder =
-                    typedRowBuilder(entity, Event.CONTENT_ITEM_TYPE, Event.TYPE_BIRTHDAY);
-            ContentValues cv = builder.cv;
-            if (cv != null && cvCompareString(cv, Event.START_DATE, birthday)) {
-                return;
-            }
-            builder.withValue(Event.START_DATE, birthday);
-            builder.withValue(Event.TYPE, Event.TYPE_BIRTHDAY);
-            add(builder.build());
-        }
-
-        public void addName(Entity entity, String prefix, String givenName, String familyName,
-                String middleName, String suffix, String displayName, String yomiFirstName,
-                String yomiLastName, String fileAs) {
-            RowBuilder builder = untypedRowBuilder(entity, StructuredName.CONTENT_ITEM_TYPE);
-            ContentValues cv = builder.cv;
-            if (cv != null && cvCompareString(cv, StructuredName.GIVEN_NAME, givenName) &&
-                    cvCompareString(cv, StructuredName.FAMILY_NAME, familyName) &&
-                    cvCompareString(cv, StructuredName.MIDDLE_NAME, middleName) &&
-                    cvCompareString(cv, StructuredName.PREFIX, prefix) &&
-                    cvCompareString(cv, StructuredName.PHONETIC_GIVEN_NAME, yomiFirstName) &&
-                    cvCompareString(cv, StructuredName.PHONETIC_FAMILY_NAME, yomiLastName) &&
-                    //cvCompareString(cv, StructuredName.DISPLAY_NAME, fileAs) &&
-                    cvCompareString(cv, StructuredName.SUFFIX, suffix)) {
-                return;
-            }
-            builder.withValue(StructuredName.GIVEN_NAME, givenName);
-            builder.withValue(StructuredName.FAMILY_NAME, familyName);
-            builder.withValue(StructuredName.MIDDLE_NAME, middleName);
-            builder.withValue(StructuredName.SUFFIX, suffix);
-            builder.withValue(StructuredName.PHONETIC_GIVEN_NAME, yomiFirstName);
-            builder.withValue(StructuredName.PHONETIC_FAMILY_NAME, yomiLastName);
-            builder.withValue(StructuredName.PREFIX, prefix);
-            //builder.withValue(StructuredName.DISPLAY_NAME, fileAs);
-            add(builder.build());
-        }
-
-        public void addPersonal(Entity entity, EasPersonal personal) {
-            RowBuilder builder = untypedRowBuilder(entity, EasPersonal.CONTENT_ITEM_TYPE);
-            ContentValues cv = builder.cv;
-            if (cv != null && cvCompareString(cv, EasPersonal.ANNIVERSARY, personal.anniversary) &&
-                    cvCompareString(cv, EasPersonal.FILE_AS , personal.fileAs)) {
-                return;
-            }
-            if (!personal.hasData()) {
-                return;
-            }
-            builder.withValue(EasPersonal.FILE_AS, personal.fileAs);
-            builder.withValue(EasPersonal.ANNIVERSARY, personal.anniversary);
-            add(builder.build());
-        }
-
-        public void addBusiness(Entity entity, EasBusiness business) {
-            RowBuilder builder = untypedRowBuilder(entity, EasBusiness.CONTENT_ITEM_TYPE);
-            ContentValues cv = builder.cv;
-            if (cv != null && cvCompareString(cv, EasBusiness.ACCOUNT_NAME, business.accountName) &&
-                    cvCompareString(cv, EasBusiness.CUSTOMER_ID, business.customerId) &&
-                    cvCompareString(cv, EasBusiness.GOVERNMENT_ID, business.governmentId)) {
-                return;
-            }
-            if (!business.hasData()) {
-                return;
-            }
-            builder.withValue(EasBusiness.ACCOUNT_NAME, business.accountName);
-            builder.withValue(EasBusiness.CUSTOMER_ID, business.customerId);
-            builder.withValue(EasBusiness.GOVERNMENT_ID, business.governmentId);
-            add(builder.build());
-        }
-
-        public void addPhoto(Entity entity, String photo) {
-            RowBuilder builder = untypedRowBuilder(entity, Photo.CONTENT_ITEM_TYPE);
-            // We're always going to add this; it's not worth trying to figure out whether the
-            // picture is the same as the one stored.
-            byte[] pic = Base64.decode(photo, Base64.DEFAULT);
-            builder.withValue(Photo.PHOTO, pic);
-            add(builder.build());
-        }
-
-        public void addPhone(Entity entity, int type, String phone) {
-            RowBuilder builder = typedRowBuilder(entity, Phone.CONTENT_ITEM_TYPE, type);
-            ContentValues cv = builder.cv;
-            if (cv != null && cvCompareString(cv, Phone.NUMBER, phone)) {
-                return;
-            }
-            builder.withValue(Phone.TYPE, type);
-            builder.withValue(Phone.NUMBER, phone);
-            add(builder.build());
-        }
-
-        public void addWebpage(Entity entity, String url) {
-            RowBuilder builder = untypedRowBuilder(entity, Website.CONTENT_ITEM_TYPE);
-            ContentValues cv = builder.cv;
-            if (cv != null && cvCompareString(cv, Website.URL, url)) {
-                return;
-            }
-            builder.withValue(Website.TYPE, Website.TYPE_WORK);
-            builder.withValue(Website.URL, url);
-            add(builder.build());
-        }
-
-        public void addRelation(Entity entity, int type, String value) {
-            RowBuilder builder = typedRowBuilder(entity, Relation.CONTENT_ITEM_TYPE, type);
-            ContentValues cv = builder.cv;
-            if (cv != null && cvCompareString(cv, Relation.DATA, value)) {
-                return;
-            }
-            builder.withValue(Relation.TYPE, type);
-            builder.withValue(Relation.DATA, value);
-            add(builder.build());
-        }
-
-        public void addNickname(Entity entity, String name) {
-            RowBuilder builder =
-                typedRowBuilder(entity, Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE_DEFAULT);
-            ContentValues cv = builder.cv;
-            if (cv != null && cvCompareString(cv, Nickname.NAME, name)) {
-                return;
-            }
-            builder.withValue(Nickname.TYPE, Nickname.TYPE_DEFAULT);
-            builder.withValue(Nickname.NAME, name);
-            add(builder.build());
-        }
-
-        public void addPostal(Entity entity, int type, String street, String city, String state,
-                String country, String code) {
-            RowBuilder builder = typedRowBuilder(entity, StructuredPostal.CONTENT_ITEM_TYPE,
-                    type);
-            ContentValues cv = builder.cv;
-            if (cv != null && cvCompareString(cv, StructuredPostal.CITY, city) &&
-                    cvCompareString(cv, StructuredPostal.STREET, street) &&
-                    cvCompareString(cv, StructuredPostal.COUNTRY, country) &&
-                    cvCompareString(cv, StructuredPostal.POSTCODE, code) &&
-                    cvCompareString(cv, StructuredPostal.REGION, state)) {
-                return;
-            }
-            builder.withValue(StructuredPostal.TYPE, type);
-            builder.withValue(StructuredPostal.CITY, city);
-            builder.withValue(StructuredPostal.STREET, street);
-            builder.withValue(StructuredPostal.COUNTRY, country);
-            builder.withValue(StructuredPostal.POSTCODE, code);
-            builder.withValue(StructuredPostal.REGION, state);
-            add(builder.build());
-        }
-
-       /**
-         * We now are dealing with up to maxRows typeless rows of mimeType data.  We need to try to
-         * match them with existing rows; if there's a match, everything's great.  Otherwise, we
-         * either need to add a new row for the data, or we have to replace an existing one
-         * that no longer matches.  This is similar to the way Emails are handled.
-         */
-        public void addUntyped(Entity entity, ArrayList<UntypedRow> rows, String mimeType,
-                int type, int maxRows) {
-            // Make a list of all same type rows in the existing entity
-            ArrayList<NamedContentValues> oldValues = EMPTY_ARRAY_NAMEDCONTENTVALUES;
-            ArrayList<NamedContentValues> entityValues = EMPTY_ARRAY_NAMEDCONTENTVALUES;
-            if (entity != null) {
-                oldValues = findUntypedData(entityValues, type, mimeType);
-                entityValues = entity.getSubValues();
-            }
-
-            // These will be rows needing replacement with new values
-            ArrayList<UntypedRow> rowsToReplace = new ArrayList<UntypedRow>();
-
-            // The count of existing rows
-            int numRows = oldValues.size();
-            for (UntypedRow row: rows) {
-                boolean found = false;
-                // If we already have this row, mark it
-                for (NamedContentValues ncv: oldValues) {
-                    ContentValues cv = ncv.values;
-                    String data = cv.getAsString(COMMON_DATA_ROW);
-                    int rowType = -1;
-                    if (cv.containsKey(COMMON_TYPE_ROW)) {
-                        rowType = cv.getAsInteger(COMMON_TYPE_ROW);
-                    }
-                    if (row.isSameAs(rowType, data)) {
-                        cv.put(FOUND_DATA_ROW, true);
-                        // Remove this to indicate it's still being used
-                        entityValues.remove(ncv);
-                        found = true;
-                        break;
-                    }
-                }
-                if (!found) {
-                    // If we don't, there are two possibilities
-                    if (numRows < maxRows) {
-                        // If there are available rows, add a new one
-                        RowBuilder builder = newRowBuilder(entity, mimeType);
-                        row.addValues(builder);
-                        add(builder.build());
-                        numRows++;
-                    } else {
-                        // Otherwise, say we need to replace a row with this
-                        rowsToReplace.add(row);
-                    }
-                }
-            }
-
-            // Go through rows needing replacement
-            for (UntypedRow row: rowsToReplace) {
-                for (NamedContentValues ncv: oldValues) {
-                    ContentValues cv = ncv.values;
-                    // Find a row that hasn't been used (i.e. doesn't match current rows)
-                    if (!cv.containsKey(FOUND_DATA_ROW)) {
-                        // And update it
-                        RowBuilder builder = new RowBuilder(
-                                ContentProviderOperation
-                                    .newUpdate(addCallerIsSyncAdapterParameter(
-                                        dataUriFromNamedContentValues(ncv))),
-                                ncv);
-                        row.addValues(builder);
-                        add(builder.build());
-                    }
-                }
-            }
-        }
-
-        public void addOrganization(Entity entity, int type, String company, String title,
-                String department, String yomiCompanyName, String officeLocation) {
-            RowBuilder builder = typedRowBuilder(entity, Organization.CONTENT_ITEM_TYPE, type);
-            ContentValues cv = builder.cv;
-            if (cv != null && cvCompareString(cv, Organization.COMPANY, company) &&
-                    cvCompareString(cv, Organization.PHONETIC_NAME, yomiCompanyName) &&
-                    cvCompareString(cv, Organization.DEPARTMENT, department) &&
-                    cvCompareString(cv, Organization.TITLE, title) &&
-                    cvCompareString(cv, Organization.OFFICE_LOCATION, officeLocation)) {
-                return;
-            }
-            builder.withValue(Organization.TYPE, type);
-            builder.withValue(Organization.COMPANY, company);
-            builder.withValue(Organization.TITLE, title);
-            builder.withValue(Organization.DEPARTMENT, department);
-            builder.withValue(Organization.PHONETIC_NAME, yomiCompanyName);
-            builder.withValue(Organization.OFFICE_LOCATION, officeLocation);
-            add(builder.build());
-        }
-
-        public void addNote(Entity entity, String note) {
-            RowBuilder builder = typedRowBuilder(entity, Note.CONTENT_ITEM_TYPE, -1);
-            ContentValues cv = builder.cv;
-            if (note == null) return;
-            note = note.replaceAll("\r\n", "\n");
-            if (cv != null && cvCompareString(cv, Note.NOTE, note)) {
-                return;
-            }
-
-            // Reject notes with nothing in them.  Often, we get something from Outlook when
-            // nothing was ever entered.  Sigh.
-            int len = note.length();
-            int i = 0;
-            for (; i < len; i++) {
-                char c = note.charAt(i);
-                if (!Character.isWhitespace(c)) {
-                    break;
-                }
-            }
-            if (i == len) return;
-
-            builder.withValue(Note.NOTE, note);
-            add(builder.build());
-        }
-    }
-
-    /**
-     * Generate the uri for the data row associated with this NamedContentValues object
-     * @param ncv the NamedContentValues object
-     * @return a uri that can be used to refer to this row
-     */
-    public Uri dataUriFromNamedContentValues(NamedContentValues ncv) {
-        long id = ncv.values.getAsLong(RawContacts._ID);
-        Uri dataUri = ContentUris.withAppendedId(ncv.uri, id);
-        return dataUri;
-    }
-
-    @Override
-    public void cleanup() {
-        // Mark the changed contacts dirty = 0
-        // Permanently delete the user deletions
-        ContactOperations ops = new ContactOperations();
-        for (Long id: mUpdatedIdList) {
-            ops.add(ContentProviderOperation
-                    .newUpdate(ContentUris.withAppendedId(RawContacts.CONTENT_URI, id)
-                            .buildUpon()
-                            .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
-                            .build())
-                    .withValue(RawContacts.DIRTY, 0).build());
-        }
-        for (Long id: mDeletedIdList) {
-            ops.add(ContentProviderOperation
-                    .newDelete(ContentUris.withAppendedId(RawContacts.CONTENT_URI, id)
-                            .buildUpon()
-                            .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
-                            .build())
-                    .build());
-        }
-        ops.execute();
-        ContentResolver cr = mContext.getContentResolver();
-        if (mGroupsUsed) {
-            // Make sure the title column is set for all of our groups
-            // And that all of our groups are visible
-            // TODO Perhaps the visible part should only happen when the group is created, but
-            // this is fine for now.
-            Uri groupsUri = uriWithAccountAndIsSyncAdapter(Groups.CONTENT_URI);
-            Cursor c = cr.query(groupsUri, new String[] {Groups.SOURCE_ID, Groups.TITLE},
-                    Groups.TITLE + " IS NULL", null, null);
-            ContentValues values = new ContentValues();
-            values.put(Groups.GROUP_VISIBLE, 1);
-            try {
-                while (c.moveToNext()) {
-                    String sourceId = c.getString(0);
-                    values.put(Groups.TITLE, sourceId);
-                    cr.update(uriWithAccountAndIsSyncAdapter(groupsUri), values,
-                            Groups.SOURCE_ID + "=?", new String[] {sourceId});
-                }
-            } finally {
-                c.close();
-            }
-        }
-    }
-
-    @Override
-    public String getCollectionName() {
-        return "Contacts";
-    }
-
-    private void sendEmail(Serializer s, ContentValues cv, int count, String displayName)
-            throws IOException {
-        // Get both parts of the email address (a newly created one in the UI won't have a name)
-        String addr = cv.getAsString(Email.DATA);
-        String name = cv.getAsString(Email.DISPLAY_NAME);
-        if (name == null) {
-            if (displayName != null) {
-                name = displayName;
-            } else {
-                name = addr;
-            }
-        }
-        // Compose address from name and addr
-        if (addr != null) {
-            String value = '\"' + name + "\" <" + addr + '>';
-            if (count < MAX_EMAIL_ROWS) {
-                s.data(EMAIL_TAGS[count], value);
-            }
-        }
-    }
-
-    private void sendIm(Serializer s, ContentValues cv, int count) throws IOException {
-        String value = cv.getAsString(Im.DATA);
-        if (value == null) return;
-        if (count < MAX_IM_ROWS) {
-            s.data(IM_TAGS[count], value);
-        }
-    }
-
-    private void sendOnePostal(Serializer s, ContentValues cv, int[] fieldNames)
-            throws IOException{
-        sendStringData(s, cv, StructuredPostal.CITY, fieldNames[0]);
-        sendStringData(s, cv, StructuredPostal.COUNTRY, fieldNames[1]);
-        sendStringData(s, cv, StructuredPostal.POSTCODE, fieldNames[2]);
-        sendStringData(s, cv, StructuredPostal.REGION, fieldNames[3]);
-        sendStringData(s, cv, StructuredPostal.STREET, fieldNames[4]);
-    }
-
-    private void sendStructuredPostal(Serializer s, ContentValues cv) throws IOException {
-        switch (cv.getAsInteger(StructuredPostal.TYPE)) {
-            case StructuredPostal.TYPE_HOME:
-                sendOnePostal(s, cv, HOME_ADDRESS_TAGS);
-                break;
-            case StructuredPostal.TYPE_WORK:
-                sendOnePostal(s, cv, WORK_ADDRESS_TAGS);
-                break;
-            case StructuredPostal.TYPE_OTHER:
-                sendOnePostal(s, cv, OTHER_ADDRESS_TAGS);
-                break;
-            default:
-                break;
-        }
-    }
-
-    private void sendStringData(Serializer s, ContentValues cv, String column, int tag)
-            throws IOException {
-        if (cv.containsKey(column)) {
-            String value = cv.getAsString(column);
-            if (!TextUtils.isEmpty(value)) {
-                s.data(tag, value);
-            }
-        }
-    }
-
-    private String sendStructuredName(Serializer s, ContentValues cv) throws IOException {
-        String displayName = null;
-        sendStringData(s, cv, StructuredName.FAMILY_NAME, Tags.CONTACTS_LAST_NAME);
-        sendStringData(s, cv, StructuredName.GIVEN_NAME, Tags.CONTACTS_FIRST_NAME);
-        sendStringData(s, cv, StructuredName.MIDDLE_NAME, Tags.CONTACTS_MIDDLE_NAME);
-        sendStringData(s, cv, StructuredName.SUFFIX, Tags.CONTACTS_SUFFIX);
-        sendStringData(s, cv, StructuredName.PHONETIC_GIVEN_NAME, Tags.CONTACTS_YOMI_FIRST_NAME);
-        sendStringData(s, cv, StructuredName.PHONETIC_FAMILY_NAME, Tags.CONTACTS_YOMI_LAST_NAME);
-        sendStringData(s, cv, StructuredName.PREFIX, Tags.CONTACTS_TITLE);
-        if (cv.containsKey(StructuredName.DISPLAY_NAME)) {
-            displayName = cv.getAsString(StructuredName.DISPLAY_NAME);
-            if (!TextUtils.isEmpty(displayName)) {
-                s.data(Tags.CONTACTS_FILE_AS, displayName);
-            }
-        }
-        return displayName;
-    }
-
-    private void sendBusiness(Serializer s, ContentValues cv) throws IOException {
-        sendStringData(s, cv, EasBusiness.ACCOUNT_NAME, Tags.CONTACTS2_ACCOUNT_NAME);
-        sendStringData(s, cv, EasBusiness.CUSTOMER_ID, Tags.CONTACTS2_CUSTOMER_ID);
-        sendStringData(s, cv, EasBusiness.GOVERNMENT_ID, Tags.CONTACTS2_GOVERNMENT_ID);
-    }
-
-    private void sendPersonal(Serializer s, ContentValues cv) throws IOException {
-        sendStringData(s, cv, EasPersonal.ANNIVERSARY, Tags.CONTACTS_ANNIVERSARY);
-        sendStringData(s, cv, EasPersonal.FILE_AS, Tags.CONTACTS_FILE_AS);
-    }
-
-    private void sendBirthday(Serializer s, ContentValues cv) throws IOException {
-        sendStringData(s, cv, Event.START_DATE, Tags.CONTACTS_BIRTHDAY);
-    }
-
-    private void sendPhoto(Serializer s, ContentValues cv) throws IOException {
-        if (cv.containsKey(Photo.PHOTO)) {
-            byte[] bytes = cv.getAsByteArray(Photo.PHOTO);
-            String pic = Base64.encodeToString(bytes, Base64.NO_WRAP);
-            s.data(Tags.CONTACTS_PICTURE, pic);
-        } else {
-            // Send an empty tag, which signals the server to delete any pre-existing photo
-            s.tag(Tags.CONTACTS_PICTURE);
-        }
-    }
-
-    private void sendOrganization(Serializer s, ContentValues cv) throws IOException {
-        sendStringData(s, cv, Organization.TITLE, Tags.CONTACTS_JOB_TITLE);
-        sendStringData(s, cv, Organization.COMPANY, Tags.CONTACTS_COMPANY_NAME);
-        sendStringData(s, cv, Organization.DEPARTMENT, Tags.CONTACTS_DEPARTMENT);
-        sendStringData(s, cv, Organization.OFFICE_LOCATION, Tags.CONTACTS_OFFICE_LOCATION);
-    }
-
-    private void sendNickname(Serializer s, ContentValues cv) throws IOException {
-        sendStringData(s, cv, Nickname.NAME, Tags.CONTACTS2_NICKNAME);
-    }
-
-    private void sendWebpage(Serializer s, ContentValues cv) throws IOException {
-        sendStringData(s, cv, Website.URL, Tags.CONTACTS_WEBPAGE);
-    }
-
-    private void sendNote(Serializer s, ContentValues cv) throws IOException {
-        // Even when there is no local note, we must explicitly upsync an empty note,
-        // which is the only way to force the server to delete any pre-existing note.
-        String note = "";
-        if (cv.containsKey(Note.NOTE)) {
-            // EAS won't accept note data with raw newline characters
-            note = cv.getAsString(Note.NOTE).replaceAll("\n", "\r\n");
-        }
-        // Format of upsync data depends on protocol version
-        if (mService.mProtocolVersionDouble >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) {
-            s.start(Tags.BASE_BODY);
-            s.data(Tags.BASE_TYPE, Eas.BODY_PREFERENCE_TEXT).data(Tags.BASE_DATA, note);
-            s.end();
-        } else {
-            s.data(Tags.CONTACTS_BODY, note);
-        }
-    }
-
-    private void sendChildren(Serializer s, ContentValues cv) throws IOException {
-        boolean first = true;
-        for (int i = 0; i < EasChildren.MAX_CHILDREN; i++) {
-            String row = EasChildren.ROWS[i];
-            if (cv.containsKey(row)) {
-                if (first) {
-                    s.start(Tags.CONTACTS_CHILDREN);
-                    first = false;
-                }
-                s.data(Tags.CONTACTS_CHILD, cv.getAsString(row));
-            }
-        }
-        if (!first) {
-            s.end();
-        }
-    }
-
-    private void sendPhone(Serializer s, ContentValues cv, int workCount, int homeCount)
-            throws IOException {
-        String value = cv.getAsString(Phone.NUMBER);
-        if (value == null) return;
-        switch (cv.getAsInteger(Phone.TYPE)) {
-            case Phone.TYPE_WORK:
-                if (workCount < MAX_PHONE_ROWS) {
-                    s.data(WORK_PHONE_TAGS[workCount], value);
-                }
-                break;
-            case Phone.TYPE_MMS:
-                s.data(Tags.CONTACTS2_MMS, value);
-                break;
-            case Phone.TYPE_ASSISTANT:
-                s.data(Tags.CONTACTS_ASSISTANT_TELEPHONE_NUMBER, value);
-                break;
-            case Phone.TYPE_FAX_WORK:
-                s.data(Tags.CONTACTS_BUSINESS_FAX_NUMBER, value);
-                break;
-            case Phone.TYPE_COMPANY_MAIN:
-                s.data(Tags.CONTACTS2_COMPANY_MAIN_PHONE, value);
-                break;
-            case Phone.TYPE_HOME:
-                if (homeCount < MAX_PHONE_ROWS) {
-                    s.data(HOME_PHONE_TAGS[homeCount], value);
-                }
-                break;
-            case Phone.TYPE_MOBILE:
-                s.data(Tags.CONTACTS_MOBILE_TELEPHONE_NUMBER, value);
-                break;
-            case Phone.TYPE_CAR:
-                s.data(Tags.CONTACTS_CAR_TELEPHONE_NUMBER, value);
-                break;
-            case Phone.TYPE_PAGER:
-                s.data(Tags.CONTACTS_PAGER_NUMBER, value);
-                break;
-            case Phone.TYPE_RADIO:
-                s.data(Tags.CONTACTS_RADIO_TELEPHONE_NUMBER, value);
-                break;
-            case Phone.TYPE_FAX_HOME:
-                s.data(Tags.CONTACTS_HOME_FAX_NUMBER, value);
-                break;
-            default:
-                break;
-        }
-    }
-
-    private void sendRelation(Serializer s, ContentValues cv) throws IOException {
-        String value = cv.getAsString(Relation.DATA);
-        if (value == null) return;
-        switch (cv.getAsInteger(Relation.TYPE)) {
-            case Relation.TYPE_ASSISTANT:
-                s.data(Tags.CONTACTS_ASSISTANT_NAME, value);
-                break;
-            case Relation.TYPE_MANAGER:
-                s.data(Tags.CONTACTS2_MANAGER_NAME, value);
-                break;
-            case Relation.TYPE_SPOUSE:
-                s.data(Tags.CONTACTS_SPOUSE, value);
-                break;
-            default:
-                break;