Only iterate each parent once

Also tidy some warnings.

b/10893667

Change-Id: I207cf2b4c26bd3b8c695971bcde6254236a54c57
diff --git a/src/com/android/exchange/adapter/FolderSyncParser.java b/src/com/android/exchange/adapter/FolderSyncParser.java
index 159ef61..46a9c6d 100644
--- a/src/com/android/exchange/adapter/FolderSyncParser.java
+++ b/src/com/android/exchange/adapter/FolderSyncParser.java
@@ -25,6 +25,7 @@
 import android.content.OperationApplicationException;
 import android.database.Cursor;
 import android.os.RemoteException;
+import android.os.TransactionTooLargeException;
 import android.text.TextUtils;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
@@ -39,7 +40,6 @@
 import com.android.exchange.CommandStatusException;
 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;
@@ -49,6 +49,8 @@
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Set;
 
 /**
  * Parse the result of a FolderSync command
@@ -124,18 +126,14 @@
 
     /** Projection used for changed parents during parent/child fixup. */
     private static final String[] FIXUP_PARENT_PROJECTION =
-            { MailboxColumns.ID, MailboxColumns.DISPLAY_NAME, MailboxColumns.HIERARCHICAL_NAME,
-                    MailboxColumns.FLAGS };
+            { MailboxColumns.ID, MailboxColumns.FLAGS };
     private static final int FIXUP_PARENT_ID_COLUMN = 0;
-    private static final int FIXUP_PARENT_DISPLAY_NAME_COLUMN = 1;
-    private static final int FIXUP_PARENT_HIERARCHICAL_NAME_COLUMN = 2;
-    private static final int FIXUP_PARENT_FLAGS_COLUMN = 3;
+    private static final int FIXUP_PARENT_FLAGS_COLUMN = 1;
 
     /** Projection used for changed children during parent/child fixup. */
     private static final String[] FIXUP_CHILD_PROJECTION =
-            { MailboxColumns.ID, MailboxColumns.DISPLAY_NAME };
+            { MailboxColumns.ID };
     private static final int FIXUP_CHILD_ID_COLUMN = 0;
-    private static final int FIXUP_CHILD_DISPLAY_NAME_COLUMN = 1;
 
     /** Flags that are set or cleared when a mailbox's child status changes. */
     private static final int HAS_CHILDREN_FLAGS =
@@ -148,8 +146,6 @@
     long mAccountId;
     @VisibleForTesting
     String mAccountIdAsString;
-    @VisibleForTesting
-    boolean mInUnitTest = false;
 
     private final String[] mBindArguments = new String[2];
 
@@ -159,7 +155,7 @@
     /** Indicates whether this sync is an initial FolderSync. */
     private boolean mInitialSync;
     /** List of folder server ids whose children changed with this sync. */
-    private final ArrayList<String> mParentFixupsNeeded = new ArrayList<String>();
+    private final Set<String> mParentFixupsNeeded = new LinkedHashSet<String>();
     /** Indicates whether the sync response provided a different sync key than we had. */
     private boolean mSyncKeyChanged = false;
 
@@ -173,7 +169,7 @@
 
     private static final ContentValues UNINITIALIZED_PARENT_KEY = new ContentValues();
 
-    {
+    static {
         UNINITIALIZED_PARENT_KEY.put(MailboxColumns.PARENT_KEY, Mailbox.PARENT_KEY_UNINITIALIZED);
     }
 
@@ -572,16 +568,36 @@
         if (mOperations.isEmpty()) {
             return;
         }
-        // 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(EmailContent.AUTHORITY, mOperations);
-        } catch (final RemoteException e) {
-            LogUtils.e(TAG, "RemoteException in commit");
-            throw new IOException("RemoteException in commit");
-        } catch (final OperationApplicationException e) {
-            LogUtils.e(TAG, "OperationApplicationException in commit");
-            throw new IOException("OperationApplicationException in commit");
+        int transactionSize = mOperations.size();
+        final ArrayList<ContentProviderOperation> subOps =
+                new ArrayList<ContentProviderOperation>(transactionSize);
+        while (!mOperations.isEmpty()) {
+            subOps.clear();
+            subOps.addAll(mOperations.subList(0, transactionSize));
+            // Try to apply the ops. If the transaction is too large, split it in half and try again
+            // If some other error happens then throw an IOException up the stack.
+            try {
+                mContentResolver.applyBatch(EmailContent.AUTHORITY, subOps);
+                mOperations.removeAll(subOps);
+            } catch (final TransactionTooLargeException e) {
+                // If the transaction is too large, try splitting it.
+                if (transactionSize == 1) {
+                    LogUtils.e(TAG, "Single operation transaction too large");
+                    throw new IOException("Single operation transaction too large");
+                }
+                LogUtils.d(TAG, "Transaction operation count %d too large, halving...",
+                        transactionSize);
+                transactionSize = transactionSize / 2;
+                if (transactionSize < 1) {
+                    transactionSize = 1;
+                }
+            } catch (final RemoteException e) {
+                LogUtils.e(TAG, "RemoteException in commit");
+                throw new IOException("RemoteException in commit");
+            } catch (final OperationApplicationException e) {
+                LogUtils.e(TAG, "OperationApplicationException in commit");
+                throw new IOException("OperationApplicationException in commit");
+            }
         }
         mOperations.clear();
     }
@@ -606,7 +622,7 @@
         // and just reset the values inside the loop as necessary.
         final String[] bindArguments = new String[2];
         bindArguments[1] = mAccountIdAsString;
-        final ContentValues cv = new ContentValues(2);
+        final ContentValues cv = new ContentValues(1);
 
         for (final String parentServerId : mParentFixupsNeeded) {
             // Get info about this parent.
@@ -618,19 +634,10 @@
                 continue;
             }
             final long parentId;
-            final String parentHierarchicalName;
             final int parentFlags;
             try {
                 if (parentCursor.moveToFirst()) {
                     parentId = parentCursor.getLong(FIXUP_PARENT_ID_COLUMN);
-                    final String hierarchicalName = parentCursor.getString(
-                            FIXUP_PARENT_HIERARCHICAL_NAME_COLUMN);
-                    if (hierarchicalName != null) {
-                        parentHierarchicalName = hierarchicalName;
-                    } else {
-                        parentHierarchicalName = parentCursor.getString(
-                                FIXUP_PARENT_DISPLAY_NAME_COLUMN);
-                    }
                     parentFlags = parentCursor.getInt(FIXUP_PARENT_FLAGS_COLUMN);
                 } else {
                     // TODO: Error handling.
@@ -653,10 +660,6 @@
                     cv.put(MailboxColumns.PARENT_KEY, parentId);
                     while (childCursor.moveToNext()) {
                         final long childId = childCursor.getLong(FIXUP_CHILD_ID_COLUMN);
-                        final String childName =
-                                childCursor.getString(FIXUP_CHILD_DISPLAY_NAME_COLUMN);
-                        cv.put(MailboxColumns.HIERARCHICAL_NAME,
-                                parentHierarchicalName + "/" + childName);
                         mOperations.add(ContentProviderOperation.newUpdate(
                                 ContentUris.withAppendedId(Mailbox.CONTENT_URI, childId)).
                                 withValues(cv).build());
@@ -681,9 +684,8 @@
                 mOperations.add(ContentProviderOperation.newUpdate(ContentUris.withAppendedId(
                         Mailbox.CONTENT_URI, parentId)).withValues(cv).build());
             }
+            flushOperations();
         }
-
-        flushOperations();
     }
 
     @Override
diff --git a/tests/src/com/android/exchange/adapter/FolderSyncParserTests.java b/tests/src/com/android/exchange/adapter/FolderSyncParserTests.java
index 3f11496..202b8f8 100644
--- a/tests/src/com/android/exchange/adapter/FolderSyncParserTests.java
+++ b/tests/src/com/android/exchange/adapter/FolderSyncParserTests.java
@@ -165,7 +165,6 @@
             if (is != null) {
                 mReader = new BufferedReader(new InputStreamReader(is));
             }
-            mInUnitTest = true;
         }
 
         private void initTagMap() {