b/4460608 Deletes exceptions with original event

Change-Id: I53fdc4783454e527860dcac187b5a88f66539665
diff --git a/src/com/android/providers/calendar/CalendarProvider2.java b/src/com/android/providers/calendar/CalendarProvider2.java
index 5255d4a..7ac7e01 100644
--- a/src/com/android/providers/calendar/CalendarProvider2.java
+++ b/src/com/android/providers/calendar/CalendarProvider2.java
@@ -90,6 +90,9 @@
     private static final String[] ID_ONLY_PROJECTION =
             new String[] {Events._ID};
 
+    private static final String[] ID_RRULE_ONLY_PROJECTION =
+        new String[] {Events._ID, Events.RRULE};
+
     private static final String[] EVENTS_PROJECTION = new String[] {
             Events._SYNC_ID,
             Events.RRULE,
@@ -161,7 +164,9 @@
 
     protected static final String SQL_WHERE_ID = BaseColumns._ID + "=?";
     private static final String SQL_WHERE_EVENT_ID = "event_id=?";
-    private static final String SQL_WHERE_ORIGINAL_EVENT = Events.ORIGINAL_SYNC_ID + "=?";
+    private static final String SQL_WHERE_ORIGINAL_ID = Events.ORIGINAL_ID + "=?";
+    private static final String SQL_WHERE_ORIGINAL_ID_DELETED = Events.ORIGINAL_ID + "=? AND " +
+            Events.DELETED + "=?";
     private static final String SQL_WHERE_ATTENDEES_ID =
             Tables.ATTENDEES + "." + Attendees._ID + "=? AND " +
             Tables.EVENTS + "." + Events._ID + "=" + Tables.ATTENDEES + "." + Attendees.EVENT_ID;
@@ -2046,7 +2051,7 @@
                 selection = appendSyncAccountToSelection(uri, selection);
 
                 // Query this event to get the ids to delete.
-                Cursor cursor = mDb.query(Views.EVENTS, ID_ONLY_PROJECTION,
+                Cursor cursor = mDb.query(Views.EVENTS, ID_RRULE_ONLY_PROJECTION,
                         selection, selectionArgs, null /* groupBy */,
                         null /* having */, null /* sortOrder */);
                 try {
@@ -2070,6 +2075,8 @@
                             + "doesn't support selection based deletion for type "
                             + match);
                 }
+                // Trying to clean exceptions is faster than querying and
+                // checking, so pass in true for hasRRule
                 return deleteEventInternal(id, callerIsSyncAdapter, false /* isBatch */);
             }
             case ATTENDEES:
@@ -2207,11 +2214,15 @@
                 if (isRecurrenceEvent(rrule, rdate, origEvent)) {
                     mMetaData.clearInstanceRange();
                 }
+                boolean hasRRule = !TextUtils.isEmpty(rrule);
 
                 // we clean the Events and Attendees table if the caller is CalendarSyncAdapter
                 // or if the event is local (no syncId)
                 if (callerIsSyncAdapter || emptySyncId) {
                     mDb.delete(Tables.EVENTS, SQL_WHERE_ID, selectionArgs);
+                    if (hasRRule) {
+                        mDb.delete(Tables.EVENTS, SQL_WHERE_ORIGINAL_ID, selectionArgs);
+                    }
                 } else {
                     ContentValues values = new ContentValues();
                     values.put(Events.DELETED, 1);
@@ -2226,6 +2237,11 @@
                     mDb.delete(Tables.CALENDAR_ALERTS, SQL_WHERE_EVENT_ID, selectionArgs);
                     mDb.delete(Tables.EXTENDED_PROPERTIES, SQL_WHERE_EVENT_ID,
                             selectionArgs);
+                    if (hasRRule) {
+                        // soft delete any exceptions as well
+                        delete(Events.CONTENT_URI, SQL_WHERE_ORIGINAL_ID_DELETED,
+                                new String[] {String.valueOf(id), "0"});
+                    }
                 }
             }
         } finally {
diff --git a/tests/src/com/android/providers/calendar/CalendarProvider2Test.java b/tests/src/com/android/providers/calendar/CalendarProvider2Test.java
index dafad3a..7f4252f 100644
--- a/tests/src/com/android/providers/calendar/CalendarProvider2Test.java
+++ b/tests/src/com/android/providers/calendar/CalendarProvider2Test.java
@@ -1874,7 +1874,6 @@
         cursor.close();
 
         cursor = mResolver.query(eventUri, null, null, null, null);
-        // TODO figure out why this test fails. App works fine for this case.
         assertEquals("Created event is missing - cannot find EventUri = " + eventUri, 1,
                 cursor.getCount());
         int selfColumn = cursor.getColumnIndex(Calendar.Events.SELF_ATTENDEE_STATUS);
@@ -1915,7 +1914,7 @@
 
     /**
      * Test the event's dirty status and clear it.
-     * 
+     *
      * @param eventId event to fetch.
      * @param wanted the wanted dirty status
      */
@@ -2175,6 +2174,8 @@
                 "_id=" + eventId, null /* selectionArgs */));
         testQueryCount(Calendar.Events.CONTENT_URI, null, 2);
         // Right account, should be deleted
+        // TODO this test is now failing due to a race condition, should be
+        // rewritten using a dummy account like the CalendarCTS tests
         assertEquals("delete", 1, mResolver.delete(
                 updatedUri(Calendar.Events.CONTENT_URI, true /* syncAdapter */, DEFAULT_ACCOUNT,
                         DEFAULT_ACCOUNT_TYPE),