diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt index f5e7234246..42a416e511 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt @@ -2075,6 +2075,16 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da return results } + /** A function that's just to help with some temporary bug investigation. */ + private fun getAllPnis(): Set { + return readableDatabase + .select(PNI_COLUMN) + .from(TABLE_NAME) + .where("$PNI NOT NULL") + .run() + .readToSet { PNI.parseOrThrow(it.requireString(PNI_COLUMN)) } + } + /** * Gives you all of the recipientIds of possibly-registered users (i.e. REGISTERED or UNKNOWN) that can be found by the set of * provided E164s. @@ -2413,9 +2423,50 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da try { SignalDatabase.messages.insertSessionSwitchoverEvent(operation.recipientId, threadId, event) } catch (e: Exception) { - // TODO do different stuff based on whether a PNI session exists Log.e(TAG, "About to crash! Breadcrumbs: ${changeSet.breadCrumbs}, Operations: ${changeSet.operations}, ID: ${changeSet.id}") - throw e + + val allPnis: Set = getAllPnis() + val pnisWithSessions: Set = sessions.findAllThatHaveAnySession(allPnis) + Log.e(TAG, "We know of ${allPnis.size} PNIs, and there are sessions with ${pnisWithSessions.size} of them.") + + val record = getRecord(operation.recipientId) + Log.e(TAG, "ID: ${record.id}, E164: ${record.e164}, ACI: ${record.aci}, PNI: ${record.pni}, Registered: ${record.registered}") + + if (record.aci != null && record.aci == SignalStore.account().aci) { + if (pnisWithSessions.contains(SignalStore.account().pni!!)) { + throw SseWithSelfAci(e) + } else { + throw SseWithSelfAciNoSession(e) + } + } + + if (record.pni != null && record.pni == SignalStore.account().pni) { + if (pnisWithSessions.contains(SignalStore.account().pni!!)) { + throw SseWithSelfPni(e) + } else { + throw SseWithSelfPniNoSession(e) + } + } + + if (record.e164 != null && record.e164 == SignalStore.account().e164) { + if (pnisWithSessions.contains(SignalStore.account().pni!!)) { + throw SseWithSelfE164(e) + } else { + throw SseWithSelfE164NoSession(e) + } + } + + if (pnisWithSessions.isEmpty()) { + throw SseWithNoPniSessionsException(e) + } else if (pnisWithSessions.size == 1) { + if (pnisWithSessions.first() == SignalStore.account().pni) { + throw SseWithASinglePniSessionForSelfException(e) + } else { + throw SseWithASinglePniSessionException(e) + } + } else { + throw SseWithMultiplePniSessionsException(e) + } } } } @@ -4599,4 +4650,15 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da val operations: List, val breadCrumbs: List ) + + class SseWithSelfAci(cause: Exception) : IllegalStateException(cause) + class SseWithSelfAciNoSession(cause: Exception) : IllegalStateException(cause) + class SseWithSelfPni(cause: Exception) : IllegalStateException(cause) + class SseWithSelfPniNoSession(cause: Exception) : IllegalStateException(cause) + class SseWithSelfE164(cause: Exception) : IllegalStateException(cause) + class SseWithSelfE164NoSession(cause: Exception) : IllegalStateException(cause) + class SseWithNoPniSessionsException(cause: Exception) : IllegalStateException(cause) + class SseWithASinglePniSessionForSelfException(cause: Exception) : IllegalStateException(cause) + class SseWithASinglePniSessionException(cause: Exception) : IllegalStateException(cause) + class SseWithMultiplePniSessionsException(cause: Exception) : IllegalStateException(cause) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SessionTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/SessionTable.kt index 84fbb48b2a..e7adfd2671 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SessionTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SessionTable.kt @@ -4,13 +4,16 @@ import android.content.Context import org.signal.core.util.CursorUtil import org.signal.core.util.SqlUtil import org.signal.core.util.logging.Log +import org.signal.core.util.readToSet import org.signal.core.util.requireInt import org.signal.core.util.requireNonNullBlob import org.signal.core.util.requireNonNullString +import org.signal.core.util.requireString import org.signal.core.util.select import org.signal.libsignal.protocol.SignalProtocolAddress import org.signal.libsignal.protocol.state.SessionRecord import org.whispersystems.signalservice.api.push.ServiceId +import org.whispersystems.signalservice.api.push.ServiceId.PNI import org.whispersystems.signalservice.api.push.SignalServiceAddress import java.io.IOException import java.util.LinkedList @@ -210,5 +213,25 @@ class SessionTable(context: Context, databaseHelper: SignalDatabase) : DatabaseT } } + /** + * Given a set of serviceIds, this will give you back a filtered set of those ids that have any session with any of your identities. + * + * This was created for getting more debug info for a specific issue. + */ + fun findAllThatHaveAnySession(serviceIds: Set): Set { + val output: MutableSet = mutableSetOf() + + for (query in SqlUtil.buildCollectionQuery(ADDRESS, serviceIds.map { it.toString() })) { + output += readableDatabase + .select(ADDRESS) + .from(TABLE_NAME) + .where(query.where, query.whereArgs) + .run() + .readToSet { PNI.parseOrThrow(it.requireString(ADDRESS)) } + } + + return output + } + class SessionRow(val address: String, val deviceId: Int, val record: SessionRecord) }