Fix group call flickering missed.

This commit is contained in:
Alex Hart
2024-07-17 16:01:04 -03:00
committed by Greyson Parrelli
parent 3c10966a36
commit d424a60345
11 changed files with 224 additions and 57 deletions

View File

@@ -605,22 +605,6 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
return handleCallLinkUpdate(callRecipient, timestamp, CallId.fromEra(eraId), Direction.INCOMING)
}
fun insertOrUpdateGroupCallFromExternalEvent(
groupRecipientId: RecipientId,
sender: RecipientId,
timestamp: Long,
messageGroupCallEraId: String?
) {
insertOrUpdateGroupCallFromLocalEvent(
groupRecipientId,
sender,
timestamp,
messageGroupCallEraId,
emptyList(),
false
)
}
fun insertOrUpdateGroupCallFromLocalEvent(
groupRecipientId: RecipientId,
sender: RecipientId,

View File

@@ -44,7 +44,7 @@ public class JobManager implements ConstraintObserver.Notifier {
private static final String TAG = Log.tag(JobManager.class);
public static final int CURRENT_VERSION = 11;
public static final int CURRENT_VERSION = 12;
private final Application application;
private final Configuration configuration;

View File

@@ -50,6 +50,18 @@ public class JsonJobData {
}
}
public static @Nullable JsonJobData deserializeOrNull(@Nullable byte[] data) {
if (data == null) {
return null;
}
try {
return JsonUtils.fromJson(data, JsonJobData.class);
} catch (IOException e) {
return null;
}
}
private JsonJobData(@JsonProperty("strings") @NonNull Map<String, String> strings,
@JsonProperty("stringArrays") @NonNull Map<String, String[]> stringArrays,
@JsonProperty("integers") @NonNull Map<String, Integer> integers,

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.jobmanager.migrations
import org.thoughtcrime.securesms.jobmanager.JobMigration
import org.thoughtcrime.securesms.jobmanager.JsonJobData
import org.thoughtcrime.securesms.jobs.protos.GroupCallPeekJobData
import org.thoughtcrime.securesms.recipients.RecipientId
/**
* Migrate jobs with just the recipient id to utilize the new data proto.
*/
class GroupCallPeekJobDataMigration : JobMigration(12) {
companion object {
private const val KEY_GROUP_RECIPIENT_ID: String = "group_recipient_id"
private val GROUP_PEEK_JOB_KEYS = arrayOf("GroupCallPeekJob", "GroupCallPeekWorkerJob")
}
override fun migrate(jobData: JobData): JobData {
if (jobData.factoryKey !in GROUP_PEEK_JOB_KEYS) {
return jobData
}
val data = jobData.data ?: return jobData
val jsonData = JsonJobData.deserializeOrNull(data) ?: return jobData
val recipientId = jsonData.getStringOrDefault(KEY_GROUP_RECIPIENT_ID, null) ?: return jobData
val jobProto = GroupCallPeekJobData(
groupRecipientId = recipientId.toLong(),
senderRecipientId = RecipientId.UNKNOWN.toLong(),
serverTimestamp = 0L
)
return jobData.withData(jobProto.encode())
}
}

View File

@@ -4,11 +4,13 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.impl.DecryptionsDrainedConstraint;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.jobs.protos.GroupCallPeekJobData;
import java.io.IOException;
/**
* Allows the enqueueing of one peek operation per group while the web socket is not drained.
@@ -19,32 +21,30 @@ public final class GroupCallPeekJob extends BaseJob {
private static final String QUEUE = "__GroupCallPeekJob__";
private static final String KEY_GROUP_RECIPIENT_ID = "group_recipient_id";
@NonNull private final GroupCallPeekJobData groupCallPeekJobData;
@NonNull private final RecipientId groupRecipientId;
public static void enqueue(@NonNull RecipientId groupRecipientId) {
public static void enqueue(@NonNull GroupCallPeekJobData groupCallPeekJobData) {
JobManager jobManager = AppDependencies.getJobManager();
String queue = QUEUE + groupRecipientId.serialize();
String queue = QUEUE + groupCallPeekJobData.groupRecipientId;
Parameters.Builder parameters = new Parameters.Builder()
.setQueue(queue)
.addConstraint(DecryptionsDrainedConstraint.KEY);
jobManager.cancelAllInQueue(queue);
jobManager.add(new GroupCallPeekJob(parameters.build(), groupRecipientId));
jobManager.add(new GroupCallPeekJob(parameters.build(), groupCallPeekJobData));
}
private GroupCallPeekJob(@NonNull Parameters parameters,
@NonNull RecipientId groupRecipientId)
@NonNull GroupCallPeekJobData groupCallPeekJobData)
{
super(parameters);
this.groupRecipientId = groupRecipientId;
this.groupCallPeekJobData = groupCallPeekJobData;
}
@Override
protected void onRun() {
AppDependencies.getJobManager().add(new GroupCallPeekWorkerJob(groupRecipientId));
AppDependencies.getJobManager().add(new GroupCallPeekWorkerJob(groupCallPeekJobData));
}
@Override
@@ -53,10 +53,8 @@ public final class GroupCallPeekJob extends BaseJob {
}
@Override
public @Nullable byte[] serialize() {
return new JsonJobData.Builder()
.putString(KEY_GROUP_RECIPIENT_ID, groupRecipientId.serialize())
.serialize();
public @NonNull byte[] serialize() {
return groupCallPeekJobData.encode();
}
@Override
@@ -72,8 +70,12 @@ public final class GroupCallPeekJob extends BaseJob {
@Override
public @NonNull GroupCallPeekJob create(@NonNull Parameters parameters, @Nullable byte[] serializedData) {
JsonJobData data = JsonJobData.deserialize(serializedData);
return new GroupCallPeekJob(parameters, RecipientId.from(data.getString(KEY_GROUP_RECIPIENT_ID)));
try {
GroupCallPeekJobData jobData = GroupCallPeekJobData.ADAPTER.decode(serializedData);
return new GroupCallPeekJob(parameters, jobData);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

View File

@@ -3,10 +3,15 @@ package org.thoughtcrime.securesms.jobs;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobs.protos.GroupCallPeekJobData;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.service.webrtc.WebRtcUtil;
import java.io.IOException;
/**
* Runs in the same queue as messages for the group.
@@ -15,26 +20,42 @@ final class GroupCallPeekWorkerJob extends BaseJob {
public static final String KEY = "GroupCallPeekWorkerJob";
private static final String KEY_GROUP_RECIPIENT_ID = "group_recipient_id";
private static final String KEY_GROUP_CALL_JOB_DATA = "group_call_job_data";
@NonNull private final RecipientId groupRecipientId;
@NonNull private final GroupCallPeekJobData groupCallPeekJobData;
public GroupCallPeekWorkerJob(@NonNull RecipientId groupRecipientId) {
public GroupCallPeekWorkerJob(@NonNull GroupCallPeekJobData groupCallPeekJobData) {
this(new Parameters.Builder()
.setQueue(PushProcessMessageJob.getQueueName(groupRecipientId))
.setQueue(PushProcessMessageJob.getQueueName(RecipientId.from(groupCallPeekJobData.groupRecipientId)))
.setMaxInstancesForQueue(2)
.build(),
groupRecipientId);
groupCallPeekJobData);
}
private GroupCallPeekWorkerJob(@NonNull Parameters parameters, @NonNull RecipientId groupRecipientId) {
private GroupCallPeekWorkerJob(@NonNull Parameters parameters, @NonNull GroupCallPeekJobData groupCallPeekJobData) {
super(parameters);
this.groupRecipientId = groupRecipientId;
this.groupCallPeekJobData = groupCallPeekJobData;
}
@Override
protected void onRun() {
AppDependencies.getSignalCallManager().peekGroupCall(groupRecipientId);
RecipientId groupRecipientId = RecipientId.from(groupCallPeekJobData.groupRecipientId);
AppDependencies.getSignalCallManager().peekGroupCall(groupRecipientId, (peekInfo) -> {
if (groupCallPeekJobData.senderRecipientId == RecipientId.UNKNOWN.toLong()) {
return;
}
RecipientId senderRecipientId = RecipientId.from(groupCallPeekJobData.senderRecipientId);
SignalDatabase.calls().insertOrUpdateGroupCallFromLocalEvent(
groupRecipientId,
senderRecipientId,
groupCallPeekJobData.serverTimestamp,
peekInfo.getEraId(),
peekInfo.getJoinedMembers(),
WebRtcUtil.isCallFull(peekInfo)
);
});
}
@Override
@@ -43,10 +64,8 @@ final class GroupCallPeekWorkerJob extends BaseJob {
}
@Override
public @Nullable byte[] serialize() {
return new JsonJobData.Builder()
.putString(KEY_GROUP_RECIPIENT_ID, groupRecipientId.serialize())
.serialize();
public @NonNull byte[] serialize() {
return groupCallPeekJobData.encode();
}
@Override
@@ -62,8 +81,12 @@ final class GroupCallPeekWorkerJob extends BaseJob {
@Override
public @NonNull GroupCallPeekWorkerJob create(@NonNull Parameters parameters, @Nullable byte[] serializedData) {
JsonJobData data = JsonJobData.deserialize(serializedData);
return new GroupCallPeekWorkerJob(parameters, RecipientId.from(data.getString(KEY_GROUP_RECIPIENT_ID)));
try {
GroupCallPeekJobData jobData = GroupCallPeekJobData.ADAPTER.decode(serializedData);
return new GroupCallPeekWorkerJob(parameters, jobData);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

View File

@@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver;
import org.thoughtcrime.securesms.jobmanager.impl.WifiConstraint;
import org.thoughtcrime.securesms.jobmanager.migrations.DonationReceiptRedemptionJobMigration;
import org.thoughtcrime.securesms.jobmanager.migrations.GroupCallPeekJobDataMigration;
import org.thoughtcrime.securesms.jobmanager.migrations.PushDecryptMessageJobEnvelopeMigration;
import org.thoughtcrime.securesms.jobmanager.migrations.PushProcessMessageJobMigration;
import org.thoughtcrime.securesms.jobmanager.migrations.PushProcessMessageQueueJobMigration;
@@ -380,6 +381,7 @@ public final class JobManagerFactories {
new PushDecryptMessageJobEnvelopeMigration(),
new SenderKeyDistributionSendJobRecipientMigration(),
new PushProcessMessageJobMigration(),
new DonationReceiptRedemptionJobMigration());
new DonationReceiptRedemptionJobMigration(),
new GroupCallPeekJobDataMigration());
}
}

View File

@@ -58,6 +58,7 @@ import org.thoughtcrime.securesms.jobs.RefreshAttributesJob
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob
import org.thoughtcrime.securesms.jobs.TrimThreadJob
import org.thoughtcrime.securesms.jobs.protos.GroupCallPeekJobData
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.linkpreview.LinkPreview
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil
@@ -1016,14 +1017,13 @@ object DataMessageProcessor {
val groupRecipientId = SignalDatabase.recipients.getOrInsertFromPossiblyMigratedGroupId(groupId)
SignalDatabase.calls.insertOrUpdateGroupCallFromExternalEvent(
groupRecipientId,
senderRecipientId,
envelope.serverTimestamp!!,
groupCallUpdate.eraId
GroupCallPeekJob.enqueue(
GroupCallPeekJobData(
groupRecipientId.toLong(),
senderRecipientId.toLong(),
envelope.serverTimestamp!!
)
)
GroupCallPeekJob.enqueue(groupRecipientId)
}
fun notifyTypingStoppedFromIncomingMessage(context: Context, senderRecipient: Recipient, threadRecipientId: RecipientId, device: Int) {

View File

@@ -103,6 +103,7 @@ import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import io.reactivex.rxjava3.core.Flowable;
@@ -446,6 +447,10 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
}
public void peekGroupCall(@NonNull RecipientId id) {
peekGroupCall(id, null);
}
public void peekGroupCall(@NonNull RecipientId id, @Nullable Consumer<PeekInfo> onWillUpdateCallFromPeek) {
if (callManager == null) {
Log.i(TAG, "Unable to peekGroupCall, call manager is null");
return;
@@ -464,6 +469,10 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
Long threadId = SignalDatabase.threads().getThreadIdFor(group.getId());
if (threadId != null) {
if (onWillUpdateCallFromPeek != null) {
onWillUpdateCallFromPeek.accept(peekInfo);
}
SignalDatabase.calls()
.updateGroupCallFromPeek(threadId,
peekInfo.getEraId(),