Make sure values are set correctly when inserting event.
For a recurrence, dtend must be null. This change enforces that,
as well as some other conditions on the value.
Includes unittest.
bug 2513213
Change-Id: I04a1b7bd2f91e579177dd80741b4487409e903fc
diff --git a/src/com/android/providers/calendar/CalendarProvider2.java b/src/com/android/providers/calendar/CalendarProvider2.java
index 1209ed9..ced68df 100644
--- a/src/com/android/providers/calendar/CalendarProvider2.java
+++ b/src/com/android/providers/calendar/CalendarProvider2.java
@@ -978,13 +978,13 @@
"originalInstanceTime >= ?)) AND (sync_events != 0)");
String selectionArgs[] = new String[] {endString, beginString, endString,
String.valueOf(begin - MAX_ASSUMED_DURATION)};
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Retrieving events to expand: " + qb.toString());
- }
-
- return qb.query(mDb, EXPAND_COLUMNS, null /* selection */,
+ Cursor c = qb.query(mDb, EXPAND_COLUMNS, null /* selection */,
selectionArgs, null /* groupBy */,
null /* having */, null /* sortOrder */);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Instance expansion: got " + c.getCount() + " entries");
+ }
+ return c;
}
/**
@@ -1538,10 +1538,11 @@
if (!values.containsKey(Events.DTSTART)) {
throw new RuntimeException("DTSTART field missing from event");
}
- // TODO: avoid the call to updateBundleFromEvent if this is just finding local
- // changes.
// TODO: do we really need to make a copy?
- ContentValues updatedValues = updateContentValuesFromEvent(values);
+ ContentValues updatedValues = new ContentValues(values);
+ validateEventData(updatedValues);
+ // updateLastDate must be after validation, to ensure proper last date computation
+ updatedValues = updateLastDate(updatedValues);
if (updatedValues == null) {
throw new RuntimeException("Could not insert event.");
// return null;
@@ -1562,7 +1563,6 @@
Log.w(TAG, "insertInTransaction: " +
"allDay is true but sec, min, hour were not 0.");
}
-
id = mDbHelper.eventsInsert(updatedValues);
if (id != -1) {
updateEventRawTimesLocked(id, updatedValues);
@@ -1660,6 +1660,71 @@
return ContentUris.withAppendedId(uri, id);
}
+ /**
+ * Do some validation on event data before inserting.
+ * In particular make sure dtend, duration, etc make sense for
+ * the type of event (regular, recurrence, exception). Remove
+ * any unexpected fields.
+ *
+ * @param values the ContentValues to insert
+ */
+ private void validateEventData(ContentValues values) {
+ boolean hasDtend = values.getAsLong(Events.DTEND) != null;
+ boolean hasDuration = !TextUtils.isEmpty(values.getAsString(Events.DURATION));
+ boolean hasRrule = !TextUtils.isEmpty(values.getAsString(Events.RRULE));
+ boolean hasRdate = !TextUtils.isEmpty(values.getAsString(Events.RDATE));
+ boolean hasOriginalEvent = !TextUtils.isEmpty(values.getAsString(Events.ORIGINAL_EVENT));
+ boolean hasOriginalInstanceTime = values.getAsLong(Events.ORIGINAL_INSTANCE_TIME) != null;
+ if (hasRrule || hasRdate) {
+ // Recurrence:
+ // dtstart is start time of first event
+ // dtend is null
+ // duration is the duration of the event
+ // rrule is the recurrence rule
+ // lastDate is the end of the last event or null if it repeats forever
+ // originalEvent is null
+ // originalInstanceTime is null
+ if (hasDtend || !hasDuration || hasOriginalEvent || hasOriginalInstanceTime) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.e(TAG, "Invalid values for recurrence: " + values);
+ }
+ values.remove(Events.DTEND);
+ values.remove(Events.ORIGINAL_EVENT);
+ values.remove(Events.ORIGINAL_INSTANCE_TIME);
+ }
+ } else if (hasOriginalEvent || hasOriginalInstanceTime) {
+ // Recurrence exception
+ // dtstart is start time of exception event
+ // dtend is end time of exception event
+ // duration is null
+ // rrule is null
+ // lastdate is same as dtend
+ // originalEvent is the _sync_id of the recurrence
+ // originalInstanceTime is the start time of the event being replaced
+ if (!hasDtend || hasDuration || !hasOriginalEvent || !hasOriginalInstanceTime) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.e(TAG, "Invalid values for recurrence exception: " + values);
+ }
+ values.remove(Events.DURATION);
+ }
+ } else {
+ // Regular event
+ // dtstart is the start time
+ // dtend is the end time
+ // duration is null
+ // rrule is null
+ // lastDate is the same as dtend
+ // originalEvent is null
+ // originalInstanceTime is null
+ if (!hasDtend || hasDuration) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.e(TAG, "Invalid values for event: " + values);
+ }
+ values.remove(Events.DURATION);
+ }
+ }
+ }
+
private void setEventDirty(int eventId) {
mDb.execSQL("UPDATE Events SET _sync_dirty=1 where _id=?", new Integer[] {eventId});
}
@@ -2075,10 +2140,13 @@
return lastMillis;
}
- private ContentValues updateContentValuesFromEvent(ContentValues initialValues) {
+ /**
+ * Add LAST_DATE to values.
+ * @param values the ContentValues (in/out)
+ * @return values on success, null on failure
+ */
+ private ContentValues updateLastDate(ContentValues values) {
try {
- ContentValues values = new ContentValues(initialValues);
-
long last = calculateLastDate(values);
if (last != -1) {
values.put(Events.LAST_DATE, last);
@@ -2541,8 +2609,9 @@
+ Events.HTML_URI
+ " in Events table is not allowed.");
}
-
- ContentValues updatedValues = updateContentValuesFromEvent(values);
+ ContentValues updatedValues = new ContentValues(values);
+ // TODO: should extend validateEventData to work with updates and call it here
+ updatedValues = updateLastDate(updatedValues);
if (updatedValues == null) {
Log.w(TAG, "Could not update event.");
return 0;
diff --git a/tests/src/com/android/providers/calendar/CalendarProvider2Test.java b/tests/src/com/android/providers/calendar/CalendarProvider2Test.java
index 683f718..59e1285 100644
--- a/tests/src/com/android/providers/calendar/CalendarProvider2Test.java
+++ b/tests/src/com/android/providers/calendar/CalendarProvider2Test.java
@@ -53,6 +53,7 @@
private int mCalendarId;
protected boolean mWipe = false;
+ protected boolean mForceDtend = false;
// We need a unique id to put in the _sync_id field so that we can create
// recurrence exceptions that refer to recurring events.
@@ -843,6 +844,7 @@
mDb = helper.getWritableDatabase();
wipeData(mDb);
mMetaData = getProvider().mMetaData;
+ mForceDtend = false;
}
@@ -921,10 +923,11 @@
m.put(Events.DTSTART, event.mDtstart);
m.put(Events.ALL_DAY, event.mAllDay ? 1 : 0);
- if (event.mRrule == null) {
+ if (event.mRrule == null || mForceDtend) {
// This is a normal event
m.put(Events.DTEND, event.mDtend);
- } else {
+ }
+ if (event.mRrule != null) {
// This is a repeating event
m.put(Events.RRULE, event.mRrule);
m.put(Events.DURATION, event.mDuration);
@@ -1143,6 +1146,12 @@
cursor.close();
}
+ // Force a dtend value to be set and make sure instance expansion still works
+ public void testInstanceRangeDtend() throws Exception {
+ mForceDtend = true;
+ testInstanceRange();
+ }
+
public void testInstanceRange() throws Exception {
Cursor cursor;
Uri url = null;