Fix a couple of race conditions when observing room info updates for calls. (#3487)

* Fix a race condition observing room info updates for calls.

* Fix a bug where call observation wasn't set up if the call comes when the app has been killed.
This commit is contained in:
Doug
2024-11-05 17:48:07 +00:00
committed by GitHub
parent cb47cb810e
commit 0da8e7af58

View File

@@ -35,14 +35,19 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe
return CXProvider(configuration: configuration)
}()
private weak var clientProxy: ClientProxyProtocol?
private weak var clientProxy: ClientProxyProtocol? {
didSet {
// There's a race condition where a call starts when the app has been killed and the
// observation set in `incomingCallID` occurs *before* the user session is restored.
// So observe when the client proxy is set to fix this (the method guards for the call).
Task { await observeIncomingCallRoomInfo() }
}
}
private var cancellables = Set<AnyCancellable>()
private var incomingCallRoomInfoCancellable: AnyCancellable?
private var incomingCallID: CallID? {
didSet {
Task {
await observeIncomingCallRoomStateUpdates()
}
Task { await observeIncomingCallRoomInfo() }
}
}
@@ -260,7 +265,7 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe
// MARK: - Private
func tearDownCallSession(sendEndCallAction: Bool = true) {
private func tearDownCallSession(sendEndCallAction: Bool = true) {
if sendEndCallAction, let ongoingCallID {
let transaction = CXTransaction(action: CXEndCallAction(call: ongoingCallID.callKitID))
callController.request(transaction) { error in
@@ -273,30 +278,35 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe
ongoingCallID = nil
}
func observeIncomingCallRoomStateUpdates() async {
cancellables.removeAll()
private func observeIncomingCallRoomInfo() async {
incomingCallRoomInfoCancellable = nil
guard let clientProxy, let incomingCallID else {
guard let incomingCallID else {
MXLog.info("No incoming call to observe for.")
return
}
guard let clientProxy else {
MXLog.warning("A ClientProxy is needed to fetch the room.")
return
}
guard case let .joined(roomProxy) = await clientProxy.roomForIdentifier(incomingCallID.roomID) else {
MXLog.warning("Failed to fetch a joined room for the incoming call.")
return
}
roomProxy.subscribeToRoomInfoUpdates()
// There's no incoming event for call cancellations so try to infer
// it from what we have. If the call is running before subscribing then wait
// for it to change to `false` otherwise wait for it to turn `true` before
// changing to `false`
let isCallOngoing = roomProxy.infoPublisher.value.hasRoomCall
roomProxy
incomingCallRoomInfoCancellable = roomProxy
.infoPublisher
.compactMap { ($0.hasRoomCall, $0.activeRoomCallParticipants) }
.removeDuplicates { $0 == $1 }
.dropFirst(isCallOngoing ? 0 : 1)
.drop(while: { hasRoomCall, _ in
// Filter all updates before hasRoomCall becomes `true`. Then we can correctly
// detect its change to `false` to stop ringing when the caller hangs up.
!hasRoomCall
})
.sink { [weak self] hasOngoingCall, activeRoomCallParticipants in
guard let self else { return }
@@ -305,17 +315,16 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe
if !hasOngoingCall {
MXLog.info("Call cancelled by remote")
cancellables.removeAll()
incomingCallRoomInfoCancellable = nil
endUnansweredCallTask?.cancel()
callProvider.reportCall(with: incomingCallID.callKitID, endedAt: nil, reason: .remoteEnded)
} else if participants.contains(roomProxy.ownUserID) {
MXLog.info("Call anwered elsewhere")
MXLog.info("Call answered elsewhere")
cancellables.removeAll()
incomingCallRoomInfoCancellable = nil
endUnansweredCallTask?.cancel()
callProvider.reportCall(with: incomingCallID.callKitID, endedAt: nil, reason: .answeredElsewhere)
}
}
.store(in: &cancellables)
}
}