Fix various problems related to synchronization, reload folder list, etc.

* The reloadFolderList service call mustn't reset the account mailbox if the foler
  list has never synced
* Be careful to not commit anything after a mailbox has been stopped
* Be careful to synchronize before checking the stopped state of a mailbox
diff --git a/src/com/android/exchange/AbstractSyncService.java b/src/com/android/exchange/AbstractSyncService.java
index 12dd3b0..063fb53 100644
--- a/src/com/android/exchange/AbstractSyncService.java
+++ b/src/com/android/exchange/AbstractSyncService.java
@@ -70,6 +70,8 @@
     protected Context mContext;
     public int mChangeCount = 0;
     public int mSyncReason = 0;
+    protected volatile boolean mStop = false;
+    private Object mSynchronizer = new Object();
 
     protected volatile long mRequestTime = 0;
     protected ArrayList<PartRequest> mPartRequests = new ArrayList<PartRequest>();
@@ -189,6 +191,14 @@
         }
     }
 
+    public boolean isStopped() {
+        return mStop;
+    }
+
+    public Object getSynchronizer() {
+        return mSynchronizer;
+    }
+
     /**
      * Asks SyncManager for a WaitLock for this sync
      */
diff --git a/src/com/android/exchange/EasSyncService.java b/src/com/android/exchange/EasSyncService.java
index e11da64..2a22b41 100644
--- a/src/com/android/exchange/EasSyncService.java
+++ b/src/com/android/exchange/EasSyncService.java
@@ -80,7 +80,8 @@
 
 public class EasSyncService extends AbstractSyncService {
 
-    private static final String WINDOW_SIZE = "10";
+    private static final String EMAIL_WINDOW_SIZE = "5";
+    private static final String PIM_WINDOW_SIZE = "20";
     private static final String WHERE_ACCOUNT_KEY_AND_SERVER_ID =
         MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SERVER_ID + "=?";
     private static final String WHERE_SYNC_INTERVAL_PING =
@@ -112,8 +113,6 @@
     public ContentResolver mContentResolver;
     String[] mBindArguments = new String[2];
     InputStream mPendingPartInputStream = null;
-    private volatile boolean mStop = false;
-    private Object mWaitTarget = new Object();
     private boolean mTriedReloadFolderList = false;
 
     public EasSyncService(Context _context, Mailbox _mailbox) {
@@ -135,8 +134,9 @@
     @Override
     public void ping() {
         userLog("We've been pinged!");
-        synchronized (mWaitTarget) {
-            mWaitTarget.notify();
+        Object synchronizer = getSynchronizer();
+        synchronized (synchronizer) {
+            synchronizer.notify();
         }
     }
 
@@ -426,12 +426,16 @@
 
             if (mAccount.mSyncKey == null) {
                 mAccount.mSyncKey = "0";
-                userLog("Account syncKey RESET");
+                userLog("Account syncKey INIT to 0");
                 ContentValues cv = new ContentValues();
                 cv.put(AccountColumns.SYNC_KEY, mAccount.mSyncKey);
                 mAccount.update(mContext, cv);
             }
 
+            if (mAccount.mSyncKey.equals("0")) {
+                userLog("Initial FolderSync");
+            }
+
             // When we first start up, change all ping mailboxes to push.
             ContentValues cv = new ContentValues();
             cv.put(Mailbox.SYNC_INTERVAL, Account.CHECK_INTERVAL_PUSH);
@@ -440,9 +444,9 @@
                 SyncManager.kick();
             }
 
-            userLog("Account syncKey: " + mAccount.mSyncKey);
             // Determine our protocol version, if we haven't already
             if (mAccount.mProtocolVersion == null) {
+                userLog("Determine EAS protocol version");
                 HttpURLConnection uc = setupEASCommand("OPTIONS", null);
                 if (uc != null) {
                     int code = uc.getResponseCode();
@@ -467,12 +471,15 @@
                     }
                 }
             }
+
             while (!mStop) {
+                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();
                 HttpURLConnection uc = sendEASPostCommand("FolderSync", s.toString());
                 int code = uc.getResponseCode();
+                if (mStop) break;
                 if (code == HttpURLConnection.HTTP_OK) {
                     String encoding = uc.getHeaderField("Transfer-Encoding");
                     if (encoding == null) {
@@ -895,7 +902,8 @@
             if (!mailbox.mSyncKey.equals("0")) {
                 s.tag(Tags.SYNC_GET_CHANGES);
             }
-            s.data(Tags.SYNC_WINDOW_SIZE, WINDOW_SIZE);
+            s.data(Tags.SYNC_WINDOW_SIZE,
+                    className.equals("Email") ? EMAIL_WINDOW_SIZE : PIM_WINDOW_SIZE);
             boolean options = false;
             if (!className.equals("Contacts")) {
                 // Set the lookback appropriately (EAS calls this a "filter")
diff --git a/src/com/android/exchange/SyncManager.java b/src/com/android/exchange/SyncManager.java
index fa074af..4a73154 100644
--- a/src/com/android/exchange/SyncManager.java
+++ b/src/com/android/exchange/SyncManager.java
@@ -291,14 +291,14 @@
                 for (Account account : mAccounts) {
                     if (!currentAccounts.contains(account.mId)) {
                         // This is a deletion; shut down any account-related syncs
-                        stopAccountSyncs(account.mId);
+                        stopAccountSyncs(account.mId, true);
                     } else {
                         // See whether any of our accounts has changed sync interval or window
                         if (accountChanged(account)) {
                             // Here's one that has...
                             INSTANCE.log("Account " + account.mDisplayName +
                                     " changed; stopping running syncs...");
-                            stopAccountSyncs(account.mId);
+                            stopAccountSyncs(account.mId, false);
                         }
                     }
                 }
@@ -343,13 +343,17 @@
             INSTANCE.log("Initializing account: " + acct.mDisplayName);
         }
 
-        void stopAccountSyncs(long acctId) {
+        void stopAccountSyncs(long acctId, boolean includeAccountMailbox) {
             synchronized (mSyncToken) {
                 List<Long> deletedBoxes = new ArrayList<Long>();
                 for (Long mid : INSTANCE.mServiceMap.keySet()) {
                     Mailbox box = Mailbox.restoreMailboxWithId(INSTANCE, mid);
                     if (box != null) {
                         if (box.mAccountKey == acctId) {
+                            if (!includeAccountMailbox &&
+                                    box.mType == Mailbox.TYPE_EAS_ACCOUNT_MAILBOX) {
+                                continue;
+                            }
                             AbstractSyncService svc = INSTANCE.mServiceMap.get(mid);
                             if (svc != null) {
                                 svc.stop();
@@ -498,11 +502,19 @@
         try {
             if (c.moveToFirst()) {
                 synchronized(mSyncToken) {
-                    long id = c.getLong(Mailbox.CONTENT_ID_COLUMN);
+                    Mailbox m = new Mailbox().restore(c);
+                    String syncKey = m.mSyncKey;
+                    // No need to reload the list if we don't have one
+                    if (syncKey == null || syncKey.equals("0")) {
+                        return;
+                    }
+                    long id = m.mId;
                     AbstractSyncService svc = INSTANCE.mServiceMap.get(id);
                     // Tell the service we're done
                     if (svc != null) {
-                        svc.stop();
+                        synchronized (svc.getSynchronizer()) {
+                            svc.stop();
+                        }
                         // Interrupt the thread so that it can stop
                         Thread thread = svc.mThread;
                         thread.setName(thread.getName() + " (Stopped)");
@@ -529,7 +541,7 @@
     static public void folderListReloaded(long acctId) {
         if (INSTANCE != null) {
             AccountObserver obs = INSTANCE.mAccountObserver;
-            obs.stopAccountSyncs(acctId);
+            obs.stopAccountSyncs(acctId, false);
             obs.addAccountMailbox(acctId);
         }
     }
@@ -740,7 +752,6 @@
 
         // If we're really debugging, turn on all logging
         if (Eas.DEBUG) {
-            Eas.PARSER_LOG = true;
             Eas.USER_LOG = true;
         }
 
diff --git a/src/com/android/exchange/adapter/AbstractSyncParser.java b/src/com/android/exchange/adapter/AbstractSyncParser.java
index c337407..6d18e02 100644
--- a/src/com/android/exchange/adapter/AbstractSyncParser.java
+++ b/src/com/android/exchange/adapter/AbstractSyncParser.java
@@ -135,12 +135,16 @@
            }
         }
 
-        // Make sure we save away the new syncKey, syncFrequency, etc.
-        ContentValues cv = new ContentValues();
-        cv.put(MailboxColumns.SYNC_KEY, mMailbox.mSyncKey);
-        cv.put(MailboxColumns.SYNC_INTERVAL, mMailbox.mSyncInterval);
-        mMailbox.update(mContext, cv);
-        mService.userLog(mMailbox.mDisplayName + " SyncKey saved as: " + mMailbox.mSyncKey);
+        synchronized (mService.getSynchronizer()) {
+            if (!mService.isStopped()) {
+                // Make sure we save away the new syncKey, syncFrequency, etc.
+                ContentValues cv = new ContentValues();
+                cv.put(MailboxColumns.SYNC_KEY, mMailbox.mSyncKey);
+                cv.put(MailboxColumns.SYNC_INTERVAL, mMailbox.mSyncInterval);
+                mMailbox.update(mContext, cv);
+                mService.userLog(mMailbox.mDisplayName + " SyncKey saved as: " + mMailbox.mSyncKey);
+            }
+        }
         // Let the caller know that there's more to do
         return moreAvailable;
     }
diff --git a/src/com/android/exchange/adapter/ContactsSyncAdapter.java b/src/com/android/exchange/adapter/ContactsSyncAdapter.java
index 7cd2882..6930b88 100644
--- a/src/com/android/exchange/adapter/ContactsSyncAdapter.java
+++ b/src/com/android/exchange/adapter/ContactsSyncAdapter.java
@@ -760,16 +760,20 @@
         }
 
         public void execute() {
-            try {
-                mService.userLog("Executing " + size() + " CPO's");
-                mResults = mService.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);
+            synchronized (mService.getSynchronizer()) {
+                if (!mService.isStopped()) {
+                    try {
+                        mService.userLog("Executing " + size() + " CPO's");
+                        mResults = mService.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);
+                    }
+                }
             }
         }
 
diff --git a/src/com/android/exchange/adapter/EmailSyncAdapter.java b/src/com/android/exchange/adapter/EmailSyncAdapter.java
index 9d07066..399bb8d 100644
--- a/src/com/android/exchange/adapter/EmailSyncAdapter.java
+++ b/src/com/android/exchange/adapter/EmailSyncAdapter.java
@@ -437,13 +437,17 @@
 
             addCleanupOps(ops);
 
-            try {
-                mService.mContext.getContentResolver()
-                        .applyBatch(EmailProvider.EMAIL_AUTHORITY, ops);
-            } catch (RemoteException e) {
-                // There is nothing to be done here; fail by returning null
-            } catch (OperationApplicationException e) {
-                // There is nothing to be done here; fail by returning null
+            // No commits if we're stopped
+            synchronized (mService.getSynchronizer()) {
+                if (mService.isStopped()) return;
+                try {
+                    mService.mContext.getContentResolver()
+                    .applyBatch(EmailProvider.EMAIL_AUTHORITY, ops);
+                } catch (RemoteException e) {
+                    // There is nothing to be done here; fail by returning null
+                } catch (OperationApplicationException e) {
+                    // There is nothing to be done here; fail by returning null
+                }
             }
 
             mService.userLog(mMailbox.mDisplayName + " SyncKey saved as: " + mMailbox.mSyncKey);
diff --git a/src/com/android/exchange/adapter/FolderSyncParser.java b/src/com/android/exchange/adapter/FolderSyncParser.java
index 72fbe56..89d24c6 100644
--- a/src/com/android/exchange/adapter/FolderSyncParser.java
+++ b/src/com/android/exchange/adapter/FolderSyncParser.java
@@ -137,9 +137,15 @@
             } else
                 skipTag();
         }
-        ContentValues cv = new ContentValues();
-        cv.put(AccountColumns.SYNC_KEY, mAccount.mSyncKey);
-        mAccount.update(mContext, cv);
+        synchronized (mService.getSynchronizer()) {
+            if (!mService.isStopped()) {
+                ContentValues cv = new ContentValues();
+                cv.put(AccountColumns.SYNC_KEY, mAccount.mSyncKey);
+                mAccount.update(mContext, cv);
+                mService.userLog("Leaving FolderSyncParser with Account syncKey="
+                        + mAccount.mSyncKey);
+            }
+        }
         return res;
     }
 
@@ -281,47 +287,50 @@
         }
 
         // Create the new mailboxes in a single batch operation
-        if (!ops.isEmpty()) {
-            mService.userLog("Applying " + ops.size() + " mailbox operations.");
+        // Don't save any data if the service has been stopped
+        synchronized (mService.getSynchronizer()) {
+            if (!ops.isEmpty() && !mService.isStopped()) {
+                mService.userLog("Applying " + ops.size() + " mailbox operations.");
 
-            // Then, we create an update for the account (most importantly, updating the syncKey)
-            ops.add(ContentProviderOperation.newUpdate(
-                    ContentUris.withAppendedId(Account.CONTENT_URI, mAccountId)).withValues(
-                    mAccount.toContentValues()).build());
+                // Then, we create an update for the account (most importantly, updating the syncKey)
+                ops.add(ContentProviderOperation.newUpdate(
+                        ContentUris.withAppendedId(Account.CONTENT_URI, mAccountId)).withValues(
+                                mAccount.toContentValues()).build());
 
-            // Finally, we execute the batch
-            try {
-                mService.mContext.getContentResolver()
-                        .applyBatch(EmailProvider.EMAIL_AUTHORITY, ops);
-                mService.userLog("New Account SyncKey: " + mAccount.mSyncKey);
-            } catch (RemoteException e) {
-                // There is nothing to be done here; fail by returning null
-            } catch (OperationApplicationException e) {
-                // There is nothing to be done here; fail by returning null
-            }
-
-            // Look for sync issues and its children and delete them
-            // I'm not aware of any other way to deal with this properly
-            mBindArguments[0] = "Sync Issues";
-            mBindArguments[1] = mAccountIdAsString;
-            Cursor c = mContentResolver.query(Mailbox.CONTENT_URI, MAILBOX_ID_COLUMNS_PROJECTION,
-                    WHERE_DISPLAY_NAME_AND_ACCOUNT, mBindArguments, null);
-            String parentServerId = null;
-            long id = 0;
-            try {
-                if (c.moveToFirst()) {
-                    id = c.getLong(0);
-                    parentServerId = c.getString(1);
+                // Finally, we execute the batch
+                try {
+                    mService.mContext.getContentResolver()
+                    .applyBatch(EmailProvider.EMAIL_AUTHORITY, ops);
+                    mService.userLog("New Account SyncKey: " + mAccount.mSyncKey);
+                } catch (RemoteException e) {
+                    // There is nothing to be done here; fail by returning null
+                } catch (OperationApplicationException e) {
+                    // There is nothing to be done here; fail by returning null
                 }
-            } finally {
-                c.close();
-            }
-            if (parentServerId != null) {
-                mContentResolver.delete(ContentUris.withAppendedId(Mailbox.CONTENT_URI, id),
-                        null, null);
-                mBindArguments[0] = parentServerId;
-                mContentResolver.delete(Mailbox.CONTENT_URI, WHERE_PARENT_SERVER_ID_AND_ACCOUNT,
-                        mBindArguments);
+
+                // Look for sync issues and its children and delete them
+                // I'm not aware of any other way to deal with this properly
+                mBindArguments[0] = "Sync Issues";
+                mBindArguments[1] = mAccountIdAsString;
+                Cursor c = mContentResolver.query(Mailbox.CONTENT_URI, MAILBOX_ID_COLUMNS_PROJECTION,
+                        WHERE_DISPLAY_NAME_AND_ACCOUNT, mBindArguments, null);
+                String parentServerId = null;
+                long id = 0;
+                try {
+                    if (c.moveToFirst()) {
+                        id = c.getLong(0);
+                        parentServerId = c.getString(1);
+                    }
+                } finally {
+                    c.close();
+                }
+                if (parentServerId != null) {
+                    mContentResolver.delete(ContentUris.withAppendedId(Mailbox.CONTENT_URI, id),
+                            null, null);
+                    mBindArguments[0] = parentServerId;
+                    mContentResolver.delete(Mailbox.CONTENT_URI, WHERE_PARENT_SERVER_ID_AND_ACCOUNT,
+                            mBindArguments);
+                }
             }
         }
     }