diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/database/CallTableTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/database/CallTableTest.kt index ceb9450e30..828ad567d4 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/database/CallTableTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/database/CallTableTest.kt @@ -21,6 +21,7 @@ class CallTableTest { @Test fun givenACall_whenISetTimestamp_thenIExpectUpdatedTimestamp() { val callId = 1L + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) val now = System.currentTimeMillis() SignalDatabase.calls.insertAcceptedGroupCall( callId, @@ -29,8 +30,8 @@ class CallTableTest { now ) - SignalDatabase.calls.setTimestamp(callId, -1L) - val call = SignalDatabase.calls.getCallById(callId) + SignalDatabase.calls.setTimestamp(callId, conversationId, -1L) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(-1L, call?.timestamp) @@ -42,6 +43,7 @@ class CallTableTest { @Test fun givenPreExistingEvent_whenIDeleteGroupCall_thenIMarkDeletedAndSetTimestamp() { val callId = 1L + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) val now = System.currentTimeMillis() SignalDatabase.calls.insertAcceptedGroupCall( callId, @@ -50,10 +52,10 @@ class CallTableTest { now ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) SignalDatabase.calls.deleteGroupCall(call!!) - val deletedCall = SignalDatabase.calls.getCallById(callId) + val deletedCall = SignalDatabase.calls.getCallById(callId, conversationId) val oldestDeletionTimestamp = SignalDatabase.calls.getOldestDeletionTimestamp() assertEquals(CallTable.Event.DELETE, deletedCall?.event) @@ -64,6 +66,7 @@ class CallTableTest { @Test fun givenNoPreExistingEvent_whenIDeleteGroupCall_thenIInsertAndMarkCallDeleted() { val callId = 1L + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertDeletedGroupCallFromSyncEvent( callId, harness.others[0], @@ -71,7 +74,7 @@ class CallTableTest { System.currentTimeMillis() ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) val oldestDeletionTimestamp = SignalDatabase.calls.getOldestDeletionTimestamp() @@ -84,6 +87,7 @@ class CallTableTest { @Test fun givenNoPriorEvent_whenIInsertAcceptedOutgoingGroupCall_thenIExpectLocalRingerAndOutgoingRing() { val callId = 1L + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertAcceptedGroupCall( callId, harness.others[0], @@ -91,7 +95,7 @@ class CallTableTest { System.currentTimeMillis() ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.OUTGOING_RING, call?.event) assertEquals(harness.self.id, call?.ringerRecipient) @@ -101,6 +105,7 @@ class CallTableTest { @Test fun givenNoPriorEvent_whenIInsertAcceptedIncomingGroupCall_thenIExpectJoined() { val callId = 1L + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertAcceptedGroupCall( callId, harness.others[0], @@ -108,7 +113,7 @@ class CallTableTest { System.currentTimeMillis() ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.JOINED, call?.event) assertNull(call?.ringerRecipient) @@ -118,6 +123,7 @@ class CallTableTest { @Test fun givenARingingCall_whenIAcceptedIncomingGroupCall_thenIExpectAccepted() { val callId = 1L + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromRingState( ringId = callId, groupRecipientId = harness.others[0], @@ -126,7 +132,7 @@ class CallTableTest { ringState = CallManager.RingUpdate.REQUESTED ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.RINGING, call?.event) @@ -134,13 +140,14 @@ class CallTableTest { call!! ) - val acceptedCall = SignalDatabase.calls.getCallById(callId) + val acceptedCall = SignalDatabase.calls.getCallById(callId, conversationId) assertEquals(CallTable.Event.ACCEPTED, acceptedCall?.event) } @Test fun givenAMissedCall_whenIAcceptedIncomingGroupCall_thenIExpectAccepted() { val callId = 1L + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromRingState( ringId = callId, groupRecipientId = harness.others[0], @@ -149,7 +156,7 @@ class CallTableTest { ringState = CallManager.RingUpdate.EXPIRED_REQUEST ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.MISSED, call?.event) @@ -157,13 +164,14 @@ class CallTableTest { call!! ) - val acceptedCall = SignalDatabase.calls.getCallById(callId) + val acceptedCall = SignalDatabase.calls.getCallById(callId, conversationId) assertEquals(CallTable.Event.ACCEPTED, acceptedCall?.event) } @Test fun givenADeclinedCall_whenIAcceptedIncomingGroupCall_thenIExpectAccepted() { val callId = 1L + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromRingState( ringId = callId, groupRecipientId = harness.others[0], @@ -172,7 +180,7 @@ class CallTableTest { ringState = CallManager.RingUpdate.DECLINED_ON_ANOTHER_DEVICE ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.DECLINED, call?.event) @@ -180,7 +188,7 @@ class CallTableTest { call!! ) - val acceptedCall = SignalDatabase.calls.getCallById(callId) + val acceptedCall = SignalDatabase.calls.getCallById(callId, conversationId) assertEquals(CallTable.Event.ACCEPTED, acceptedCall?.event) } @@ -188,6 +196,7 @@ class CallTableTest { fun givenAGenericGroupCall_whenIAcceptedIncomingGroupCall_thenIExpectAccepted() { val era = "aaa" val callId = CallId.fromEra(era).longValue() + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent( groupRecipientId = harness.others[0], sender = harness.others[1], @@ -197,7 +206,7 @@ class CallTableTest { isCallFull = false ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.GENERIC_GROUP_CALL, call?.event) @@ -205,7 +214,7 @@ class CallTableTest { call!! ) - val acceptedCall = SignalDatabase.calls.getCallById(callId) + val acceptedCall = SignalDatabase.calls.getCallById(callId, conversationId) assertEquals(CallTable.Event.JOINED, acceptedCall?.event) } @@ -213,6 +222,7 @@ class CallTableTest { fun givenNoPriorCallEvent_whenIReceiveAGroupCallUpdateMessage_thenIExpectAGenericGroupCall() { val era = "aaa" val callId = CallId.fromEra(era).longValue() + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent( groupRecipientId = harness.others[0], sender = harness.others[1], @@ -222,7 +232,7 @@ class CallTableTest { isCallFull = false ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.GENERIC_GROUP_CALL, call?.event) } @@ -232,6 +242,7 @@ class CallTableTest { val era = "aaa" val callId = CallId.fromEra(era).longValue() val now = System.currentTimeMillis() + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent( groupRecipientId = harness.others[0], sender = harness.others[1], @@ -241,7 +252,7 @@ class CallTableTest { isCallFull = false ) - SignalDatabase.calls.getCallById(callId).let { + SignalDatabase.calls.getCallById(callId, conversationId).let { assertNotNull(it) assertEquals(now, it?.timestamp) } @@ -255,7 +266,7 @@ class CallTableTest { isCallFull = false ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.GENERIC_GROUP_CALL, call?.event) assertEquals(1L, call?.timestamp) @@ -264,6 +275,7 @@ class CallTableTest { @Test fun givenADeletedCallEvent_whenIReceiveARingUpdate_thenIIgnoreTheRingUpdate() { val callId = 1L + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertDeletedGroupCallFromSyncEvent( callId = callId, recipientId = harness.others[0], @@ -279,7 +291,7 @@ class CallTableTest { ringState = CallManager.RingUpdate.REQUESTED ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.DELETE, call?.event) } @@ -289,6 +301,7 @@ class CallTableTest { val era = "aaa" val callId = CallId.fromEra(era).longValue() val now = System.currentTimeMillis() + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent( groupRecipientId = harness.others[0], sender = harness.others[1], @@ -306,7 +319,7 @@ class CallTableTest { CallManager.RingUpdate.REQUESTED ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.RINGING, call?.event) assertEquals(harness.others[1], call?.ringerRecipient) @@ -317,6 +330,7 @@ class CallTableTest { val era = "aaa" val callId = CallId.fromEra(era).longValue() val now = System.currentTimeMillis() + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertAcceptedGroupCall( callId, harness.others[0], @@ -332,7 +346,7 @@ class CallTableTest { CallManager.RingUpdate.REQUESTED ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.ACCEPTED, call?.event) assertEquals(harness.others[1], call?.ringerRecipient) @@ -343,6 +357,7 @@ class CallTableTest { val era = "aaa" val callId = CallId.fromEra(era).longValue() val now = System.currentTimeMillis() + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent( groupRecipientId = harness.others[0], sender = harness.others[1], @@ -360,7 +375,7 @@ class CallTableTest { CallManager.RingUpdate.EXPIRED_REQUEST ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.MISSED, call?.event) assertEquals(harness.others[1], call?.ringerRecipient) @@ -371,6 +386,7 @@ class CallTableTest { val era = "aaa" val callId = CallId.fromEra(era).longValue() val now = System.currentTimeMillis() + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent( groupRecipientId = harness.others[0], sender = harness.others[1], @@ -396,7 +412,7 @@ class CallTableTest { CallManager.RingUpdate.EXPIRED_REQUEST ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.MISSED, call?.event) assertEquals(harness.others[1], call?.ringerRecipient) @@ -407,6 +423,7 @@ class CallTableTest { val era = "aaa" val callId = CallId.fromEra(era).longValue() val now = System.currentTimeMillis() + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertAcceptedGroupCall( callId, harness.others[0], @@ -422,7 +439,7 @@ class CallTableTest { CallManager.RingUpdate.BUSY_LOCALLY ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.ACCEPTED, call?.event) } @@ -432,6 +449,7 @@ class CallTableTest { val era = "aaa" val callId = CallId.fromEra(era).longValue() val now = System.currentTimeMillis() + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertAcceptedGroupCall( callId, harness.others[0], @@ -447,7 +465,7 @@ class CallTableTest { CallManager.RingUpdate.BUSY_ON_ANOTHER_DEVICE ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.ACCEPTED, call?.event) } @@ -457,6 +475,7 @@ class CallTableTest { val era = "aaa" val callId = CallId.fromEra(era).longValue() val now = System.currentTimeMillis() + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent( groupRecipientId = harness.others[0], sender = harness.others[1], @@ -482,7 +501,7 @@ class CallTableTest { CallManager.RingUpdate.BUSY_LOCALLY ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.MISSED, call?.event) } @@ -492,6 +511,7 @@ class CallTableTest { val era = "aaa" val callId = CallId.fromEra(era).longValue() val now = System.currentTimeMillis() + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent( groupRecipientId = harness.others[0], sender = harness.others[1], @@ -517,7 +537,7 @@ class CallTableTest { CallManager.RingUpdate.BUSY_ON_ANOTHER_DEVICE ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.MISSED, call?.event) } @@ -527,6 +547,7 @@ class CallTableTest { val era = "aaa" val callId = CallId.fromEra(era).longValue() val now = System.currentTimeMillis() + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent( groupRecipientId = harness.others[0], sender = harness.others[1], @@ -544,7 +565,7 @@ class CallTableTest { CallManager.RingUpdate.ACCEPTED_ON_ANOTHER_DEVICE ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.ACCEPTED, call?.event) } @@ -553,6 +574,7 @@ class CallTableTest { fun givenARingingCallEvent_whenRingDeclinedOnAnotherDevice_thenIMoveToDeclinedState() { val era = "aaa" val callId = CallId.fromEra(era).longValue() + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromRingState( callId, @@ -570,7 +592,7 @@ class CallTableTest { CallManager.RingUpdate.DECLINED_ON_ANOTHER_DEVICE ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.DECLINED, call?.event) } @@ -579,6 +601,7 @@ class CallTableTest { fun givenAMissedCallEvent_whenRingDeclinedOnAnotherDevice_thenIMoveToDeclinedState() { val era = "aaa" val callId = CallId.fromEra(era).longValue() + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromRingState( callId, @@ -596,7 +619,7 @@ class CallTableTest { CallManager.RingUpdate.DECLINED_ON_ANOTHER_DEVICE ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.DECLINED, call?.event) } @@ -605,6 +628,7 @@ class CallTableTest { fun givenAnOutgoingRingCallEvent_whenRingDeclinedOnAnotherDevice_thenIDoNotChangeState() { val era = "aaa" val callId = CallId.fromEra(era).longValue() + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertAcceptedGroupCall( callId, @@ -621,7 +645,7 @@ class CallTableTest { CallManager.RingUpdate.DECLINED_ON_ANOTHER_DEVICE ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.OUTGOING_RING, call?.event) } @@ -629,6 +653,7 @@ class CallTableTest { @Test fun givenNoPriorEvent_whenRingRequested_thenICreateAnEventInTheRingingStateAndSetRinger() { val callId = 1L + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromRingState( callId, harness.others[0], @@ -637,7 +662,7 @@ class CallTableTest { CallManager.RingUpdate.REQUESTED ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.RINGING, call?.event) assertEquals(harness.others[1], call?.ringerRecipient) @@ -647,6 +672,7 @@ class CallTableTest { @Test fun givenNoPriorEvent_whenRingExpired_thenICreateAnEventInTheMissedStateAndSetRinger() { val callId = 1L + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromRingState( callId, harness.others[0], @@ -655,7 +681,7 @@ class CallTableTest { CallManager.RingUpdate.EXPIRED_REQUEST ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.MISSED, call?.event) assertEquals(harness.others[1], call?.ringerRecipient) @@ -665,6 +691,7 @@ class CallTableTest { @Test fun givenNoPriorEvent_whenRingCancelledByRinger_thenICreateAnEventInTheMissedStateAndSetRinger() { val callId = 1L + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromRingState( callId, harness.others[0], @@ -673,7 +700,7 @@ class CallTableTest { CallManager.RingUpdate.CANCELLED_BY_RINGER ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.MISSED, call?.event) assertEquals(harness.others[1], call?.ringerRecipient) @@ -683,6 +710,7 @@ class CallTableTest { @Test fun givenNoPriorEvent_whenRingCancelledBecauseUserIsBusyLocally_thenICreateAnEventInTheMissedState() { val callId = 1L + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromRingState( callId, harness.others[0], @@ -691,7 +719,7 @@ class CallTableTest { CallManager.RingUpdate.BUSY_LOCALLY ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.MISSED, call?.event) assertNotNull(call?.messageId) @@ -700,6 +728,7 @@ class CallTableTest { @Test fun givenNoPriorEvent_whenRingCancelledBecauseUserIsBusyOnAnotherDevice_thenICreateAnEventInTheMissedState() { val callId = 1L + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromRingState( callId, harness.others[0], @@ -708,7 +737,7 @@ class CallTableTest { CallManager.RingUpdate.BUSY_ON_ANOTHER_DEVICE ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.MISSED, call?.event) assertNotNull(call?.messageId) @@ -717,6 +746,7 @@ class CallTableTest { @Test fun givenNoPriorEvent_whenRingAcceptedOnAnotherDevice_thenICreateAnEventInTheAcceptedState() { val callId = 1L + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromRingState( callId, harness.others[0], @@ -725,7 +755,7 @@ class CallTableTest { CallManager.RingUpdate.ACCEPTED_ON_ANOTHER_DEVICE ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.ACCEPTED, call?.event) assertNotNull(call?.messageId) @@ -734,6 +764,7 @@ class CallTableTest { @Test fun givenNoPriorEvent_whenRingDeclinedOnAnotherDevice_thenICreateAnEventInTheDeclinedState() { val callId = 1L + val conversationId = CallTable.CallConversationId.Peer(harness.others[0]) SignalDatabase.calls.insertOrUpdateGroupCallFromRingState( callId, harness.others[0], @@ -742,7 +773,7 @@ class CallTableTest { CallManager.RingUpdate.DECLINED_ON_ANOTHER_DEVICE ) - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, conversationId) assertNotNull(call) assertEquals(CallTable.Event.DECLINED, call?.event) assertNotNull(call?.messageId) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/CallLinkTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/CallLinkTable.kt new file mode 100644 index 0000000000..76678b631d --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/CallLinkTable.kt @@ -0,0 +1,24 @@ +package org.thoughtcrime.securesms.database + +import android.content.Context +import org.signal.core.util.logging.Log + +/** + * Table containing ad-hoc call link details + */ +class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTable(context, databaseHelper) { + + companion object { + private val TAG = Log.tag(CallLinkTable::class.java) + + const val TABLE_NAME = "call_link" + const val ID = "_id" + + //language=sql + val CREATE_TABLE = """ + CREATE TABLE $TABLE_NAME ( + $ID INTEGER PRIMARY KEY + ) + """.trimIndent() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt index b572659ece..067f10cb95 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt @@ -46,6 +46,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl private const val CALL_ID = "call_id" private const val MESSAGE_ID = "message_id" private const val PEER = "peer" + private const val CALL_LINK = "call_link" private const val TYPE = "type" private const val DIRECTION = "direction" private const val EVENT = "event" @@ -57,15 +58,18 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl val CREATE_TABLE = """ CREATE TABLE $TABLE_NAME ( $ID INTEGER PRIMARY KEY, - $CALL_ID INTEGER NOT NULL UNIQUE, + $CALL_ID INTEGER NOT NULL, $MESSAGE_ID INTEGER DEFAULT NULL REFERENCES ${MessageTable.TABLE_NAME} (${MessageTable.ID}) ON DELETE SET NULL, $PEER INTEGER DEFAULT NULL REFERENCES ${RecipientTable.TABLE_NAME} (${RecipientTable.ID}) ON DELETE CASCADE, + $CALL_LINK INTEGER DEFAULT NULL REFERENCES ${CallLinkTable.TABLE_NAME} (${CallLinkTable.ID}) ON DELETE CASCADE, $TYPE INTEGER NOT NULL, $DIRECTION INTEGER NOT NULL, $EVENT INTEGER NOT NULL, $TIMESTAMP INTEGER NOT NULL, $RINGER INTEGER DEFAULT NULL, - $DELETION_TIMESTAMP INTEGER DEFAULT 0 + $DELETION_TIMESTAMP INTEGER DEFAULT 0, + UNIQUE ($CALL_ID, $PEER, $CALL_LINK) ON CONFLICT FAIL, + CHECK (($PEER IS NULL AND $CALL_LINK IS NOT NULL) OR ($PEER IS NOT NULL AND $CALL_LINK IS NULL)) ) """.trimIndent() @@ -127,11 +131,13 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl } } - fun getCallById(callId: Long): Call? { + fun getCallById(callId: Long, conversationId: CallConversationId): Call? { + val query = getCallSelectionQuery(callId, conversationId) + return readableDatabase .select() .from(TABLE_NAME) - .where("$CALL_ID = ?", callId) + .where(query.where, query.whereArgs) .run() .readToSingleObject(Call.Deserializer) } @@ -358,7 +364,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl } val callId = CallId.fromEra(peekGroupCallEraId).longValue() - val call = getCallById(callId) + val call = getCallById(callId, CallConversationId.Peer(groupRecipientId)) val messageId: MessageId = if (call != null) { if (call.event == Event.DELETE) { Log.d(TAG, "Dropping group call update for deleted call.") @@ -409,8 +415,9 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl groupRecipientId: RecipientId, timestamp: Long ) { + val conversationId = CallConversationId.Peer(groupRecipientId) if (messageId != null) { - val call = getCallById(callId) + val call = getCallById(callId, conversationId) if (call == null) { val direction = if (sender == Recipient.self().id) Direction.OUTGOING else Direction.INCOMING @@ -431,7 +438,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl Log.d(TAG, "Inserted new call event from group call update message. Call Id: $callId") } else { if (timestamp < call.timestamp) { - setTimestamp(callId, timestamp) + setTimestamp(callId, conversationId, timestamp) Log.d(TAG, "Updated call event timestamp for call id $callId") } @@ -482,8 +489,8 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl handleGroupRingState(ringId, groupRecipientId, ringerRecipient.id, dateReceived, ringState) } - fun isRingCancelled(ringId: Long): Boolean { - val call = getCallById(ringId) ?: return false + fun isRingCancelled(ringId: Long, groupRecipientId: RecipientId): Boolean { + val call = getCallById(ringId, CallConversationId.Peer(groupRecipientId)) ?: return false return call.event != Event.RINGING } @@ -496,7 +503,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl ) { Log.d(TAG, "Processing group ring state update for $ringId in state $ringState") - val call = getCallById(ringId) + val call = getCallById(ringId, CallConversationId.Peer(groupRecipientId)) if (call != null) { if (call.event == Event.DELETE) { Log.d(TAG, "Ignoring ring request for $ringId since its event has been deleted.") @@ -633,9 +640,9 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl Log.d(TAG, "Inserted a new group ring event for $callId with event $event") } - fun setTimestamp(callId: Long, timestamp: Long) { + fun setTimestamp(callId: Long, conversationId: CallConversationId, timestamp: Long) { writableDatabase.withinTransaction { db -> - val call = getCallById(callId) + val call = getCallById(callId, conversationId) if (call == null || call.event == Event.DELETE) { Log.d(TAG, "Refusing to update deleted call event.") return@withinTransaction @@ -683,6 +690,13 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl .run() } + private fun getCallSelectionQuery(callId: Long, conversationId: CallConversationId): SqlUtil.Query { + return when (conversationId) { + is CallConversationId.CallLink -> SqlUtil.Query("$CALL_ID = ? AND $CALL_LINK = ?", SqlUtil.buildArgs(callId, conversationId.callLinkId)) + is CallConversationId.Peer -> SqlUtil.Query("$CALL_ID = ? AND $PEER = ?", SqlUtil.buildArgs(callId, conversationId.recipientId)) + } + } + private fun getMessageIds(callIds: Set): Set { val queries = SqlUtil.buildCollectionQuery( CALL_ID, @@ -908,6 +922,11 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl } } + sealed interface CallConversationId { + data class Peer(val recipientId: RecipientId) : CallConversationId + data class CallLink(val callLinkId: Int) : CallConversationId + } + enum class Event(private val code: Int) { /** * 1:1 Calls only. diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt index 552f027974..51deaaa9f5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt @@ -108,6 +108,7 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data db.execSQL(CdsTable.CREATE_TABLE) db.execSQL(RemoteMegaphoneTable.CREATE_TABLE) db.execSQL(PendingPniSignatureMessageTable.CREATE_TABLE) + db.execSQL(CallLinkTable.CREATE_TABLE) db.execSQL(CallTable.CREATE_TABLE) executeStatements(db, SearchTable.CREATE_TABLE) executeStatements(db, RemappedRecordTables.CREATE_TABLE) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index 484bb042ed..ee1ed5a89e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V179_CleanupDanglin import org.thoughtcrime.securesms.database.helpers.migration.V180_RecipientNicknameMigration import org.thoughtcrime.securesms.database.helpers.migration.V181_ThreadTableForeignKeyCleanup import org.thoughtcrime.securesms.database.helpers.migration.V182_CallTableMigration +import org.thoughtcrime.securesms.database.helpers.migration.V183_CallLinkTableMigration /** * Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness. @@ -46,7 +47,7 @@ object SignalDatabaseMigrations { val TAG: String = Log.tag(SignalDatabaseMigrations.javaClass) - const val DATABASE_VERSION = 182 + const val DATABASE_VERSION = 183 @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { @@ -185,6 +186,10 @@ object SignalDatabaseMigrations { if (oldVersion < 182) { V182_CallTableMigration.migrate(context, db, oldVersion, newVersion) } + + if (oldVersion < 183) { + V183_CallLinkTableMigration.migrate(context, db, oldVersion, newVersion) + } } @JvmStatic diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V183_CallLinkTableMigration.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V183_CallLinkTableMigration.kt new file mode 100644 index 0000000000..09d9db7013 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V183_CallLinkTableMigration.kt @@ -0,0 +1,55 @@ +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase + +/** + * Adds the CallLinkTable and modifies the CallTable to include an FK into it. + */ +object V183_CallLinkTableMigration : SignalDatabaseMigration { + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL("CREATE TABLE call_link (_id INTEGER PRIMARY KEY)") + + db.execSQL( + """ + CREATE TABLE call_tmp ( + _id INTEGER PRIMARY KEY, + call_id INTEGER NOT NULL, + message_id INTEGER DEFAULT NULL REFERENCES message (_id) ON DELETE SET NULL, + peer INTEGER DEFAULT NULL REFERENCES recipient (_id) ON DELETE CASCADE, + call_link INTEGER DEFAULT NULL REFERENCES call_link (_id) ON DELETE CASCADE, + type INTEGER NOT NULL, + direction INTEGER NOT NULL, + event INTEGER NOT NULL, + timestamp INTEGER NOT NULL, + ringer INTEGER DEFAULT NULL, + deletion_timestamp INTEGER DEFAULT 0, + UNIQUE (_id, peer, call_link) ON CONFLICT FAIL, + CHECK ((peer IS NULL AND call_link IS NOT NULL) OR (peer IS NOT NULL AND call_link IS NULL)) + ) + """.trimIndent() + ) + + db.execSQL( + """ + INSERT INTO call_tmp + SELECT + _id, + call_id, + message_id, + peer, + NULL as call_link, + type, + direction, + event, + timestamp, + ringer, + deletion_timestamp + FROM call + """.trimIndent() + ) + + db.execSQL("DROP TABLE call") + db.execSQL("ALTER TABLE call_tmp RENAME TO call") + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java index 0229fde4c6..5643aa554e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java @@ -1290,7 +1290,7 @@ public class MessageContentProcessor { log(envelopeTimestamp, "Synchronize call event call: " + callId); - CallTable.Call call = SignalDatabase.calls().getCallById(callId); + CallTable.Call call = SignalDatabase.calls().getCallById(callId, new CallTable.CallConversationId.Peer(recipientId)); if (call != null) { boolean typeMismatch = call.getType() != type; boolean directionMismatch = call.getDirection() != direction; @@ -1331,7 +1331,11 @@ public class MessageContentProcessor { return; } - CallTable.Call call = SignalDatabase.calls().getCallById(callId); + GroupId groupId = GroupId.push(callEvent.getConversationId().toByteArray()); + RecipientId recipientId = Recipient.externalGroupExact(groupId).getId(); + CallTable.CallConversationId callConversationId = new CallTable.CallConversationId.Peer(recipientId); + + CallTable.Call call = SignalDatabase.calls().getCallById(callId, callConversationId); if (call != null) { if (call.getType() != type) { warn(envelopeTimestamp, "Group/Ad-hoc call event type mismatch, ignoring. timestamp: " + timestamp + " type: " + type + " direction: " + direction + " event: " + event + " hasPeer: " + callEvent.hasConversationId()); @@ -1344,7 +1348,7 @@ public class MessageContentProcessor { break; case ACCEPTED: if (call.getTimestamp() < callEvent.getTimestamp()) { - SignalDatabase.calls().setTimestamp(call.getCallId(), callEvent.getTimestamp()); + SignalDatabase.calls().setTimestamp(call.getCallId(), callConversationId, callEvent.getTimestamp()); } if (callEvent.getDirection() == SyncMessage.CallEvent.Direction.INCOMING) { @@ -1359,9 +1363,6 @@ public class MessageContentProcessor { warn("Unsupported event type " + event + ". Ignoring. timestamp: " + timestamp + " type: " + type + " direction: " + direction + " event: " + event + " hasPeer: " + callEvent.hasConversationId()); } } else { - GroupId groupId = GroupId.push(callEvent.getConversationId().toByteArray()); - RecipientId recipientId = Recipient.externalGroupExact(groupId).getId(); - switch (event) { case DELETE: SignalDatabase.calls().insertDeletedGroupCallFromSyncEvent(callEvent.getId(), recipientId, direction, timestamp); diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt index c9a93681a0..477b8baacc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt @@ -1009,7 +1009,7 @@ object SyncMessageProcessor { log(envelopeTimestamp, "Synchronize call event call: $callId") - val call = SignalDatabase.calls.getCallById(callId) + val call = SignalDatabase.calls.getCallById(callId, CallTable.CallConversationId.Peer(recipientId)) if (call != null) { val typeMismatch = call.type !== type val directionMismatch = call.direction !== direction @@ -1044,7 +1044,11 @@ object SyncMessageProcessor { return } - val call = SignalDatabase.calls.getCallById(callId) + val groupId: GroupId = GroupId.push(callEvent.conversationId.toByteArray()) + val recipientId = Recipient.externalGroupExact(groupId).id + val conversationId = CallTable.CallConversationId.Peer(recipientId) + + val call = SignalDatabase.calls.getCallById(callId, CallTable.CallConversationId.Peer(recipientId)) if (call != null) { if (call.type !== type) { @@ -1055,7 +1059,7 @@ object SyncMessageProcessor { CallTable.Event.DELETE -> SignalDatabase.calls.deleteGroupCall(call) CallTable.Event.ACCEPTED -> { if (call.timestamp < callEvent.timestamp) { - SignalDatabase.calls.setTimestamp(call.callId, callEvent.timestamp) + SignalDatabase.calls.setTimestamp(call.callId, conversationId, callEvent.timestamp) } if (callEvent.direction == SyncMessage.CallEvent.Direction.INCOMING) { SignalDatabase.calls.acceptIncomingGroupCall(call) @@ -1067,8 +1071,6 @@ object SyncMessageProcessor { else -> warn("Unsupported event type " + event + ". Ignoring. timestamp: " + timestamp + " type: " + type + " direction: " + direction + " event: " + event + " hasPeer: " + callEvent.hasConversationId()) } } else { - val groupId: GroupId = GroupId.push(callEvent.conversationId.toByteArray()) - val recipientId = Recipient.externalGroupExact(groupId).id when (event) { CallTable.Event.DELETE -> SignalDatabase.calls.insertDeletedGroupCallFromSyncEvent(callEvent.id, recipientId, direction, timestamp) CallTable.Event.ACCEPTED -> SignalDatabase.calls.insertAcceptedGroupCall(callEvent.id, recipientId, direction, timestamp) diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IdleActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IdleActionProcessor.java index 09d20f1110..1437115b46 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IdleActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IdleActionProcessor.java @@ -7,6 +7,7 @@ import org.signal.ringrtc.CallException; import org.signal.ringrtc.CallManager; import org.signal.ringrtc.PeekInfo; import org.thoughtcrime.securesms.components.webrtc.EglBaseWrapper; +import org.thoughtcrime.securesms.database.CallTable; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.events.WebRtcViewModel; import org.thoughtcrime.securesms.groups.GroupId; @@ -104,7 +105,7 @@ public class IdleActionProcessor extends WebRtcActionProcessor { if (ringUpdate != CallManager.RingUpdate.REQUESTED) { SignalDatabase.calls().insertOrUpdateGroupCallFromRingState(ringId, remotePeerGroup.getId(), sender, System.currentTimeMillis(), ringUpdate); return currentState; - } else if (SignalDatabase.calls().isRingCancelled(ringId)) { + } else if (SignalDatabase.calls().isRingCancelled(ringId, remotePeerGroup.getId())) { try { Log.i(TAG, "Incoming ring request for already cancelled ring: " + ringId); webRtcInteractor.getCallManager().cancelGroupRing(groupId.getDecodedId(), ringId, null); @@ -135,7 +136,7 @@ public class IdleActionProcessor extends WebRtcActionProcessor { protected @NonNull WebRtcServiceState handleReceivedGroupCallPeekForRingingCheck(@NonNull WebRtcServiceState currentState, @NonNull GroupCallRingCheckInfo info, @NonNull PeekInfo peekInfo) { Log.i(tag, "handleReceivedGroupCallPeekForRingingCheck(): recipient: " + info.getRecipientId() + " ring: " + info.getRingId()); - if (SignalDatabase.calls().isRingCancelled(info.getRingId())) { + if (SignalDatabase.calls().isRingCancelled(info.getRingId(), info.getRecipientId())) { try { Log.i(TAG, "Ring was cancelled while getting peek info ring: " + info.getRingId()); webRtcInteractor.getCallManager().cancelGroupRing(info.getGroupId().getDecodedId(), info.getRingId(), null); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingGroupCallActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingGroupCallActionProcessor.java index b057ba246c..77cd10364b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingGroupCallActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingGroupCallActionProcessor.java @@ -58,7 +58,7 @@ public final class IncomingGroupCallActionProcessor extends DeviceAwareActionPro boolean updateForCurrentRingId = ringId == currentState.getCallSetupState(RemotePeer.GROUP_CALL_ID).getRingId(); boolean isCurrentlyRinging = currentState.getCallInfoState().getGroupCallState().isRinging(); - if (SignalDatabase.calls().isRingCancelled(ringId)) { + if (SignalDatabase.calls().isRingCancelled(ringId, remotePeerGroup.getId())) { try { Log.i(TAG, "Ignoring incoming ring request for already cancelled ring: " + ringId); webRtcInteractor.getCallManager().cancelGroupRing(groupId.getDecodedId(), ringId, null);