Make Calendar and Contacts syncing go through EasService

Now, instead of forwarding the request to the EmailSyncAdapterService,
both Calendar and Contacts syncAdapterServices actually make calls
through to the EasService to perform the sync.

Change-Id: I60346736e1639838593f4b52934a3d15b3faf543
diff --git a/src/com/android/exchange/ExchangeBroadcastReceiver.java b/src/com/android/exchange/ExchangeBroadcastReceiver.java
index 94542e2..33e8d08 100644
--- a/src/com/android/exchange/ExchangeBroadcastReceiver.java
+++ b/src/com/android/exchange/ExchangeBroadcastReceiver.java
@@ -14,7 +14,7 @@
 
 import com.android.emailcommon.provider.EmailContent;
 import com.android.emailcommon.provider.Mailbox;
-import com.android.exchange.R.string;
+import com.android.exchange.R;
 import com.android.mail.utils.LogUtils;
 
 public class ExchangeBroadcastReceiver extends BroadcastReceiver {
@@ -22,7 +22,7 @@
     @Override
     public void onReceive(final Context context, final Intent intent) {
         final Account[] accounts = AccountManager.get(context)
-                .getAccountsByType(context.getString(string.account_manager_type_exchange));
+                .getAccountsByType(context.getString(R.string.account_manager_type_exchange));
         LogUtils.i(Eas.LOG_TAG, "Accounts changed - requesting FolderSync for unsynced accounts");
         for (final Account account : accounts) {
             // Only do a sync for accounts that are not configured to sync any types, since the
diff --git a/src/com/android/exchange/eas/EasOperation.java b/src/com/android/exchange/eas/EasOperation.java
index 0950bff..364115a 100644
--- a/src/com/android/exchange/eas/EasOperation.java
+++ b/src/com/android/exchange/eas/EasOperation.java
@@ -99,7 +99,7 @@
  * complexity beyond what's required.
  */
 public abstract class EasOperation {
-    public static final String LOG_TAG = Eas.LOG_TAG;
+    public static final String LOG_TAG = LogUtils.TAG;
 
     /** The maximum number of server redirects we allow before returning failure. */
     private static final int MAX_REDIRECTS = 3;
@@ -747,7 +747,7 @@
     }
 
     protected static void requestSyncForMailboxes(final android.accounts.Account amAccount,
-            final ArrayList<Long> mailboxIds) {
+            final String authority, final ArrayList<Long> mailboxIds) {
         final Bundle extras = Mailbox.createSyncBundle(mailboxIds);
         /**
          * Please note that it is very possible that we are trying to send a request to the
@@ -758,26 +758,8 @@
          * by another caller, then we should reconsider if manual=true is the right thing to do.
          */
         extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
-        ContentResolver.requestSync(amAccount, EmailContent.AUTHORITY, extras);
-        LogUtils.i(LOG_TAG, "requestSync EasOperation requestSyncForMailboxes  %s, %s",
-                amAccount.toString(), extras.toString());
-    }
-
-    /**
-     * RequestNoOpSync
-     * This requests a sync for a particular authority purely so that that account
-     * in settings will recognize that it is trying to sync, and will display the
-     * appropriate UI. In fact, all exchange data syncing actually happens through the
-     * EmailSyncAdapterService.
-     * @param amAccount
-     * @param authority
-     */
-    protected static void requestNoOpSync(final android.accounts.Account amAccount,
-            final String authority) {
-        final Bundle extras = new Bundle(1);
-        extras.putBoolean(Mailbox.SYNC_EXTRA_NOOP, true);
         ContentResolver.requestSync(amAccount, authority, extras);
-        LogUtils.d(LOG_TAG, "requestSync EasOperation requestNoOpSync %s, %s",
+        LogUtils.i(LOG_TAG, "EasOperation requestSyncForMailboxes  %s, %s",
                 amAccount.toString(), extras.toString());
     }
 
diff --git a/src/com/android/exchange/eas/EasPing.java b/src/com/android/exchange/eas/EasPing.java
index 5893ead..8b082e3 100644
--- a/src/com/android/exchange/eas/EasPing.java
+++ b/src/com/android/exchange/eas/EasPing.java
@@ -22,6 +22,8 @@
 import android.database.Cursor;
 import android.os.Bundle;
 import android.os.SystemClock;
+import android.provider.CalendarContract;
+import android.provider.ContactsContract;
 import android.text.format.DateUtils;
 
 import com.android.emailcommon.provider.Account;
@@ -181,6 +183,8 @@
             throw new IOException("Empty ping response");
         }
 
+        LogUtils.d(TAG, "EasPing.handleResponse");
+
         // Handle a valid response.
         final PingParser pp = new PingParser(response.getInputStream());
         pp.parse();
@@ -319,8 +323,9 @@
         final String[] bindArguments = new String[2];
         bindArguments[0] = Long.toString(getAccountId());
 
-        final ArrayList<Long> mailboxIds = new ArrayList<Long>();
-        final HashSet<Integer> contentTypes = new HashSet<Integer>();
+        final ArrayList<Long> emailMailboxIds = new ArrayList<Long>();
+        final ArrayList<Long> calendarMailboxIds = new ArrayList<Long>();
+        final ArrayList<Long> contactsMailboxIds = new ArrayList<Long>();
 
         for (final String serverId : syncList) {
             bindArguments[1] = serverId;
@@ -350,28 +355,29 @@
                 if (c.moveToFirst()) {
                     final long mailboxId = c.getLong(Mailbox.CONTENT_ID_COLUMN);
                     final int contentType = c.getInt(Mailbox.CONTENT_TYPE_COLUMN);
-                    mailboxIds.add(mailboxId);
-                    contentTypes.add(contentType);
+                    switch (contentType) {
+                        case Mailbox.TYPE_MAIL:
+                        case Mailbox.TYPE_INBOX:
+                        case Mailbox.TYPE_DRAFTS:
+                        case Mailbox.TYPE_SENT:
+                        case Mailbox.TYPE_TRASH:
+                        case Mailbox.TYPE_JUNK:
+                            emailMailboxIds.add(mailboxId);
+                        case Mailbox.TYPE_CALENDAR:
+                            calendarMailboxIds.add(mailboxId);
+                        case Mailbox.TYPE_CONTACTS:
+                            contactsMailboxIds.add(mailboxId);
+                        default:
+                            LogUtils.e(LOG_TAG, "unexpected collectiontype %d in EasPing", contentType);
+                    }
                 }
             } finally {
                 c.close();
             }
         }
-
-        for (final int type : contentTypes) {
-            switch (type) {
-                case Mailbox.TYPE_CALENDAR:
-                case Mailbox.TYPE_CONTACTS:
-                    // Ask for a no-op sync so that we'll see calendar or contacts
-                    // syncing in settings.
-                    requestNoOpSync(mAmAccount, Mailbox.getAuthority(type));
-                default:
-                    // Do nothing, we're already doing an Email sync.
-            }
-        }
-        // Ask the EmailSyncAdapter to sync all of these mailboxes, whether they're regular
-        // mailboxes or calendar or contacts.
-        requestSyncForMailboxes(mAmAccount, mailboxIds);
+        requestSyncForMailboxes(mAmAccount, EmailContent.AUTHORITY, emailMailboxIds);
+        requestSyncForMailboxes(mAmAccount, CalendarContract.AUTHORITY, calendarMailboxIds);
+        requestSyncForMailboxes(mAmAccount, ContactsContract.AUTHORITY, contactsMailboxIds);
     }
 
     /**
@@ -381,7 +387,7 @@
         final Bundle extras = new Bundle(1);
         extras.putBoolean(Mailbox.SYNC_EXTRA_ACCOUNT_ONLY, true);
         ContentResolver.requestSync(mAmAccount, EmailContent.AUTHORITY, extras);
-        LogUtils.i(LOG_TAG, "requestFolderSync EasOperation %s, %s",
+        LogUtils.i(LOG_TAG, "requestFolderSync EasPing %s, %s",
                 mAmAccount.toString(), extras.toString());
     }
 
diff --git a/src/com/android/exchange/service/AbstractSyncAdapterService.java b/src/com/android/exchange/service/AbstractSyncAdapterService.java
index bb1e7e2..ae55bc3 100644
--- a/src/com/android/exchange/service/AbstractSyncAdapterService.java
+++ b/src/com/android/exchange/service/AbstractSyncAdapterService.java
@@ -16,12 +16,28 @@
 
 package com.android.exchange.service;
 
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.app.Service;
 import android.content.AbstractThreadedSyncAdapter;
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.SyncResult;
+import android.database.Cursor;
+import android.net.Uri;
 import android.os.IBinder;
+import android.text.format.DateUtils;
 
+import com.android.emailcommon.provider.Account;
 import com.android.emailcommon.provider.EmailContent;
+import com.android.emailcommon.service.EmailServiceStatus;
+import com.android.emailcommon.service.IEmailService;
+import com.android.emailcommon.utility.IntentUtilities;
+import com.android.exchange.R;
+import com.android.mail.utils.LogUtils;
 
 /**
  * Base class for services that handle sync requests from the system SyncManager.
@@ -29,15 +45,49 @@
  * should just implement their sync adapter, and override {@link #getSyncAdapter}.
  */
 public abstract class AbstractSyncAdapterService extends Service {
+    private static final String TAG = LogUtils.TAG;
+
+    // The call to ServiceConnection.onServiceConnected is asynchronous to bindService. It's
+    // possible for that to be delayed if, in which case, a call to onPerformSync
+    // could occur before we have a connection to the service.
+    // In onPerformSync, if we don't yet have our EasService, we will wait for up to 10
+    // seconds for it to appear. If it takes longer than that, we will fail the sync.
+    private static final long MAX_WAIT_FOR_SERVICE_MS = 10 * DateUtils.SECOND_IN_MILLIS;
+
     public AbstractSyncAdapterService() {
         super();
     }
 
+    protected IEmailService mEasService;
+    protected ServiceConnection mConnection;
+
     @Override
     public void onCreate() {
         super.onCreate();
         // Make sure EmailContent is initialized in Exchange app
         EmailContent.init(this);
+        mConnection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name,  IBinder binder) {
+                LogUtils.v(TAG, "onServiceConnected");
+                synchronized (mConnection) {
+                    mEasService = IEmailService.Stub.asInterface(binder);
+                    mConnection.notify();
+                }
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                mEasService = null;
+            }
+        };
+        bindService(new Intent(this, EasService.class), mConnection, Context.BIND_AUTO_CREATE);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        unbindService(mConnection);
     }
 
     @Override
@@ -51,4 +101,113 @@
      * @return An instance of the sync adapter.
      */
     protected abstract AbstractThreadedSyncAdapter getSyncAdapter();
+
+    /**
+     * Create and return an intent to display (and edit) settings for a specific account, or -1
+     * for any/all accounts.  If an account name string is provided, a warning dialog will be
+     * displayed as well.
+     */
+    public static Intent createAccountSettingsIntent(long accountId, String accountName) {
+        final Uri.Builder builder = IntentUtilities.createActivityIntentUrlBuilder(
+                IntentUtilities.PATH_SETTINGS);
+        IntentUtilities.setAccountId(builder, accountId);
+        IntentUtilities.setAccountName(builder, accountName);
+        return new Intent(Intent.ACTION_EDIT, builder.build());
+    }
+
+    protected void showAuthNotification(long accountId, String accountName) {
+        final PendingIntent pendingIntent = PendingIntent.getActivity(
+                this,
+                0,
+                createAccountSettingsIntent(accountId, accountName),
+                0);
+
+        final Notification notification = new Notification.Builder(this)
+                .setContentTitle(this.getString(R.string.auth_error_notification_title))
+                .setContentText(this.getString(
+                        R.string.auth_error_notification_text, accountName))
+                .setSmallIcon(R.drawable.stat_notify_auth)
+                .setContentIntent(pendingIntent)
+                .setAutoCancel(true)
+                .getNotification();
+
+        final NotificationManager nm = (NotificationManager)
+                this.getSystemService(Context.NOTIFICATION_SERVICE);
+        nm.notify("AuthError", 0, notification);
+    }
+
+    /**
+     * Interpret a result code from an {@link IEmailService.sync()} and, if it's an error, write
+     * it to the appropriate field in {@link android.content.SyncResult}.
+     * @param result
+     * @param syncResult
+     * @return Whether an error code was written to syncResult.
+     */
+    public static boolean writeResultToSyncResult(final int result, final SyncResult syncResult) {
+        switch (result) {
+            case EmailServiceStatus.SUCCESS:
+                return false;
+
+            case EmailServiceStatus.REMOTE_EXCEPTION:
+            case EmailServiceStatus.LOGIN_FAILED:
+            case EmailServiceStatus.SECURITY_FAILURE:
+            case EmailServiceStatus.CLIENT_CERTIFICATE_ERROR:
+            case EmailServiceStatus.ACCESS_DENIED:
+                syncResult.stats.numAuthExceptions = 1;
+                return true;
+
+            case EmailServiceStatus.HARD_DATA_ERROR:
+            case EmailServiceStatus.INTERNAL_ERROR:
+                syncResult.databaseError = true;
+                return true;
+
+            case EmailServiceStatus.CONNECTION_ERROR:
+            case EmailServiceStatus.IO_ERROR:
+                syncResult.stats.numIoExceptions = 1;
+                return true;
+
+            case EmailServiceStatus.TOO_MANY_REDIRECTS:
+                syncResult.tooManyRetries = true;
+                return true;
+
+            case EmailServiceStatus.IN_PROGRESS:
+            case EmailServiceStatus.MESSAGE_NOT_FOUND:
+            case EmailServiceStatus.ATTACHMENT_NOT_FOUND:
+            case EmailServiceStatus.FOLDER_NOT_DELETED:
+            case EmailServiceStatus.FOLDER_NOT_RENAMED:
+            case EmailServiceStatus.FOLDER_NOT_CREATED:
+            case EmailServiceStatus.ACCOUNT_UNINITIALIZED:
+            case EmailServiceStatus.PROTOCOL_ERROR:
+                LogUtils.e(TAG, "Unexpected sync result %d", result);
+                return false;
+        }
+        return false;
+    }
+
+    protected final boolean waitForService() {
+        synchronized(mConnection) {
+            if (mEasService == null) {
+                LogUtils.d(TAG, "service not yet connected");
+                try {
+                    mConnection.wait(MAX_WAIT_FOR_SERVICE_MS);
+                } catch (InterruptedException e) {
+                    LogUtils.wtf(TAG, "InterrupedException waiting for EasService to connect");
+                    return false;
+                }
+                if (mEasService == null) {
+                    LogUtils.wtf(TAG, "timed out waiting for EasService to connect");
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    protected final Account getAccountFromAndroidAccount(final android.accounts.Account acct) {
+        final Account emailAccount;
+        emailAccount = Account.restoreAccountWithAddress(this, acct.name);
+        return emailAccount;
+    }
+
 }
+
diff --git a/src/com/android/exchange/service/CalendarSyncAdapterService.java b/src/com/android/exchange/service/CalendarSyncAdapterService.java
index 388f880..ee2844e 100644
--- a/src/com/android/exchange/service/CalendarSyncAdapterService.java
+++ b/src/com/android/exchange/service/CalendarSyncAdapterService.java
@@ -16,25 +16,27 @@
 
 package com.android.exchange.service;
 
-import android.accounts.Account;
 import android.content.AbstractThreadedSyncAdapter;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.ServiceConnection;
 import android.content.SyncResult;
 import android.database.Cursor;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.provider.CalendarContract.Events;
 import android.util.Log;
 
-import com.android.emailcommon.provider.EmailContent;
+import com.android.emailcommon.provider.Account;
 import com.android.emailcommon.provider.EmailContent.MailboxColumns;
 import com.android.emailcommon.provider.Mailbox;
+import com.android.emailcommon.service.EmailServiceStatus;
 import com.android.exchange.Eas;
 import com.android.mail.utils.LogUtils;
 
 public class CalendarSyncAdapterService extends AbstractSyncAdapterService {
-    private static final String TAG = Eas.LOG_TAG;
+    private static final String TAG = LogUtils.TAG;
     private static final String ACCOUNT_AND_TYPE_CALENDAR =
         MailboxColumns.ACCOUNT_KEY + "=? AND " + MailboxColumns.TYPE + '=' + Mailbox.TYPE_CALENDAR;
     private static final String DIRTY_IN_ACCOUNT =
@@ -57,75 +59,80 @@
         }
     }
 
-    private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
+    private class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
         public SyncAdapterImpl(Context context) {
             super(context, true /* autoInitialize */);
         }
 
         @Override
-        public void onPerformSync(Account account, Bundle extras,
+        public void onPerformSync(android.accounts.Account acct, Bundle extras,
                 String authority, ContentProviderClient provider, SyncResult syncResult) {
             if (LogUtils.isLoggable(TAG, Log.DEBUG)) {
-                LogUtils.d(TAG, "onPerformSync Calendar starting %s, %s", account.toString(),
-                        extras.toString());
+                LogUtils.d(TAG, "onPerformSync calendar: %s, %s",
+                        acct.toString(), extras.toString());
             } else {
-                LogUtils.i(TAG, "onPerformSync Calendar starting %s", extras.toString());
+                LogUtils.i(TAG, "onPerformSync calendar: %s", extras.toString());
             }
-            CalendarSyncAdapterService.performSync(getContext(), account, extras);
-            LogUtils.d(TAG, "onPerformSync Calendar finished");
-        }
-    }
 
-    /**
-     * Partial integration with system SyncManager; we tell our EasService 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) {
-        if (extras.getBoolean(Mailbox.SYNC_EXTRA_NOOP, false)) {
-            LogUtils.d(TAG, "No-op sync requested, done");
-            return;
-        }
-
-        final ContentResolver cr = context.getContentResolver();
-        final boolean logging = Eas.USER_LOG;
-        if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD)) {
-            final Cursor c = cr.query(Events.CONTENT_URI,
-                    new String[] {Events._ID}, DIRTY_IN_ACCOUNT, new String[] {account.name}, null);
-            if (c == null) {
-                LogUtils.e(TAG, "Null changes cursor in CalendarSyncAdapterService");
+            if (!waitForService()) {
+                // The service didn't connect, nothing we can do.
                 return;
             }
-            try {
-                if (!c.moveToFirst()) {
-                    if (logging) {
-                        LogUtils.d(TAG, "No changes for " + account.name);
-                    }
+            final Account emailAccount = Account.restoreAccountWithAddress(
+                    CalendarSyncAdapterService.this, acct.name);
+
+            // TODO: is this still needed?
+            if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD)) {
+                final Cursor c = getContentResolver().query(Events.CONTENT_URI,
+                        new String[] {Events._ID}, DIRTY_IN_ACCOUNT,
+                        new String[] {acct.name}, null);
+                if (c == null) {
+                    LogUtils.e(TAG, "Null changes cursor in CalendarSyncAdapterService");
                     return;
                 }
-            } finally {
-                c.close();
+                try {
+                    if (!c.moveToFirst()) {
+                        if (Eas.USER_LOG) {
+                            LogUtils.d(TAG, "No changes for " + acct.name);
+                        }
+                        return;
+                    }
+                } finally {
+                    c.close();
+                }
             }
-        }
 
-        // Forward the sync request to the EmailSyncAdapterService.
-        final long [] mailboxIds = Mailbox.getMailboxIdsFromBundle(extras);
-        final Bundle mailExtras;
-        if (mailboxIds == null) {
-            // We weren't given any particular mailboxId, specify a sync for all calendars.
-            mailExtras = new Bundle();
-            mailExtras.putInt(Mailbox.SYNC_EXTRA_MAILBOX_TYPE, Mailbox.TYPE_CALENDAR);
-        } else {
-            // Otherwise, add all of the mailboxes specified in the original sync extras.
-            mailExtras = Mailbox.createSyncBundle(mailboxIds);
+            // TODO: move this logic to some common place.
+            // Push only means this sync request should only refresh the ping (either because
+            // settings changed, or we need to restart it for some reason).
+            final boolean pushOnly = Mailbox.isPushOnlyExtras(extras);
+
+            if (pushOnly) {
+                LogUtils.d(TAG, "onPerformSync calendar: mailbox push only");
+                if (mEasService != null) {
+                    try {
+                        mEasService.pushModify(emailAccount.mId);
+                        return;
+                    } catch (final RemoteException re) {
+                        LogUtils.e(TAG, re, "While trying to pushModify within onPerformSync");
+                        // TODO: how to handle this?
+                    }
+                }
+                return;
+            } else {
+                try {
+                    final int result = mEasService.sync(emailAccount.mId, extras);
+                    writeResultToSyncResult(result, syncResult);
+                    if (syncResult.stats.numAuthExceptions > 0 &&
+                            result != EmailServiceStatus.PROVISIONING_ERROR) {
+                        showAuthNotification(emailAccount.mId, emailAccount.mEmailAddress);
+                    }
+                } catch (RemoteException e) {
+                    LogUtils.e(TAG, e, "While trying to pushModify within onPerformSync");
+                }
+            }
+
+            LogUtils.d(TAG, "onPerformSync calendar: finished");
         }
-        mailExtras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
-        mailExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
-        if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
-            mailExtras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
-        }
-        ContentResolver.requestSync(account, EmailContent.AUTHORITY, mailExtras);
-        LogUtils.d(TAG, "requestSync CalendarSyncAdapter %s", mailExtras.toString());
     }
 }
diff --git a/src/com/android/exchange/service/ContactsSyncAdapterService.java b/src/com/android/exchange/service/ContactsSyncAdapterService.java
index 99f0e8b..c8b5be5 100644
--- a/src/com/android/exchange/service/ContactsSyncAdapterService.java
+++ b/src/com/android/exchange/service/ContactsSyncAdapterService.java
@@ -16,7 +16,6 @@
 
 package com.android.exchange.service;
 
-import android.accounts.Account;
 import android.content.AbstractThreadedSyncAdapter;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
@@ -25,13 +24,16 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.provider.ContactsContract.Groups;
 import android.provider.ContactsContract.RawContacts;
 import android.util.Log;
 
+import com.android.emailcommon.provider.Account;
 import com.android.emailcommon.provider.EmailContent;
 import com.android.emailcommon.provider.EmailContent.MailboxColumns;
 import com.android.emailcommon.provider.Mailbox;
+import com.android.emailcommon.service.EmailServiceStatus;
 import com.android.exchange.Eas;
 import com.android.mail.utils.LogUtils;
 
@@ -57,22 +59,83 @@
         }
     }
 
-    private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
+    private class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
         public SyncAdapterImpl(Context context) {
             super(context, true /* autoInitialize */);
         }
 
         @Override
-        public void onPerformSync(Account account, Bundle extras,
+        public void onPerformSync(android.accounts.Account acct, Bundle extras,
                 String authority, ContentProviderClient provider, SyncResult syncResult) {
             if (LogUtils.isLoggable(TAG, Log.DEBUG)) {
-                LogUtils.d(TAG, "onPerformSync Contacts starting %s, %s", account.toString(),
+                LogUtils.d(TAG, "onPerformSync contacts starting %s, %s", acct.toString(),
                         extras.toString());
             } else {
-                LogUtils.i(TAG, "onPerformSync Contacts starting %s", extras.toString());
+                LogUtils.i(TAG, "onPerformSync contacts starting %s", extras.toString());
             }
-            ContactsSyncAdapterService.performSync(getContext(), account, extras);
-            LogUtils.d(TAG, "onPerformSync Contacts finished");
+            if (!waitForService()) {
+                // The service didn't connect, nothing we can do.
+                return;
+            }
+
+            final Account emailAccount = Account.restoreAccountWithAddress(
+                    ContactsSyncAdapterService.this, acct.name);
+
+            // TODO: is this still needed?
+            // 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, acct.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(getContentResolver(), uri, RawContacts.DIRTY);
+                if (!changed) {
+                    uri = Groups.CONTENT_URI.buildUpon()
+                            .appendQueryParameter(RawContacts.ACCOUNT_NAME, acct.name)
+                            .appendQueryParameter(RawContacts.ACCOUNT_TYPE,
+                                    Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE)
+                            .build();
+                    changed = hasDirtyRows(getContentResolver(), uri, Groups.DIRTY);
+                }
+                if (!changed) {
+                    LogUtils.d(TAG, "Upload sync; no changes");
+                    return;
+                }
+            }
+
+            // TODO: move this to some common place.
+            // Push only means this sync request should only refresh the ping (either because
+            // settings changed, or we need to restart it for some reason).
+            final boolean pushOnly = Mailbox.isPushOnlyExtras(extras);
+
+            if (pushOnly) {
+                LogUtils.d(TAG, "onPerformSync email: mailbox push only");
+                if (mEasService != null) {
+                    try {
+                        mEasService.pushModify(emailAccount.mId);
+                        return;
+                    } catch (final RemoteException re) {
+                        LogUtils.e(TAG, re, "While trying to pushModify within onPerformSync");
+                        // TODO: how to handle this?
+                    }
+                }
+                return;
+            } else {
+                try {
+                    final int result = mEasService.sync(emailAccount.mId, extras);
+                    writeResultToSyncResult(result, syncResult);
+                    if (syncResult.stats.numAuthExceptions > 0 &&
+                            result != EmailServiceStatus.PROVISIONING_ERROR) {
+                        showAuthNotification(emailAccount.mId, emailAccount.mEmailAddress);
+                    }
+                } catch (RemoteException e) {
+                    LogUtils.e(TAG, e, "While trying to pushModify within onPerformSync");
+                }
+            }
+
+            LogUtils.d(TAG, "onPerformSync contacts: finished");
         }
     }
 
@@ -87,58 +150,4 @@
             c.close();
         }
     }
-
-    /**
-     * Partial integration with system SyncManager; we tell our EasService 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) {
-        if (extras.getBoolean(Mailbox.SYNC_EXTRA_NOOP, false)) {
-            LogUtils.d(TAG, "No-op sync requested, done");
-            return;
-        }
-        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) {
-                LogUtils.d(TAG, "Upload sync; no changes");
-                return;
-            }
-        }
-
-        // Forward the sync request to the EmailSyncAdapterService.
-        long [] mailboxIds = Mailbox.getMailboxIdsFromBundle(extras);
-        final Bundle mailExtras;
-        if (mailboxIds == null) {
-            // We weren't given any particular mailboxId, specify a sync for all contacts.
-            mailExtras = new Bundle();
-            mailExtras.putInt(Mailbox.SYNC_EXTRA_MAILBOX_TYPE, Mailbox.TYPE_CONTACTS);
-        } else {
-            // Otherwise, add all of the mailboxes specified in the original sync extras.
-            mailExtras = Mailbox.createSyncBundle(mailboxIds);
-        }
-        mailExtras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
-        mailExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
-        if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
-            mailExtras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
-        }
-        ContentResolver.requestSync(account, EmailContent.AUTHORITY, mailExtras);
-        LogUtils.d(TAG, "requestSync ContactsSyncAdapter %s", mailExtras.toString());
-    }
 }
diff --git a/src/com/android/exchange/service/EasServerConnection.java b/src/com/android/exchange/service/EasServerConnection.java
index e7f741b..faa092e 100644
--- a/src/com/android/exchange/service/EasServerConnection.java
+++ b/src/com/android/exchange/service/EasServerConnection.java
@@ -496,40 +496,4 @@
     public boolean isProtocolVersionSet() {
         return mProtocolVersionIsSet;
     }
-
-    /**
-     * Convenience method for adding a Message to an account's outbox
-     * @param account The {@link Account} from which to send the message.
-     * @param msg The message to send
-     */
-    protected void sendMessage(final Account account, final EmailContent.Message msg) {
-        long mailboxId = Mailbox.findMailboxOfType(mContext, account.mId, Mailbox.TYPE_OUTBOX);
-        // TODO: Improve system mailbox handling.
-        if (mailboxId == Mailbox.NO_MAILBOX) {
-            LogUtils.d(TAG, "No outbox for account %d, creating it", account.mId);
-            final Mailbox outbox =
-                    Mailbox.newSystemMailbox(mContext, account.mId, Mailbox.TYPE_OUTBOX);
-            outbox.save(mContext);
-            mailboxId = outbox.mId;
-        }
-        msg.mMailboxKey = mailboxId;
-        msg.mAccountKey = account.mId;
-        msg.save(mContext);
-        requestSyncForMailbox(new android.accounts.Account(account.mEmailAddress,
-                Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE), EmailContent.AUTHORITY, mailboxId);
-    }
-
-    /**
-     * Issue a {@link android.content.ContentResolver#requestSync} for a specific mailbox.
-     * @param amAccount The {@link android.accounts.Account} for the account we're pinging.
-     * @param authority The authority for the mailbox that needs to sync.
-     * @param mailboxId The id of the mailbox that needs to sync.
-     */
-    protected static void requestSyncForMailbox(final android.accounts.Account amAccount,
-            final String authority, final long mailboxId) {
-        final Bundle extras = Mailbox.createSyncBundle(mailboxId);
-        ContentResolver.requestSync(amAccount, authority, extras);
-        LogUtils.d(TAG, "requestSync EasServerConnection requestSyncForMailbox %s, %s",
-                amAccount.toString(), extras.toString());
-    }
 }
diff --git a/src/com/android/exchange/service/EasService.java b/src/com/android/exchange/service/EasService.java
index 720a577..4117279 100644
--- a/src/com/android/exchange/service/EasService.java
+++ b/src/com/android/exchange/service/EasService.java
@@ -29,6 +29,7 @@
 import android.provider.ContactsContract;
 import android.text.TextUtils;
 
+import com.android.emailcommon.TempDirectory;
 import com.android.emailcommon.provider.Account;
 import com.android.emailcommon.provider.EmailContent;
 import com.android.emailcommon.provider.EmailContent.Message;
@@ -233,6 +234,7 @@
                 try {
                     while (c.moveToNext()) {
                         final Account account = new Account();
+                        LogUtils.d(TAG, "RestartPingsTask starting ping for %s", account);
                         account.restore(c);
                         if (EasService.this.pingNeededForAccount(account)) {
                             mHasRestartedPing = true;
@@ -262,7 +264,9 @@
 
     @Override
     public void onCreate() {
+        LogUtils.d(TAG, "EasService.onCreate");
         super.onCreate();
+        TempDirectory.setTempDirectory(this);
         EmailContent.init(this);
         AUTHORITIES_TO_SYNC = new String[] {
                 EmailContent.AUTHORITY,
diff --git a/src/com/android/exchange/service/EmailSyncAdapterService.java b/src/com/android/exchange/service/EmailSyncAdapterService.java
index 32d20d5..1619023 100644
--- a/src/com/android/exchange/service/EmailSyncAdapterService.java
+++ b/src/com/android/exchange/service/EmailSyncAdapterService.java
@@ -16,70 +16,28 @@
 
 package com.android.exchange.service;
 
-import android.app.Notification;
-import android.app.Notification.Builder;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
 import android.content.AbstractThreadedSyncAdapter;
-import android.content.ComponentName;
 import android.content.ContentProviderClient;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.ServiceConnection;
 import android.content.SyncResult;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.Log;
 
-import com.android.emailcommon.TempDirectory;
 import com.android.emailcommon.provider.Account;
-import com.android.emailcommon.provider.EmailContent;
-import com.android.emailcommon.provider.EmailContent.AccountColumns;
-import com.android.emailcommon.provider.EmailContent.MessageColumns;
-import com.android.emailcommon.provider.EmailContent.SyncColumns;
 import com.android.emailcommon.provider.Mailbox;
 import com.android.emailcommon.service.EmailServiceStatus;
 import com.android.emailcommon.service.IEmailService;
-import com.android.emailcommon.service.ServiceProxy;
-import com.android.emailcommon.utility.IntentUtilities;
 import com.android.exchange.Eas;
-import com.android.exchange.R;
-import com.android.exchange.eas.EasFolderSync;
-import com.android.exchange.eas.EasMoveItems;
-import com.android.exchange.eas.EasOperation;
-import com.android.exchange.eas.EasPing;
-import com.android.exchange.eas.EasSync;
 import com.android.mail.utils.LogUtils;
 
-/**
- * TODO: Most of this is deprecated.
- * This is needed to handle syncs, but pings and IEmailService functionality will be in EasService.
- * Also, we need to refactor sync functionality into some common class so that it can be used by
- * this, and ContactsSyncAdapterService and CalendarSyncAdapterService.
- *
- * Service for communicating with Exchange servers. There are three main parts of this class:
- * TODO: Flesh out these comments.
- * 1) An {@link AbstractThreadedSyncAdapter} to handle actually performing syncs.
- * 2) Bookkeeping for running Ping requests, which handles push notifications.
- * 3) An {@link IEmailService} Stub to handle RPC from the UI.
- */
 public class EmailSyncAdapterService extends AbstractSyncAdapterService {
 
     private static final String TAG = Eas.LOG_TAG;
 
-    private IEmailService mEasService;
-    private ServiceConnection mConnection;
-
-    private static final String EXTRA_START_PING = "START_PING";
-    private static final String EXTRA_PING_ACCOUNT = "PING_ACCOUNT";
-
     // The call to ServiceConnection.onServiceConnected is asynchronous to bindService. It's
     // possible for that to be delayed if, in which case, a call to onPerformSync
     // could occur before we have a connection to the service.
@@ -106,88 +64,17 @@
         super();
     }
 
-    /**
-     * {@link AsyncTask} for restarting pings for all accounts that need it.
-     */
-    private static final String[] PUSH_ACCOUNTS_PROJECTION = new String[] {AccountColumns._ID};
-    private static final String PUSH_ACCOUNTS_SELECTION =
-            AccountColumns.SYNC_INTERVAL + "=" + Integer.toString(Account.CHECK_INTERVAL_PUSH);
-    private class RestartPingsTask extends AsyncTask<Void, Void, Void> {
-
-        private final ContentResolver mContentResolver;
-        private final IEmailService mEasService;
-        private boolean mAnyAccounts;
-
-        public RestartPingsTask(final ContentResolver contentResolver,
-                                final IEmailService easService) {
-            mContentResolver = contentResolver;
-            mEasService = easService;
-        }
-
-        @Override
-        protected Void doInBackground(Void... params) {
-            final Cursor c = mContentResolver.query(Account.CONTENT_URI,
-                    PUSH_ACCOUNTS_PROJECTION, PUSH_ACCOUNTS_SELECTION, null, null);
-            if (c != null) {
-                try {
-                    mAnyAccounts = (c.getCount() != 0);
-                    while (c.moveToNext()) {
-                        try {
-                            mEasService.pushModify(c.getLong(0));
-                        } catch (RemoteException re) {
-                            LogUtils.wtf(TAG, re, "While trying to pushModify in RestartPingsTask");
-                            // TODO: how to handle this?
-                        }
-                    }
-                } finally {
-                    c.close();
-                }
-            } else {
-                mAnyAccounts = false;
-            }
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Void result) {
-            if (!mAnyAccounts) {
-                LogUtils.d(TAG, "stopping for no accounts");
-                EmailSyncAdapterService.this.stopSelf();
-            }
-        }
-    }
-
     @Override
     public void onCreate() {
-        LogUtils.v(TAG, "onCreate()");
+        LogUtils.v(TAG, "EmailSyncAdapterService.onCreate()");
         super.onCreate();
         startService(new Intent(this, EmailSyncAdapterService.class));
-        mConnection = new ServiceConnection() {
-            @Override
-            public void onServiceConnected(ComponentName name,  IBinder binder) {
-                LogUtils.v(TAG, "onServiceConnected");
-                synchronized (mConnection) {
-                    mEasService = IEmailService.Stub.asInterface(binder);
-                    // Start up the initial pings now that we have an EasService
-                    new RestartPingsTask(getContentResolver(), mEasService).executeOnExecutor(
-                            AsyncTask.THREAD_POOL_EXECUTOR);
-                    mConnection.notify();
-                }
-            }
-
-            @Override
-            public void onServiceDisconnected(ComponentName name) {
-                mEasService = null;
-            }
-        };
-        bindService(new Intent(this, EasService.class), mConnection, Context.BIND_AUTO_CREATE);
     }
 
     @Override
     public void onDestroy() {
-        LogUtils.v(TAG, "onDestroy()");
+        LogUtils.v(TAG, "EmailSyncAdapterService.onDestroy()");
         super.onDestroy();
-        unbindService(mConnection);
     }
 
     @Override
@@ -196,30 +83,6 @@
     }
 
     @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        if (intent != null &&
-                TextUtils.equals(Eas.EXCHANGE_SERVICE_INTENT_ACTION, intent.getAction())) {
-            if (intent.getBooleanExtra(ServiceProxy.EXTRA_FORCE_SHUTDOWN, false)) {
-                // We've been asked to forcibly shutdown. This happens if email accounts are
-                // deleted, otherwise we can get errors if services are still running for
-                // accounts that are now gone.
-                // TODO: This is kind of a hack, it would be nicer if we could handle it correctly
-                // if accounts disappear out from under us.
-                LogUtils.d(TAG, "Forced shutdown, killing process");
-                System.exit(-1);
-            } else if (intent.getBooleanExtra(EXTRA_START_PING, false)) {
-                LogUtils.d(TAG, "Restarting ping from alarm");
-                // We've been woken up by an alarm to restart our ping. This happens if a sync
-                // fails, rather that instantly starting the ping, we'll hold off for a few minutes.
-                final android.accounts.Account account =
-                        intent.getParcelableExtra(EXTRA_PING_ACCOUNT);
-                EasPing.requestPing(account);
-            }
-        }
-        return super.onStartCommand(intent, flags, startId);
-    }
-
-    @Override
     protected AbstractThreadedSyncAdapter getSyncAdapter() {
         synchronized (sSyncAdapterLock) {
             if (sSyncAdapter == null) {
@@ -240,54 +103,22 @@
                 final String authority, final ContentProviderClient provider,
                 final SyncResult syncResult) {
             if (LogUtils.isLoggable(TAG, Log.DEBUG)) {
-                LogUtils.d(TAG, "onPerformSync: %s, %s", acct.toString(), extras.toString());
+                LogUtils.d(TAG, "onPerformSync email: %s, %s", acct.toString(), extras.toString());
             } else {
-                LogUtils.i(TAG, "onPerformSync: %s", extras.toString());
+                LogUtils.i(TAG, "onPerformSync email: %s", extras.toString());
             }
-            synchronized(mConnection) {
-                if (mEasService == null) {
-                    LogUtils.d(TAG, "service not yet connected");
-                    try {
-                        mConnection.wait(MAX_WAIT_FOR_SERVICE_MS);
-                    } catch (InterruptedException e) {
-                        LogUtils.wtf(TAG, "InterrupedException waiting for EasService to connect");
-                    }
-                    if (mEasService == null) {
-                        LogUtils.wtf(TAG, "timed out waiting for EasService to connect");
-                        return;
-                    }
-                }
+            if (!waitForService()) {
+                // The service didn't connect, nothing we can do.
+                return;
             }
 
-            TempDirectory.setTempDirectory(EmailSyncAdapterService.this);
-
             // TODO: Perform any connectivity checks, bail early if we don't have proper network
             // for this sync operation.
             // FLAG: Do we actually need to do this? I don't think the sync manager will invoke
             // a sync if we don't have good network.
 
-            final Context context = getContext();
-            final ContentResolver cr = context.getContentResolver();
-
-            // Get the EmailContent Account
-            // TODO shouldn't this functionality live in Account, not here?
-            final Account account;
-            final Cursor accountCursor = cr.query(Account.CONTENT_URI, Account.CONTENT_PROJECTION,
-                    AccountColumns.EMAIL_ADDRESS + "=?", new String[] {acct.name}, null);
-            try {
-                if (accountCursor == null || !accountCursor.moveToFirst()) {
-                    // Could not load account.
-                    // TODO: improve error handling.
-                    LogUtils.w(TAG, "onPerformSync: could not load account");
-                    return;
-                }
-                account = new Account();
-                account.restore(accountCursor);
-            } finally {
-                if (accountCursor != null) {
-                    accountCursor.close();
-                }
-            }
+            final Account emailAccount = Account.restoreAccountWithAddress(
+                    EmailSyncAdapterService.this, acct.name);
 
             // Push only means this sync request should only refresh the ping (either because
             // settings changed, or we need to restart it for some reason).
@@ -296,7 +127,7 @@
             if (pushOnly) {
                 LogUtils.d(TAG, "onPerformSync: mailbox push only");
                 try {
-                    mEasService.pushModify(account.mId);
+                    mEasService.pushModify(emailAccount.mId);
                     return;
                 } catch (final RemoteException re) {
                     LogUtils.e(TAG, re, "While trying to pushModify within onPerformSync");
@@ -305,100 +136,18 @@
                 return;
             } else {
                 try {
-                    final int result = mEasService.sync(account.mId, extras);
+                    final int result = mEasService.sync(emailAccount.mId, extras);
                     writeResultToSyncResult(result, syncResult);
                     if (syncResult.stats.numAuthExceptions > 0 &&
                             result != EmailServiceStatus.PROVISIONING_ERROR) {
-                        showAuthNotification(account.mId, account.mEmailAddress);
+                        showAuthNotification(emailAccount.mId, emailAccount.mEmailAddress);
                     }
                 } catch (RemoteException e) {
                      LogUtils.e(TAG, e, "While trying to pushModify within onPerformSync");
                 }
             }
 
-            LogUtils.d(TAG, "onPerformSync: finished");
+            LogUtils.d(TAG, "onPerformSync email: finished");
         }
     }
-
-    private void showAuthNotification(long accountId, String accountName) {
-        final PendingIntent pendingIntent = PendingIntent.getActivity(
-                this,
-                0,
-                createAccountSettingsIntent(accountId, accountName),
-                0);
-
-        final Notification notification = new Builder(this)
-                .setContentTitle(this.getString(R.string.auth_error_notification_title))
-                .setContentText(this.getString(
-                        R.string.auth_error_notification_text, accountName))
-                .setSmallIcon(R.drawable.stat_notify_auth)
-                .setContentIntent(pendingIntent)
-                .setAutoCancel(true)
-                .build();
-
-        final NotificationManager nm = (NotificationManager)
-                this.getSystemService(Context.NOTIFICATION_SERVICE);
-        nm.notify("AuthError", 0, notification);
-    }
-
-    /**
-     * Create and return an intent to display (and edit) settings for a specific account, or -1
-     * for any/all accounts.  If an account name string is provided, a warning dialog will be
-     * displayed as well.
-     */
-    public static Intent createAccountSettingsIntent(long accountId, String accountName) {
-        final Uri.Builder builder = IntentUtilities.createActivityIntentUrlBuilder(
-                IntentUtilities.PATH_SETTINGS);
-        IntentUtilities.setAccountId(builder, accountId);
-        IntentUtilities.setAccountName(builder, accountName);
-        return new Intent(Intent.ACTION_EDIT, builder.build());
-    }
-
-    /**
-     * Interpret a result code from an {@link IEmailService.sync()} and, if it's an error, write it to
-     * the appropriate field in {@link SyncResult}.
-     * @param result
-     * @param syncResult
-     * @return Whether an error code was written to syncResult.
-     */
-    public static boolean writeResultToSyncResult(final int result, final SyncResult syncResult) {
-        switch (result) {
-            case EmailServiceStatus.SUCCESS:
-                return false;
-
-            case EmailServiceStatus.REMOTE_EXCEPTION:
-            case EmailServiceStatus.LOGIN_FAILED:
-            case EmailServiceStatus.SECURITY_FAILURE:
-            case EmailServiceStatus.CLIENT_CERTIFICATE_ERROR:
-            case EmailServiceStatus.ACCESS_DENIED:
-                    syncResult.stats.numAuthExceptions = 1;
-                return true;
-
-            case EmailServiceStatus.HARD_DATA_ERROR:
-            case EmailServiceStatus.INTERNAL_ERROR:
-                syncResult.databaseError = true;
-                return true;
-
-            case EmailServiceStatus.CONNECTION_ERROR:
-            case EmailServiceStatus.IO_ERROR:
-                syncResult.stats.numIoExceptions = 1;
-                return true;
-
-            case EmailServiceStatus.TOO_MANY_REDIRECTS:
-                syncResult.tooManyRetries = true;
-                return true;
-
-            case EmailServiceStatus.IN_PROGRESS:
-            case EmailServiceStatus.MESSAGE_NOT_FOUND:
-            case EmailServiceStatus.ATTACHMENT_NOT_FOUND:
-            case EmailServiceStatus.FOLDER_NOT_DELETED:
-            case EmailServiceStatus.FOLDER_NOT_RENAMED:
-            case EmailServiceStatus.FOLDER_NOT_CREATED:
-            case EmailServiceStatus.ACCOUNT_UNINITIALIZED:
-            case EmailServiceStatus.PROTOCOL_ERROR:
-                LogUtils.e(TAG, "Unexpected sync result %d", result);
-                return false;
-        }
-        return false;
-    }
 }