Merge "Split Apply Ops Into Chuncks" into jb-ub-mail-ur10
diff --git a/src/com/android/exchange/ExchangeService.java b/src/com/android/exchange/ExchangeService.java
index cd391c3..8733b5c 100644
--- a/src/com/android/exchange/ExchangeService.java
+++ b/src/com/android/exchange/ExchangeService.java
@@ -24,6 +24,7 @@
 import android.content.Intent;
 import android.database.ContentObserver;
 import android.database.Cursor;
+import android.database.DatabaseUtils;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -32,6 +33,8 @@
 import android.provider.CalendarContract;
 import android.provider.CalendarContract.Calendars;
 import android.provider.CalendarContract.Events;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.RawContacts;
 
 import com.android.emailcommon.Api;
 import com.android.emailcommon.provider.Account;
@@ -363,34 +366,6 @@
         return accounts;
     }
 
-    public static void deleteAccountPIMData(final Context context, final long accountId) {
-        /*Mailbox mailbox =
-            Mailbox.restoreMailboxOfType(context, accountId, Mailbox.TYPE_CONTACTS);
-        if (mailbox != null) {
-            EasSyncService service = EasSyncService.getServiceForMailbox(context, mailbox);
-            // ContactsSyncAdapter is gone now, and this class is deprecated.
-            // Just leaving this commented out code here for reference.
-            ContactsSyncAdapter adapter = new ContactsSyncAdapter(service);
-            adapter.wipe();
-        }*/
-        final Mailbox mailbox =
-            Mailbox.restoreMailboxOfType(context, accountId, Mailbox.TYPE_CALENDAR);
-
-        if (mailbox != null) {
-            final EasSyncService service = EasSyncService.getServiceForMailbox(context, mailbox);
-            final Uri eventsAsSyncAdapter = eventsAsSyncAdapter(Events.CONTENT_URI,
-                    service.mAccount.mEmailAddress, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE);
-            // CalenderSyncAdapter is gone now, and this class is deprecated.
-            // Just leaving this commented out code here for reference.
-//          CalendarSyncAdapter adapter = new CalendarSyncAdapter(service);
-//          adapter.wipe();
-            // Need to have a selection since we don't specify an id in the url
-            context.getContentResolver().delete(eventsAsSyncAdapter, "TRUE", new String[0]);
-            unregisterCalendarObservers();
-
-        }
-    }
-
     public static boolean onSecurityHold(Account account) {
         return (account.mFlags & Account.FLAGS_SECURITY_HOLD) != 0;
     }
@@ -399,10 +374,12 @@
         return (account.mFlags & Account.FLAGS_SYNC_DISABLED) != 0;
     }
 
-    private static Uri eventsAsSyncAdapter(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();
+    private static Uri eventsAsSyncAdapter(final Uri uri, final String account,
+            final String accountType) {
+        return uri.buildUpon()
+                .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
+                .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
+                .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
     }
 
     /**
diff --git a/src/com/android/exchange/adapter/FolderSyncParser.java b/src/com/android/exchange/adapter/FolderSyncParser.java
index 2826533..49f88b3 100644
--- a/src/com/android/exchange/adapter/FolderSyncParser.java
+++ b/src/com/android/exchange/adapter/FolderSyncParser.java
@@ -40,6 +40,8 @@
 import com.android.exchange.CommandStatusException.CommandStatus;
 import com.android.exchange.Eas;
 import com.android.exchange.ExchangeService;
+import com.android.exchange.service.EasCalendarSyncHandler;
+import com.android.exchange.service.EasContactsSyncHandler;
 import com.android.mail.utils.LogUtils;
 import com.google.common.annotations.VisibleForTesting;
 
@@ -149,15 +151,15 @@
     @VisibleForTesting
     boolean mInUnitTest = false;
 
-    private String[] mBindArguments = new String[2];
+    private final String[] mBindArguments = new String[2];
 
     /** List of pending operations to send as a batch to the content provider. */
-    private ArrayList<ContentProviderOperation> mOperations =
+    private final ArrayList<ContentProviderOperation> mOperations =
             new ArrayList<ContentProviderOperation>();
     /** Indicates whether this sync is an initial FolderSync. */
     private boolean mInitialSync;
     /** List of folder server ids whose children changed with this sync. */
-    private ArrayList<String> mParentFixupsNeeded = new ArrayList<String>();
+    private final ArrayList<String> mParentFixupsNeeded = new ArrayList<String>();
     /** Indicates whether the sync response provided a different sync key than we had. */
     private boolean mSyncKeyChanged = false;
 
@@ -237,8 +239,11 @@
                     // and EAS 14 style command status
                     } else if (status == Eas.FOLDER_STATUS_INVALID_KEY ||
                             CommandStatus.isBadSyncKey(status)) {
-                        // Delete PIM data
-                        ExchangeService.deleteAccountPIMData(mContext, mAccountId);
+                        EasCalendarSyncHandler.wipeAccountFromContentProvider(mContext,
+                                mAccount.mEmailAddress);
+                        EasContactsSyncHandler.wipeAccountFromContentProvider(mContext,
+                                mAccount.mEmailAddress);
+
                         // Save away any mailbox sync information that is NOT default
                         saveMailboxSyncOptions();
                         // And only then, delete mailboxes
diff --git a/src/com/android/exchange/provider/ExchangeDirectoryProvider.java b/src/com/android/exchange/provider/ExchangeDirectoryProvider.java
index 75740bd..2b94271 100644
--- a/src/com/android/exchange/provider/ExchangeDirectoryProvider.java
+++ b/src/com/android/exchange/provider/ExchangeDirectoryProvider.java
@@ -84,6 +84,7 @@
 
     @Override
     public boolean onCreate() {
+        EmailContent.init(getContext());
         return true;
     }
 
diff --git a/src/com/android/exchange/service/AbstractSyncAdapterService.java b/src/com/android/exchange/service/AbstractSyncAdapterService.java
index eb56083..bb1e7e2 100644
--- a/src/com/android/exchange/service/AbstractSyncAdapterService.java
+++ b/src/com/android/exchange/service/AbstractSyncAdapterService.java
@@ -26,11 +26,9 @@
 /**
  * Base class for services that handle sync requests from the system SyncManager.
  * This class covers the boilerplate for using an {@link AbstractThreadedSyncAdapter}. Subclasses
- * should just implement their sync adapter, and override {@link #newSyncAdapter}.
+ * should just implement their sync adapter, and override {@link #getSyncAdapter}.
  */
 public abstract class AbstractSyncAdapterService extends Service {
-    private AbstractThreadedSyncAdapter mSyncAdapter = null;
-
     public AbstractSyncAdapterService() {
         super();
     }
@@ -40,17 +38,17 @@
         super.onCreate();
         // Make sure EmailContent is initialized in Exchange app
         EmailContent.init(this);
-        mSyncAdapter = newSyncAdapter();
     }
 
     @Override
     public IBinder onBind(Intent intent) {
-        return mSyncAdapter.getSyncAdapterBinder();
+        return getSyncAdapter().getSyncAdapterBinder();
     }
 
     /**
-     * Subclasses should override this to supply a new instance of its sync adapter.
-     * @return A new instance of the sync adapter.
+     * Subclasses should override this to supply an instance of its sync adapter. Best practice is
+     * to create a singleton and return that.
+     * @return An instance of the sync adapter.
      */
-    protected abstract AbstractThreadedSyncAdapter newSyncAdapter();
+    protected abstract AbstractThreadedSyncAdapter getSyncAdapter();
 }
diff --git a/src/com/android/exchange/service/CalendarSyncAdapterService.java b/src/com/android/exchange/service/CalendarSyncAdapterService.java
index 876b1c4..0297847 100644
--- a/src/com/android/exchange/service/CalendarSyncAdapterService.java
+++ b/src/com/android/exchange/service/CalendarSyncAdapterService.java
@@ -40,13 +40,21 @@
     private static final String DIRTY_IN_ACCOUNT =
         Events.DIRTY + "=1 AND " + Events.ACCOUNT_NAME + "=?";
 
+    private static final Object sSyncAdapterLock = new Object();
+    private static AbstractThreadedSyncAdapter sSyncAdapter = null;
+
     public CalendarSyncAdapterService() {
         super();
     }
 
     @Override
-    protected AbstractThreadedSyncAdapter newSyncAdapter() {
-        return new SyncAdapterImpl(this);
+    protected AbstractThreadedSyncAdapter getSyncAdapter() {
+        synchronized (sSyncAdapterLock) {
+            if (sSyncAdapter == null) {
+                sSyncAdapter = new SyncAdapterImpl(this);
+            }
+            return sSyncAdapter;
+        }
     }
 
     private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
diff --git a/src/com/android/exchange/service/ContactsSyncAdapterService.java b/src/com/android/exchange/service/ContactsSyncAdapterService.java
index 54ef2e0..d99cfcd 100644
--- a/src/com/android/exchange/service/ContactsSyncAdapterService.java
+++ b/src/com/android/exchange/service/ContactsSyncAdapterService.java
@@ -40,13 +40,21 @@
     private static final String ACCOUNT_AND_TYPE_CONTACTS =
         MailboxColumns.ACCOUNT_KEY + "=? AND " + MailboxColumns.TYPE + '=' + Mailbox.TYPE_CONTACTS;
 
+    private static final Object sSyncAdapterLock = new Object();
+    private static AbstractThreadedSyncAdapter sSyncAdapter = null;
+
     public ContactsSyncAdapterService() {
         super();
     }
 
     @Override
-    protected AbstractThreadedSyncAdapter newSyncAdapter() {
-        return new SyncAdapterImpl(this);
+    protected AbstractThreadedSyncAdapter getSyncAdapter() {
+        synchronized (sSyncAdapterLock) {
+            if (sSyncAdapter == null) {
+                sSyncAdapter = new SyncAdapterImpl(this);
+            }
+            return sSyncAdapter;
+        }
     }
 
     private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
diff --git a/src/com/android/exchange/service/EmailSyncAdapterService.java b/src/com/android/exchange/service/EmailSyncAdapterService.java
index 07ed718..964755c 100644
--- a/src/com/android/exchange/service/EmailSyncAdapterService.java
+++ b/src/com/android/exchange/service/EmailSyncAdapterService.java
@@ -73,6 +73,9 @@
     /** Projection used for getting email address for an account. */
     private static final String[] ACCOUNT_EMAIL_PROJECTION = { AccountColumns.EMAIL_ADDRESS };
 
+    private static final Object sSyncAdapterLock = new Object();
+    private static AbstractThreadedSyncAdapter sSyncAdapter = null;
+
     /**
      * Bookkeeping for handling synchronization between pings and syncs.
      * "Ping" refers to a hanging POST or GET that is used to receive push notifications. Ping is
@@ -496,8 +499,13 @@
     }
 
     @Override
-    protected AbstractThreadedSyncAdapter newSyncAdapter() {
-        return new SyncAdapterImpl(this);
+    protected AbstractThreadedSyncAdapter getSyncAdapter() {
+        synchronized (sSyncAdapterLock) {
+            if (sSyncAdapter == null) {
+                sSyncAdapter = new SyncAdapterImpl(this);
+            }
+            return sSyncAdapter;
+        }
     }
 
     // TODO: Handle cancelSync() appropriately.