Merge "Update file type acceptance rules" into honeycomb
diff --git a/src/com/android/exchange/adapter/FolderSyncParser.java b/src/com/android/exchange/adapter/FolderSyncParser.java
index 6c298c9..0d36a0e 100644
--- a/src/com/android/exchange/adapter/FolderSyncParser.java
+++ b/src/com/android/exchange/adapter/FolderSyncParser.java
@@ -20,11 +20,11 @@
 import com.android.email.Utility;
 import com.android.email.provider.AttachmentProvider;
 import com.android.email.provider.EmailContent;
+import com.android.email.provider.EmailProvider;
 import com.android.email.provider.EmailContent.Account;
 import com.android.email.provider.EmailContent.AccountColumns;
 import com.android.email.provider.EmailContent.Mailbox;
 import com.android.email.provider.EmailContent.MailboxColumns;
-import com.android.email.provider.EmailProvider;
 import com.android.exchange.Eas;
 import com.android.exchange.ExchangeService;
 import com.android.exchange.MockParserStream;
@@ -67,8 +67,8 @@
     public static final int JOURNAL_TYPE = 11;
     public static final int USER_MAILBOX_TYPE = 12;
 
-    // Maximum user mailboxes we will handle (to prevent binder overload when sending to provider)
-    public final static int MAX_USER_MAILBOXES = 1000;
+    // Chunk size for our mailbox commits
+    public final static int MAILBOX_COMMIT_SIZE = 20;
 
     // EAS types that we are willing to consider valid folders for EAS sync
     public static final List<Integer> VALID_EAS_FOLDER_TYPES = Arrays.asList(INBOX_TYPE,
@@ -111,6 +111,14 @@
         int status;
         boolean res = false;
         boolean resetFolders = false;
+        // Since we're now (potentially) committing mailboxes in chunks, ensure that we start with
+        // only the account mailbox
+        String key = mAccount.mSyncKey;
+        boolean initialSync = (key == null) || "0".equals(key);
+        if (initialSync) {
+            mContentResolver.delete(Mailbox.CONTENT_URI, ALL_BUT_ACCOUNT_MAILBOX,
+                    new String[] {Long.toString(mAccountId)});
+        }
         if (nextTag(START_DOCUMENT) != Tags.FOLDER_FOLDER_SYNC)
             throw new EasParserException();
         while (nextTag(START_DOCUMENT) != END_DOCUMENT) {
@@ -138,7 +146,7 @@
                 mAccount.mSyncKey = getValue();
                 userLog("New Account SyncKey: ", mAccount.mSyncKey);
             } else if (tag == Tags.FOLDER_CHANGES) {
-                changesParser(mOperations);
+                changesParser(mOperations, initialSync);
             } else
                 skipTag();
         }
@@ -353,7 +361,42 @@
         }
     }
 
-    public void changesParser(ArrayList<ContentProviderOperation> ops) throws IOException {
+    private void commitMailboxes(ArrayList<Mailbox> validMailboxes,
+            ArrayList<Mailbox> userMailboxes, HashMap<String, Mailbox> mailboxMap,
+            ArrayList<ContentProviderOperation> ops) throws IOException {
+
+        // Go through the generic user mailboxes; we'll call them valid if any parent is valid
+        for (Mailbox m: userMailboxes) {
+            if (isValidMailFolder(m, mailboxMap)) {
+                m.mType = Mailbox.TYPE_MAIL;
+                validMailboxes.add(m);
+            } else {
+                userLog("Rejecting unknown type mailbox: " + m.mDisplayName);
+            }
+        }
+
+        // Add operations for all valid mailboxes
+        for (Mailbox m: validMailboxes) {
+            userLog("Adding mailbox: ", m.mDisplayName);
+            ops.add(ContentProviderOperation
+                    .newInsert(Mailbox.CONTENT_URI).withValues(m.toContentValues()).build());
+        }
+
+        // Commit the mailboxes
+        userLog("Applying ", mOperations.size(), " mailbox operations.");
+        // Execute the batch; throw IOExceptions if this fails, hoping the issue isn't repeatable
+        // If it IS repeatable, there's no good result, since the folder list will be invalid
+        try {
+            mContentResolver.applyBatch(EmailProvider.EMAIL_AUTHORITY, mOperations);
+        } catch (RemoteException e) {
+            throw new IOException("RemoteException committing folders.");
+        } catch (OperationApplicationException e) {
+            throw new IOException("OperationApplicationException committing folders.");
+        }
+    }
+
+    public void changesParser(ArrayList<ContentProviderOperation> ops, boolean initialSync)
+            throws IOException {
         // Mailboxes that we known contain email
         ArrayList<Mailbox> validMailboxes = new ArrayList<Mailbox>();
         // Mailboxes that we're unsure about
@@ -361,7 +404,7 @@
         // Maps folder serverId to mailbox type
         HashMap<String, Mailbox> mailboxMap = new HashMap<String, Mailbox>();
 
-        int userMailboxCount = 0;
+        int mailboxAddCount = 0;
         while (nextTag(Tags.FOLDER_CHANGES) != END) {
             if (tag == Tags.FOLDER_ADD) {
                 Mailbox mailbox = addParser();
@@ -370,14 +413,19 @@
                     mailboxMap.put(mailbox.mServerId, mailbox);
                     // And add the mailbox to the proper list
                     if (type == USER_MAILBOX_TYPE) {
-                        // Limit user mailboxes to 1000
-                        if (++userMailboxCount < MAX_USER_MAILBOXES) {
-                            userMailboxes.add(mailbox);
-                        }
-                        // TODO: Consider ways to avoid having to truncate the list of mailboxes
+                        userMailboxes.add(mailbox);
                     } else {
                         validMailboxes.add(mailbox);
                     }
+                    // On initial sync, we commit what we have every 20 mailboxes
+                    if (initialSync && (++mailboxAddCount == MAILBOX_COMMIT_SIZE)) {
+                        commitMailboxes(validMailboxes, userMailboxes, mailboxMap, ops);
+                        // Clear our arrays to prepare for more
+                        userMailboxes.clear();
+                        validMailboxes.clear();
+                        ops.clear();
+                        mailboxAddCount = 0;
+                    }
                 }
             } else if (tag == Tags.FOLDER_DELETE) {
                 deleteParser(ops);
@@ -397,21 +445,14 @@
             return;
         }
 
-        // Go through the generic user mailboxes; we'll call them valid if any parent is valid
-        for (Mailbox m: userMailboxes) {
-            if (isValidMailFolder(m, mailboxMap)) {
-                m.mType = Mailbox.TYPE_MAIL;
-                validMailboxes.add(m);
-            } else {
-                userLog("Rejecting unknown type mailbox: " + m.mDisplayName);
-            }
-        }
-
-        for (Mailbox m: validMailboxes) {
-            userLog("Adding mailbox: ", m.mDisplayName);
-            ops.add(ContentProviderOperation
-                    .newInsert(Mailbox.CONTENT_URI).withValues(m.toContentValues()).build());
-        }
+        // Commit the sync key and mailboxes
+        ContentValues cv = new ContentValues();
+        cv.put(AccountColumns.SYNC_KEY, mAccount.mSyncKey);
+        ops.add(ContentProviderOperation.newUpdate(
+                ContentUris.withAppendedId(Account.CONTENT_URI, mAccount.mId))
+                .withValues(cv)
+                .build());
+        commitMailboxes(validMailboxes, userMailboxes, mailboxMap, ops);
     }
 
     /**
@@ -422,30 +463,10 @@
     }
 
     /**
-     * Commit all changes from this sync (sync key, adds, updates, and deletes)
+     * Clean up after sync
      */
     @Override
     public void commit() throws IOException {
-        // Commit the sync key
-        ContentValues cv = new ContentValues();
-        cv.put(AccountColumns.SYNC_KEY, mAccount.mSyncKey);
-        mOperations.add(ContentProviderOperation.newUpdate(
-                ContentUris.withAppendedId(Account.CONTENT_URI, mAccount.mId))
-                .withValues(cv)
-                .build());
-
-        userLog("Applying ", mOperations.size(), " mailbox operations.");
-
-        // Execute the batch
-        try {
-            mContentResolver.applyBatch(EmailProvider.EMAIL_AUTHORITY, mOperations);
-            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";