Fix longstanding EAS battery burner

* This happens when the plain text of an Exchange 2003 message
  is more than about 500k characters; what's happening is that
  a TransactionTooLarge exception is being thrown in the binder
* The reason we never figured it out previously is that the
  exception isn't listed in emaillog.txt (which users can easily
  send); bugreports/logcat output is harder...
* The fix is to progressively truncate the text

Bug: 5789201
Change-Id: I7524070721e7c25b490200293cb8efb8dfd99d05
diff --git a/src/com/android/exchange/adapter/EmailSyncAdapter.java b/src/com/android/exchange/adapter/EmailSyncAdapter.java
index 4181723..33c517f 100644
--- a/src/com/android/exchange/adapter/EmailSyncAdapter.java
+++ b/src/com/android/exchange/adapter/EmailSyncAdapter.java
@@ -25,6 +25,7 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.RemoteException;
+import android.os.TransactionTooLargeException;
 import android.provider.CalendarContract.Events;
 import android.text.Html;
 import android.text.SpannedString;
@@ -1111,9 +1112,23 @@
 
         @Override
         public void commit() {
+            commitImpl(0);
+        }
+
+        public void commitImpl(int tryCount) {
             // Use a batch operation to handle the changes
             ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
 
+            // Maximum size of message text per fetch
+            int numFetched = fetchedEmails.size();
+            int maxPerFetch = 0;
+            if (numFetched > 0 && tryCount > 0) {
+                // Educated guess that 450000 chars (900k) is ok; 600k is a killer
+                // Remember that when fetching, we're not getting any other data
+                // We'll keep trying, reducing the maximum each time
+                // Realistically, this will rarely exceed 1, and probably never 2
+                maxPerFetch = 450000 / numFetched / tryCount;
+            }
             for (Message msg: fetchedEmails) {
                 // Find the original message's id (by serverId and mailbox)
                 Cursor c = getServerIdCursor(msg.mServerId, EmailContent.ID_PROJECTION);
@@ -1138,6 +1153,10 @@
                 if (id != null) {
                     userLog("Fetched body successfully for ", id);
                     mBindArgument[0] = id;
+                    if ((maxPerFetch > 0) && (msg.mText.length() > maxPerFetch)) {
+                        userLog("Truncating message to " + maxPerFetch);
+                        msg.mText = msg.mText.substring(0, maxPerFetch) + "...";
+                    }
                     ops.add(ContentProviderOperation.newUpdate(Body.CONTENT_URI)
                             .withSelection(Body.MESSAGE_KEY + "=?", mBindArgument)
                             .withValue(Body.TEXT_CONTENT, msg.mText)
@@ -1192,7 +1211,10 @@
                 try {
                     mContentResolver.applyBatch(EmailContent.AUTHORITY, ops);
                     userLog(mMailbox.mDisplayName, " SyncKey saved as: ", mMailbox.mSyncKey);
-                } catch (RemoteException e) {
+                } catch (TransactionTooLargeException e) {
+                    Log.w(TAG, "Transaction failed on fetched message; retrying...");
+                    commitImpl(++tryCount);
+                 } 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