From b00a660c0af7b0385c409d0b33bd777e7c83c07e Mon Sep 17 00:00:00 2001 From: Marc Blank Date: Sat, 6 Mar 2010 16:51:39 -0800 Subject: [PATCH] Partially fix #2455341 (Exception adding event w/o DTSTART) * Apparently, the Exchange server can send event changes that have incomplete data. This is not documented, and the result is very bad in that an exception is thrown by CP2, and the sync (and future syncs) is prevented from finishing * Wrote some defensive code to detect bad events and simply not add them * Open a new bug to track trying to determine why the Exchange server is sending this kind of data. Bug: 2455341 Change-Id: Ibcfd412382164351c96f368dc043f38c7b481154 --- .../exchange/adapter/CalendarSyncAdapter.java | 55 +++++++++++++++++-- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/src/com/android/exchange/adapter/CalendarSyncAdapter.java b/src/com/android/exchange/adapter/CalendarSyncAdapter.java index 663e77987..91396adf1 100644 --- a/src/com/android/exchange/adapter/CalendarSyncAdapter.java +++ b/src/com/android/exchange/adapter/CalendarSyncAdapter.java @@ -60,6 +60,7 @@ import java.util.GregorianCalendar; import java.util.StringTokenizer; import java.util.TimeZone; import java.util.UUID; +import java.util.Map.Entry; /** * Sync adapter class for EAS calendars @@ -240,6 +241,7 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter { String organizerName = null; String organizerEmail = null; int eventOffset = -1; + int deleteOffset = -1; boolean firstTag = true; long eventId = -1; @@ -268,11 +270,15 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter { } else { // Otherwise, delete the original event and recreate it userLog("Changing (delete/add) event ", serverId); - ops.delete(id); + deleteOffset = ops.newDelete(id); // Add a placeholder event so that associated tables can reference // this as a back reference. We add the event at the end of the method eventOffset = ops.newEvent(PLACEHOLDER_OPERATION); } + } else { + // The changed item isn't found. We'll treat this as a new item + eventOffset = ops.newEvent(PLACEHOLDER_OPERATION); + userLog(TAG, "Changed item not found; treating as new."); } } else if (firstTag) { // Add a placeholder event so that associated tables can reference @@ -405,11 +411,46 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter { // Put the real event in the proper place in the ops ArrayList if (eventOffset >= 0) { - ops.set(eventOffset, ContentProviderOperation - .newInsert(sEventsUri).withValues(cv).build()); + if (isValidEventValues(cv, update)) { + ops.set(eventOffset, ContentProviderOperation + .newInsert(sEventsUri).withValues(cv).build()); + } else { + // If we can't add this event (it's invalid), remove all of the inserts + // we've built for it + int cnt = ops.mCount - eventOffset; + userLog(TAG, "Removing " + cnt + " inserts from mOps"); + for (int i = 0; i < cnt; i++) { + ops.remove(eventOffset); + } + ops.mCount = eventOffset; + // If this is a change, we need to also remove the deletion that comes + // before the addition + if (deleteOffset >= 0) { + ops.remove(deleteOffset); + userLog(TAG, "Removing deletion from mOps"); + ops.mCount = deleteOffset; + } + } } } + private boolean isValidEventValues(ContentValues cv, boolean update) { + // Do a sanity check on this set of values + // At the very least, we must get DTSTART and _SYNC_DATA (uid) + // If it's invalid, log the columns we've got (will help debugging) + if (!cv.containsKey(Events.DTSTART) || !cv.containsKey(Events._SYNC_DATA)) { + userLog(TAG, (update ? "Changed" : "New") + " event invalid; skipping"); + StringBuilder sb = new StringBuilder("Columns: "); + for (Entry entry: cv.valueSet()) { + sb.append(entry.getKey()); + sb.append(' '); + } + userLog(TAG, sb.toString()); + return false; + } + return true; + } + private String recurrenceParser(CalendarOperations ops) throws IOException { // Turn this information into an RRULE int type = -1; @@ -895,7 +936,7 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter { private class CalendarOperations extends ArrayList { private static final long serialVersionUID = 1L; - private int mCount = 0; + public int mCount = 0; private ContentProviderResult[] mResults = null; private int mEventStart = 0; @@ -912,6 +953,12 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter { return mEventStart; } + public int newDelete(long id) { + int offset = mCount; + delete(id); + return offset; + } + public void newAttendee(ContentValues cv) { add(ContentProviderOperation .newInsert(sAttendeesUri)