From fd6c7a10161e9926623fa2fdd6f02c5d43eeaa4e Mon Sep 17 00:00:00 2001 From: td Date: Wed, 22 May 2024 22:15:00 +0530 Subject: [PATCH] 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 --- lib/src/voip/backend/call_backend_model.dart | 2 ++ lib/src/voip/backend/livekit_backend.dart | 29 ++++++++++------- lib/src/voip/backend/mesh_backend.dart | 5 +++ lib/src/voip/group_call_session.dart | 30 +++++++++++------- lib/src/voip/voip.dart | 33 ++++++++++++-------- 5 files changed, 63 insertions(+), 36 deletions(-) diff --git a/lib/src/voip/backend/call_backend_model.dart b/lib/src/voip/backend/call_backend_model.dart index 814d87ce..40607f77 100644 --- a/lib/src/voip/backend/call_backend_model.dart +++ b/lib/src/voip/backend/call_backend_model.dart @@ -73,6 +73,8 @@ abstract class CallBackend { List anyLeft, ); + Future preShareKey(GroupCallSession groupCall); + Future requestEncrytionKey( GroupCallSession groupCall, List remoteParticipants, diff --git a/lib/src/voip/backend/livekit_backend.dart b/lib/src/voip/backend/livekit_backend.dart index 87238c31..13ad6baf 100644 --- a/lib/src/voip/backend/livekit_backend.dart +++ b/lib/src/voip/backend/livekit_backend.dart @@ -52,9 +52,10 @@ class LiveKitBackend extends CallBackend { return newIndex; } + @override Future 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 _changeEncryptionKey( + GroupCallSession groupCall, + List 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 _setEncryptionKey( @@ -180,8 +194,6 @@ class LiveKitBackend extends CallBackend { int keyIndex, { List? 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 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 onNewParticipant( GroupCallSession groupCall, List anyJoined, - ) async { - if (!e2eeEnabled) return; - if (groupCall.voip.enableSFUE2EEKeyRatcheting) { - await _ratchetLocalParticipantKey(groupCall, anyJoined); - } else { - await _makeNewSenderKey(groupCall, true); - } - } + ) => + _changeEncryptionKey(groupCall, anyJoined, true); @override Future onLeftParticipant( diff --git a/lib/src/voip/backend/mesh_backend.dart b/lib/src/voip/backend/mesh_backend.dart index 4a0cb47c..150cba6a 100644 --- a/lib/src/voip/backend/mesh_backend.dart +++ b/lib/src/voip/backend/mesh_backend.dart @@ -877,4 +877,9 @@ class MeshBackend extends CallBackend { List remoteParticipants) async { return; } + + @override + Future preShareKey(GroupCallSession groupCall) async { + return; + } } diff --git a/lib/src/voip/group_call_session.dart b/lib/src/voip/group_call_session.dart index 71eccf32..77e5bad7 100644 --- a/lib/src/voip/group_call_session.dart +++ b/lib/src/voip/group_call_session.dart @@ -48,7 +48,7 @@ class GroupCallSession { CallParticipant? get localParticipant => voip.localParticipant; List get participants => List.unmodifiable(_participants); - final List _participants = []; + final Set _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 newP = []; + final Set newP = {}; for (final mem in memsForCurrentGroupCall) { final rp = CallParticipant( @@ -239,23 +239,29 @@ class GroupCallSession { await backend.setupP2PCallWithNewMember(this, rp, mem); } - final newPcopy = List.from(newP); - final oldPcopy = List.from(_participants); - final anyJoined = newPcopy.where((element) => !oldPcopy.contains(element)); - final anyLeft = oldPcopy.where((element) => !newPcopy.contains(element)); + final newPcopy = Set.from(newP); + final oldPcopy = Set.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); diff --git a/lib/src/voip/voip.dart b/lib/src/voip/voip.dart index 1b78e3d4..7a752c04 100644 --- a/lib/src/voip/voip.dart +++ b/lib/src/voip/voip.dart @@ -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 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; + 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}', + ); } - // 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) {