Insert session switchover events when appropriate.

This commit is contained in:
Greyson Parrelli
2023-01-24 20:49:59 -05:00
parent 7745ae62ea
commit a7d9bd944b
10 changed files with 331 additions and 76 deletions

View File

@@ -73,6 +73,7 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.GroupCallUpdateD
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExportState;
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails;
import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent;
import org.thoughtcrime.securesms.database.model.databaseprotos.SessionSwitchoverEvent;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
import org.thoughtcrime.securesms.insights.InsightsConstants;
@@ -94,6 +95,7 @@ import org.thoughtcrime.securesms.sms.IncomingGroupUpdateMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.stories.Stories;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -1125,6 +1127,26 @@ public class MessageTable extends DatabaseTable implements MessageTypes, Recipie
ApplicationDependencies.getDatabaseObserver().notifyConversationListeners(threadId);
}
public void insertSessionSwitchoverEvent(@NonNull RecipientId recipientId, long threadId, @NonNull SessionSwitchoverEvent event) {
if (!FeatureFlags.phoneNumberPrivacy()) {
throw new IllegalStateException("Should not occur in a non-PNP world!");
}
ContentValues values = new ContentValues();
values.put(RECIPIENT_ID, recipientId.serialize());
values.put(RECIPIENT_DEVICE_ID, 1);
values.put(DATE_RECEIVED, System.currentTimeMillis());
values.put(DATE_SENT, System.currentTimeMillis());
values.put(READ, 1);
values.put(TYPE, MessageTypes.SESSION_SWITCHOVER_TYPE);
values.put(THREAD_ID, threadId);
values.put(BODY, Base64.encodeBytes(event.toByteArray()));
getWritableDatabase().insert(TABLE_NAME, null, values);
ApplicationDependencies.getDatabaseObserver().notifyConversationListeners(threadId);
}
public void insertSmsExportMessage(@NonNull RecipientId recipientId, long threadId) {
ContentValues values = new ContentValues();
values.put(RECIPIENT_ID, recipientId.serialize());

View File

@@ -50,6 +50,7 @@ public interface MessageTypes {
long BOOST_REQUEST_TYPE = 15;
long THREAD_MERGE_TYPE = 16;
long SMS_EXPORT_TYPE = 17;
long SESSION_SWITCHOVER_TYPE = 18;
long BASE_INBOX_TYPE = 20;
long BASE_OUTBOX_TYPE = 21;
@@ -207,6 +208,10 @@ public interface MessageTypes {
return (type & BASE_TYPE_MASK) == THREAD_MERGE_TYPE;
}
static boolean isSessionSwitchoverType(long type) {
return (type & BASE_TYPE_MASK) == SESSION_SWITCHOVER_TYPE;
}
static boolean isSecureType(long type) {
return (type & SECURE_MESSAGE_BIT) != 0;
}

View File

@@ -37,7 +37,7 @@ data class PnpDataSet(
* Applies the set of operations and returns the resulting dataset.
* Important: This only occurs _in memory_. You must still apply the operations to disk to persist them.
*/
fun perform(operations: List<PnpOperation>): PnpDataSet {
fun perform(operations: LinkedHashSet<PnpOperation>): PnpDataSet {
if (operations.isEmpty()) {
return this
}
@@ -136,7 +136,7 @@ data class PnpDataSet(
*/
data class PnpChangeSet(
val id: PnpIdResolver,
val operations: List<PnpOperation> = emptyList(),
val operations: LinkedHashSet<PnpOperation> = linkedSetOf(),
val breadCrumbs: List<String> = emptyList()
) {
// We want to exclude breadcrumbs from equality for testing purposes
@@ -213,7 +213,8 @@ sealed class PnpOperation {
}
data class SessionSwitchoverInsert(
override val recipientId: RecipientId
override val recipientId: RecipientId,
val e164: String?
) : PnpOperation()
data class ChangeNumberInsert(

View File

@@ -66,6 +66,7 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.ChatColor
import org.thoughtcrime.securesms.database.model.databaseprotos.DeviceLastResetTime
import org.thoughtcrime.securesms.database.model.databaseprotos.ExpiringProfileKeyCredentialColumnData
import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras
import org.thoughtcrime.securesms.database.model.databaseprotos.SessionSwitchoverEvent
import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent
import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
@@ -2367,7 +2368,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
affectedIds = affectedIds,
oldIds = oldIds,
changedNumberId = changedNumberId,
operations = changeSet.operations,
operations = changeSet.operations.toList(),
breadCrumbs = changeSet.breadCrumbs
)
}
@@ -2436,8 +2437,14 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
merge(operation.primaryId, operation.secondaryId, inputPni)
}
is PnpOperation.SessionSwitchoverInsert -> {
// TODO [pnp]
Log.w(TAG, "Session switchover events aren't implemented yet!")
val threadId: Long? = threads.getThreadIdFor(operation.recipientId)
if (threadId != null) {
val event = SessionSwitchoverEvent
.newBuilder()
.setE164(operation.e164 ?: "")
.build()
SignalDatabase.messages.insertSessionSwitchoverEvent(operation.recipientId, threadId, event)
}
}
is PnpOperation.ChangeNumberInsert -> {
if (changeSet.id is PnpIdResolver.PnpNoopId) {
@@ -2544,7 +2551,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
check(fullData.commonId == null)
check(listOfNotNull(fullData.byE164, fullData.byPniSid, fullData.byPniOnly, fullData.byAciSid).size >= 2)
val operations: MutableList<PnpOperation> = mutableListOf()
val operations: LinkedHashSet<PnpOperation> = linkedSetOf()
operations += processPossibleE164PniSidMerge(pni, pniVerified, fullData, breadCrumbs)
operations += processPossiblePniSidAciSidMerge(e164, pni, aci, fullData.perform(operations), changeSelf, breadCrumbs)
@@ -2559,6 +2566,13 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
recipientId = primaryId,
aci = aci
)
if (!pniVerified && finalData.pni != null && sessions.hasAnySessionFor(finalData.pni.toString())) {
operations += PnpOperation.SessionSwitchoverInsert(
recipientId = primaryId,
e164 = finalData.e164
)
}
}
if (finalData.byE164 == null && e164 != null && (changeSelf || notSelf(e164, pni, aci))) {
@@ -2599,7 +2613,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
private fun processNonMergePnpUpdate(e164: String?, pni: PNI?, aci: ACI?, pniVerified: Boolean, changeSelf: Boolean, commonId: RecipientId, breadCrumbs: MutableList<String>): PnpChangeSet {
val record: RecipientRecord = getRecord(commonId)
val operations: MutableList<PnpOperation> = mutableListOf()
val operations: LinkedHashSet<PnpOperation> = linkedSetOf()
// This is a special case. The ACI passed in doesn't match the common record. We can't change ACIs, so we need to make a new record.
if (aci != null && aci != record.serviceId && record.serviceId != null && !record.sidIsPni()) {
@@ -2658,7 +2672,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
val newServiceId: ServiceId? = aci ?: pni ?: record.serviceId
if (!pniVerified && record.serviceId != null && record.serviceId != newServiceId && sessions.hasAnySessionFor(record.serviceId.toString())) {
operations += PnpOperation.SessionSwitchoverInsert(commonId)
operations += PnpOperation.SessionSwitchoverInsert(recipientId = commonId, e164 = record.e164 ?: e164)
}
return PnpChangeSet(
@@ -2668,15 +2682,15 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
)
}
private fun processPossibleE164PniSidMerge(pni: PNI?, pniVerified: Boolean, data: PnpDataSet, breadCrumbs: MutableList<String>): List<PnpOperation> {
private fun processPossibleE164PniSidMerge(pni: PNI?, pniVerified: Boolean, data: PnpDataSet, breadCrumbs: MutableList<String>): LinkedHashSet<PnpOperation> {
if (pni == null || data.byE164 == null || data.byPniSid == null || data.e164Record == null || data.pniSidRecord == null || data.e164Record.id == data.pniSidRecord.id) {
return emptyList()
return linkedSetOf()
}
// We have found records for both the E164 and PNI, and they're different
breadCrumbs += "E164PniSidMerge"
val operations: MutableList<PnpOperation> = mutableListOf()
val operations: LinkedHashSet<PnpOperation> = linkedSetOf()
// The PNI record only has a single identifier. We know we must merge.
if (data.pniSidRecord.sidOnly(pni)) {
@@ -2704,31 +2718,35 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
)
if (!pniVerified && sessions.hasAnySessionFor(data.pniSidRecord.serviceId.toString())) {
operations += PnpOperation.SessionSwitchoverInsert(data.byPniSid)
operations += PnpOperation.SessionSwitchoverInsert(recipientId = data.byPniSid, e164 = data.pniSidRecord.e164)
if (data.e164Record.serviceId == null || data.e164Record.sidIsPni()) {
operations += PnpOperation.SessionSwitchoverInsert(recipientId = data.byE164, e164 = data.e164Record.e164)
}
}
if (!pniVerified && data.e164Record.serviceId != null && data.e164Record.sidIsPni() && sessions.hasAnySessionFor(data.e164Record.serviceId.toString())) {
operations += PnpOperation.SessionSwitchoverInsert(data.byE164)
operations += PnpOperation.SessionSwitchoverInsert(recipientId = data.byE164, e164 = data.e164)
}
}
return operations
}
private fun processPossiblePniSidAciSidMerge(e164: String?, pni: PNI?, aci: ACI?, data: PnpDataSet, changeSelf: Boolean, breadCrumbs: MutableList<String>): List<PnpOperation> {
private fun processPossiblePniSidAciSidMerge(e164: String?, pni: PNI?, aci: ACI?, data: PnpDataSet, changeSelf: Boolean, breadCrumbs: MutableList<String>): LinkedHashSet<PnpOperation> {
if (pni == null || aci == null || data.byPniSid == null || data.byAciSid == null || data.pniSidRecord == null || data.aciSidRecord == null || data.pniSidRecord.id == data.aciSidRecord.id) {
return emptyList()
return linkedSetOf()
}
if (!changeSelf && isSelf(e164, pni, aci)) {
breadCrumbs += "ChangeSelfPreventsPniSidAciSidMerge"
return emptyList()
return linkedSetOf()
}
// We have found records for both the PNI and ACI, and they're different
breadCrumbs += "PniSidAciSidMerge"
val operations: MutableList<PnpOperation> = mutableListOf()
val operations: LinkedHashSet<PnpOperation> = linkedSetOf()
// The PNI record only has a single identifier. We know we must merge.
if (data.pniSidRecord.sidOnly(pni)) {

View File

@@ -86,7 +86,7 @@ public final class FeatureFlags {
private static final String USE_HARDWARE_AEC_IF_OLD = "android.calling.useHardwareAecIfOlderThanApi29";
private static final String USE_AEC3 = "android.calling.useAec3";
private static final String PAYMENTS_COUNTRY_BLOCKLIST = "android.payments.blocklist";
private static final String PHONE_NUMBER_PRIVACY = "android.pnp";
public static final String PHONE_NUMBER_PRIVACY = "android.pnp";
private static final String USE_FCM_FOREGROUND_SERVICE = "android.useFcmForegroundService.3";
private static final String STORIES_AUTO_DOWNLOAD_MAXIMUM = "android.stories.autoDownloadMaximum";
private static final String TELECOM_MANUFACTURER_ALLOWLIST = "android.calling.telecomAllowList";