summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTony Mak <tonymak@google.com>2015-06-08 10:50:34 +0100
committerTony Mak <tonymak@google.com>2015-06-08 10:50:34 +0100
commit503a798e5f76ecce75607277292bd9a326ba79ec (patch)
treee4cb5d294d8156cacf4bb09a4475379098d23955
parentea28dfc327c87b24855f7abd9a48ba9a1b3f43f5 (diff)
downloadandroid_packages_providers_CalendarProvider-503a798e5f76ecce75607277292bd9a326ba79ec.tar.gz
android_packages_providers_CalendarProvider-503a798e5f76ecce75607277292bd9a326ba79ec.tar.bz2
android_packages_providers_CalendarProvider-503a798e5f76ecce75607277292bd9a326ba79ec.zip
Fix crash when inserting reminder/attendee/extended property to a non-existent event
The problem is when inserting reminder/attendee/extended property, the code assumes the event it belongs to exists. However, it may not be true in some edge cases. For example, user is on the event edit page, while the event is being deleted in server at this moment. Add checking to confirm the event does exists before having the insert logic. As all logics in insert is run within a single transaction, we can be assured that the event does exist throughout the transaction. Bug: b2/13287671 Change-Id: I655720c67ab3176d52759c43032682874aa3eb8b
-rw-r--r--src/com/android/providers/calendar/CalendarDatabaseHelper.java19
-rw-r--r--src/com/android/providers/calendar/CalendarProvider2.java51
-rw-r--r--tests/src/com/android/providers/calendar/CalendarProvider2Test.java44
3 files changed, 88 insertions, 26 deletions
diff --git a/src/com/android/providers/calendar/CalendarDatabaseHelper.java b/src/com/android/providers/calendar/CalendarDatabaseHelper.java
index 407e21a..bb979c6 100644
--- a/src/com/android/providers/calendar/CalendarDatabaseHelper.java
+++ b/src/com/android/providers/calendar/CalendarDatabaseHelper.java
@@ -3368,21 +3368,12 @@ import java.util.TimeZone;
*/
protected void duplicateEvent(final long id) {
final SQLiteDatabase db = getWritableDatabase();
- try {
- final long canPartiallyUpdate = DatabaseUtils.longForQuery(db, "SELECT "
- + Calendars.CAN_PARTIALLY_UPDATE + " FROM " + Views.EVENTS
- + " WHERE " + Events._ID + " = ?", new String[] {
+ final long canPartiallyUpdate = DatabaseUtils.longForQuery(db, "SELECT "
+ + Calendars.CAN_PARTIALLY_UPDATE + " FROM " + Views.EVENTS
+ + " WHERE " + Events._ID + " = ?", new String[]{
String.valueOf(id)
- });
- if (canPartiallyUpdate == 0) {
- return;
- }
- } catch (SQLiteDoneException e) {
- // b/11392862
- // If no results are returned, this will be thrown. This can happen if the Events View
- // has no rows for the provided id. This might happen for example if someone inserts a
- // reminder that refers to a non existent event id.
- // Return without doing anything because there is no event to duplicate.
+ });
+ if (canPartiallyUpdate == 0) {
return;
}
diff --git a/src/com/android/providers/calendar/CalendarProvider2.java b/src/com/android/providers/calendar/CalendarProvider2.java
index 73a2275..ea2dd38 100644
--- a/src/com/android/providers/calendar/CalendarProvider2.java
+++ b/src/com/android/providers/calendar/CalendarProvider2.java
@@ -2174,8 +2174,8 @@ public class CalendarProvider2 extends SQLiteContentProvider implements OnAccoun
}
if (updatedValues.containsKey(Events.ORIGINAL_SYNC_ID)
&& !updatedValues.containsKey(Events.ORIGINAL_ID)) {
- long originalId = getOriginalId(updatedValues.getAsString(
- Events.ORIGINAL_SYNC_ID),
+ long originalId = getOriginalId(updatedValues
+ .getAsString(Events.ORIGINAL_SYNC_ID),
updatedValues.getAsString(Events.CALENDAR_ID));
if (originalId != -1) {
updatedValues.put(Events.ORIGINAL_ID, originalId);
@@ -2284,11 +2284,16 @@ public class CalendarProvider2 extends SQLiteContentProvider implements OnAccoun
}
id = mDbHelper.colorsInsert(values);
break;
- case ATTENDEES:
+ case ATTENDEES: {
if (!values.containsKey(Attendees.EVENT_ID)) {
throw new IllegalArgumentException("Attendees values must "
+ "contain an event_id");
}
+ Long eventIdObj = values.getAsLong(Reminders.EVENT_ID);
+ if (!doesEventExist(eventIdObj)) {
+ Log.i(TAG, "Trying to insert a attendee to a non-existent event");
+ return null;
+ }
if (!callerIsSyncAdapter) {
final Long eventId = values.getAsLong(Attendees.EVENT_ID);
mDbHelper.duplicateEvent(eventId);
@@ -2299,13 +2304,18 @@ public class CalendarProvider2 extends SQLiteContentProvider implements OnAccoun
// Copy the attendee status value to the Events table.
updateEventAttendeeStatus(mDb, values);
break;
- case REMINDERS:
- {
+ }
+ case REMINDERS: {
Long eventIdObj = values.getAsLong(Reminders.EVENT_ID);
if (eventIdObj == null) {
throw new IllegalArgumentException("Reminders values must "
+ "contain a numeric event_id");
}
+ if (!doesEventExist(eventIdObj)) {
+ Log.i(TAG, "Trying to insert a reminder to a non-existent event");
+ return null;
+ }
+
if (!callerIsSyncAdapter) {
mDbHelper.duplicateEvent(eventIdObj);
setEventDirty(eventIdObj);
@@ -2322,19 +2332,31 @@ public class CalendarProvider2 extends SQLiteContentProvider implements OnAccoun
mCalendarAlarm.scheduleNextAlarm(false /* do not remove alarms */);
break;
}
- case CALENDAR_ALERTS:
- if (!values.containsKey(CalendarAlerts.EVENT_ID)) {
+ case CALENDAR_ALERTS: {
+ Long eventIdObj = values.getAsLong(Reminders.EVENT_ID);
+ if (eventIdObj == null) {
throw new IllegalArgumentException("CalendarAlerts values must "
- + "contain an event_id");
+ + "contain a numeric event_id");
+ }
+ if (!doesEventExist(eventIdObj)) {
+ Log.i(TAG, "Trying to insert an alert to a non-existent event");
+ return null;
}
id = mDbHelper.calendarAlertsInsert(values);
// Note: dirty bit is not set for Alerts because it is not synced.
// It is generated from Reminders, which is synced.
break;
- case EXTENDED_PROPERTIES:
- if (!values.containsKey(CalendarContract.ExtendedProperties.EVENT_ID)) {
+ }
+ case EXTENDED_PROPERTIES: {
+ Long eventIdObj = values.getAsLong(Reminders.EVENT_ID);
+ if (eventIdObj == null) {
throw new IllegalArgumentException("ExtendedProperties values must "
- + "contain an event_id");
+ + "contain a numeric event_id");
+ }
+ if (!doesEventExist(eventIdObj)) {
+ Log.i(TAG, "Trying to insert extended properties to a non-existent event id = "
+ + eventIdObj);
+ return null;
}
if (!callerIsSyncAdapter) {
final Long eventId = values
@@ -2344,6 +2366,7 @@ public class CalendarProvider2 extends SQLiteContentProvider implements OnAccoun
}
id = mDbHelper.extendedPropertiesInsert(values);
break;
+ }
case EMMA:
// Special target used during code-coverage evaluation.
handleEmmaRequest(values);
@@ -2364,10 +2387,14 @@ public class CalendarProvider2 extends SQLiteContentProvider implements OnAccoun
if (id < 0) {
return null;
}
-
return ContentUris.withAppendedId(uri, id);
}
+ private boolean doesEventExist(long eventId) {
+ return DatabaseUtils.queryNumEntries(mDb, Tables.EVENTS, Events._ID + "=?",
+ new String[]{String.valueOf(eventId)}) > 0;
+ }
+
/**
* Handles special commands related to EMMA code-coverage testing.
*
diff --git a/tests/src/com/android/providers/calendar/CalendarProvider2Test.java b/tests/src/com/android/providers/calendar/CalendarProvider2Test.java
index 98c4b6a..a4c5e35 100644
--- a/tests/src/com/android/providers/calendar/CalendarProvider2Test.java
+++ b/tests/src/com/android/providers/calendar/CalendarProvider2Test.java
@@ -2014,6 +2014,50 @@ public class CalendarProvider2Test extends AndroidTestCase {
cursor.close();
}
+ public void testInsertAlertToNonExistentEvent() {
+ Uri alertUri = CalendarContract.CalendarAlerts.insert(mResolver, 1 /* eventId */,
+ 2 /* begin */, 3 /* end */, 4 /* alarmTime */, 5 /* minutes */);
+ assertEquals(null, alertUri);
+ }
+
+ public void testInsertReminderToNonExistentEvent() {
+ ContentValues reminder = new ContentValues();
+ reminder.put(CalendarContract.Reminders.MINUTES, 30);
+ reminder.put(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_EMAIL);
+ reminder.put(CalendarContract.Attendees.EVENT_ID, 1);
+ Uri reminderUri = mResolver.insert(
+ updatedUri(CalendarContract.Reminders.CONTENT_URI, true, DEFAULT_ACCOUNT,
+ DEFAULT_ACCOUNT_TYPE), reminder);
+ assertEquals(null, reminderUri);
+ }
+
+ public void testInsertAttendeeToNonExistentEvent() {
+ ContentValues attendee = new ContentValues();
+ attendee.put(CalendarContract.Attendees.ATTENDEE_NAME, "Joe");
+ attendee.put(CalendarContract.Attendees.ATTENDEE_EMAIL, DEFAULT_ACCOUNT);
+ attendee.put(CalendarContract.Attendees.ATTENDEE_TYPE,
+ CalendarContract.Attendees.TYPE_REQUIRED);
+ attendee.put(CalendarContract.Attendees.ATTENDEE_RELATIONSHIP,
+ CalendarContract.Attendees.RELATIONSHIP_ORGANIZER);
+ attendee.put(CalendarContract.Attendees.EVENT_ID, 1);
+ attendee.put(CalendarContract.Attendees.ATTENDEE_IDENTITY, "ID1");
+ attendee.put(CalendarContract.Attendees.ATTENDEE_ID_NAMESPACE, "IDNS1");
+ Uri attendeesUri = mResolver.insert(CalendarContract.Attendees.CONTENT_URI, attendee);
+ assertEquals(null, attendeesUri);
+ }
+
+ public void testInsertExtendedPropertyToNonExistentEvent() {
+ ContentValues extended = new ContentValues();
+ extended.put(CalendarContract.ExtendedProperties.NAME, "foo");
+ extended.put(CalendarContract.ExtendedProperties.VALUE, "bar");
+ extended.put(CalendarContract.ExtendedProperties.EVENT_ID, 1);
+
+ Uri extendedUri = mResolver.insert(
+ updatedUri(CalendarContract.ExtendedProperties.CONTENT_URI, true,
+ DEFAULT_ACCOUNT, DEFAULT_ACCOUNT_TYPE), extended);
+ assertEquals(null, extendedUri);
+ }
+
void checkEvents(int count, SQLiteDatabase db) {
Cursor cursor = db.query("Events", null, null, null, null, null, null);
try {