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
This commit is contained in:
Marc Blank 2010-03-06 16:51:39 -08:00
parent 850e9fdda9
commit b00a660c0a

View File

@ -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<String, Object> 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<ContentProviderOperation> {
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)