feat: preShareKey using fetchOrCreateGroupCall

fix: only fire backend.onNewParticipant and backend.onLeftParticipant when it is not the local participant, this fixes the issue where onNewParticipant would get triggered when it detects a new call even though you were not in the call, as of now there is no code in those functions which needs to be triggered before you have joined the call so this should be fine

chore: also improve participants join leave tracking logic
This commit is contained in:
td 2024-05-22 22:15:00 +05:30
parent f98a947181
commit fd6c7a1016
No known key found for this signature in database
GPG Key ID: 62A30523D4D6CE28
5 changed files with 63 additions and 36 deletions

View File

@ -73,6 +73,8 @@ abstract class CallBackend {
List<CallParticipant> anyLeft,
);
Future<void> preShareKey(GroupCallSession groupCall);
Future<void> requestEncrytionKey(
GroupCallSession groupCall,
List<CallParticipant> remoteParticipants,

View File

@ -52,9 +52,10 @@ class LiveKitBackend extends CallBackend {
return newIndex;
}
@override
Future<void> preShareKey(GroupCallSession groupCall) async {
await groupCall.onMemberStateChanged();
await _makeNewSenderKey(groupCall, false);
await _changeEncryptionKey(groupCall, groupCall.participants, false);
}
/// makes a new e2ee key for local user and sets it with a delay if specified
@ -120,6 +121,19 @@ class LiveKitBackend extends CallBackend {
);
}
Future<void> _changeEncryptionKey(
GroupCallSession groupCall,
List<CallParticipant> anyJoined,
bool delayBeforeUsingKeyOurself,
) async {
if (!e2eeEnabled) return;
if (groupCall.voip.enableSFUE2EEKeyRatcheting) {
await _ratchetLocalParticipantKey(groupCall, anyJoined);
} else {
await _makeNewSenderKey(groupCall, delayBeforeUsingKeyOurself);
}
}
/// sets incoming keys and also sends the key if it was for the local user
/// if sendTo is null, its sent to all _participants, see `_sendEncryptionKeysEvent`
Future<void> _setEncryptionKey(
@ -180,8 +194,6 @@ class LiveKitBackend extends CallBackend {
int keyIndex, {
List<CallParticipant>? sendTo,
}) async {
Logs().i('Sending encryption keys event');
final myKeys = _getKeysForParticipant(groupCall.localParticipant!);
final myLatestKey = myKeys?[keyIndex];
@ -235,6 +247,7 @@ class LiveKitBackend extends CallBackend {
Map<String, Object> data,
String eventType,
) async {
if (remoteParticipants.isEmpty) return;
Logs().v(
'[VOIP] _sendToDeviceEvent: sending ${data.toString()} to ${remoteParticipants.map((e) => e.id)} ');
final txid =
@ -392,14 +405,8 @@ class LiveKitBackend extends CallBackend {
Future<void> onNewParticipant(
GroupCallSession groupCall,
List<CallParticipant> anyJoined,
) async {
if (!e2eeEnabled) return;
if (groupCall.voip.enableSFUE2EEKeyRatcheting) {
await _ratchetLocalParticipantKey(groupCall, anyJoined);
} else {
await _makeNewSenderKey(groupCall, true);
}
}
) =>
_changeEncryptionKey(groupCall, anyJoined, true);
@override
Future<void> onLeftParticipant(

View File

@ -877,4 +877,9 @@ class MeshBackend extends CallBackend {
List<CallParticipant> remoteParticipants) async {
return;
}
@override
Future<void> preShareKey(GroupCallSession groupCall) async {
return;
}
}

View File

@ -48,7 +48,7 @@ class GroupCallSession {
CallParticipant? get localParticipant => voip.localParticipant;
List<CallParticipant> get participants => List.unmodifiable(_participants);
final List<CallParticipant> _participants = [];
final Set<CallParticipant> _participants = {};
String groupCallId;
@ -218,7 +218,7 @@ class GroupCallSession {
'[VOIP] Ignored ${mem.userId}\'s mem event ${mem.toJson()} while updating _participants list for callId: $groupCallId, expiry status: ${mem.isExpired}');
}
final List<CallParticipant> newP = [];
final Set<CallParticipant> newP = {};
for (final mem in memsForCurrentGroupCall) {
final rp = CallParticipant(
@ -239,23 +239,29 @@ class GroupCallSession {
await backend.setupP2PCallWithNewMember(this, rp, mem);
}
final newPcopy = List<CallParticipant>.from(newP);
final oldPcopy = List<CallParticipant>.from(_participants);
final anyJoined = newPcopy.where((element) => !oldPcopy.contains(element));
final anyLeft = oldPcopy.where((element) => !newPcopy.contains(element));
final newPcopy = Set<CallParticipant>.from(newP);
final oldPcopy = Set<CallParticipant>.from(_participants);
final anyJoined = newPcopy.difference(oldPcopy);
final anyLeft = oldPcopy.difference(newPcopy);
if (anyJoined.isNotEmpty || anyLeft.isNotEmpty) {
if (anyJoined.isNotEmpty) {
Logs().d('anyJoined: ${anyJoined.map((e) => e.id).toString()}');
final nonLocalAnyJoined = anyJoined..remove(localParticipant);
if (nonLocalAnyJoined.isNotEmpty && state == GroupCallState.entered) {
Logs().v(
'nonLocalAnyJoined: ${nonLocalAnyJoined.map((e) => e.id).toString()} roomId: ${room.id} groupCallId: $groupCallId');
await backend.onNewParticipant(this, nonLocalAnyJoined.toList());
}
_participants.addAll(anyJoined);
await backend.onNewParticipant(this, anyJoined.toList());
}
if (anyLeft.isNotEmpty) {
Logs().d('anyLeft: ${anyLeft.map((e) => e.id).toString()}');
for (final leftp in anyLeft) {
_participants.remove(leftp);
final nonLocalAnyLeft = anyLeft..remove(localParticipant);
if (nonLocalAnyLeft.isNotEmpty && state == GroupCallState.entered) {
Logs().v(
'nonLocalAnyLeft: ${nonLocalAnyLeft.map((e) => e.id).toString()} roomId: ${room.id} groupCallId: $groupCallId');
await backend.onLeftParticipant(this, nonLocalAnyLeft.toList());
}
await backend.onLeftParticipant(this, anyLeft.toList());
_participants.removeAll(anyLeft);
}
onGroupCallEvent.add(GroupCallStateChange.participantsChanged);

View File

@ -756,37 +756,44 @@ class VoIP {
/// [application] normal group call, thrirdroom, etc
///
/// [scope] room, between specifc users, etc.
///
/// [preShareKey] for livekit calls it creates and shares a key with other
/// participants in the call without entering, useful on onboarding screens.
/// does not do anything in mesh calls
Future<GroupCallSession> fetchOrCreateGroupCall(
String groupCallId,
Room room,
CallBackend backend,
String? application,
String? scope,
) async {
String? scope, {
bool preShareKey = true,
}) async {
if (!room.groupCallsEnabledForEveryone) {
await room.enableGroupCalls();
}
final groupCall = getGroupCallById(room.id, groupCallId);
if (groupCall != null) {
if (!room.canJoinGroupCall) {
throw MatrixSDKVoipException(
'User ${client.userID}:${client.deviceID} is not allowed to join famedly calls in room ${room.id}, canJoinGroupCall: ${room.canJoinGroupCall}, room.canJoinGroupCall: ${room.groupCallsEnabledForEveryone}',
);
}
return groupCall;
}
// The call doesn't exist, but we can create it
return await _newGroupCall(
GroupCallSession? groupCall = getGroupCallById(room.id, groupCallId);
groupCall ??= await _newGroupCall(
groupCallId,
room,
backend,
application,
scope,
);
if (preShareKey) {
await groupCall.backend.preShareKey(groupCall);
}
return groupCall;
}
GroupCallSession? getGroupCallById(String roomId, String groupCallId) {