Add stricter call row identification.

This commit is contained in:
Alex Hart
2023-04-06 16:21:40 -03:00
committed by Greyson Parrelli
parent 490feb358c
commit 433e8266c9
10 changed files with 204 additions and 65 deletions

View File

@@ -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()
}
}

View File

@@ -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<Long>): Set<Long> {
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.

View File

@@ -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)

View File

@@ -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

View File

@@ -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")
}
}