mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Conference calls
This commit is contained in:
@@ -4,7 +4,6 @@ import MtProtoKit
|
||||
import SwiftSignalKit
|
||||
import TelegramApi
|
||||
|
||||
|
||||
private let minLayer: Int32 = 65
|
||||
|
||||
public enum CallSessionError: Equatable {
|
||||
@@ -15,11 +14,11 @@ public enum CallSessionError: Equatable {
|
||||
case disconnected
|
||||
}
|
||||
|
||||
public enum CallSessionEndedType {
|
||||
public enum CallSessionEndedType: Equatable {
|
||||
case hungUp
|
||||
case busy
|
||||
case missed
|
||||
case switchedToConference
|
||||
case switchedToConference(slug: String)
|
||||
}
|
||||
|
||||
public enum CallSessionTerminationReason: Equatable {
|
||||
@@ -65,41 +64,47 @@ public struct GroupCallReference: Equatable {
|
||||
}
|
||||
|
||||
extension GroupCallReference {
|
||||
init(_ apiGroupCall: Api.InputGroupCall) {
|
||||
init?(_ apiGroupCall: Api.InputGroupCall) {
|
||||
switch apiGroupCall {
|
||||
case let .inputGroupCall(id, accessHash):
|
||||
self.init(id: id, accessHash: accessHash)
|
||||
case .inputGroupCallSlug, .inputGroupCallInviteMessage:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var apiInputGroupCall: Api.InputGroupCall {
|
||||
return .inputGroupCall(id: self.id, accessHash: self.accessHash)
|
||||
}
|
||||
}
|
||||
|
||||
enum CallSessionInternalState {
|
||||
case ringing(id: Int64, accessHash: Int64, gAHash: Data, b: Data, versions: [String], conferenceCall: GroupCallReference?)
|
||||
case accepting(id: Int64, accessHash: Int64, gAHash: Data, b: Data, conferenceCall: GroupCallReference?, disposable: Disposable)
|
||||
case ringing(id: Int64, accessHash: Int64, gAHash: Data, b: Data, versions: [String])
|
||||
case accepting(id: Int64, accessHash: Int64, gAHash: Data, b: Data, disposable: Disposable)
|
||||
case awaitingConfirmation(id: Int64, accessHash: Int64, gAHash: Data, b: Data, config: SecretChatEncryptionConfig)
|
||||
case requesting(a: Data, conferenceCall: GroupCallReference?, disposable: Disposable)
|
||||
case requested(id: Int64, accessHash: Int64, a: Data, gA: Data, config: SecretChatEncryptionConfig, remoteConfirmationTimestamp: Int32?, conferenceCall: GroupCallReference?)
|
||||
case confirming(id: Int64, accessHash: Int64, key: Data, keyId: Int64, keyVisualHash: Data, conferenceCall: GroupCallReference?, disposable: Disposable)
|
||||
case active(id: Int64, accessHash: Int64, beginTimestamp: Int32, key: Data, keyId: Int64, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, conferenceCall: GroupCallReference?, willSwitchToConference: Bool)
|
||||
case switchedToConference(key: Data, keyVisualHash: Data, conferenceCall: GroupCallReference)
|
||||
case requesting(a: Data, disposable: Disposable)
|
||||
case requested(id: Int64, accessHash: Int64, a: Data, gA: Data, config: SecretChatEncryptionConfig, remoteConfirmationTimestamp: Int32?)
|
||||
case confirming(id: Int64, accessHash: Int64, key: Data, keyId: Int64, keyVisualHash: Data, disposable: Disposable)
|
||||
case active(id: Int64, accessHash: Int64, beginTimestamp: Int32, key: Data, keyId: Int64, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool)
|
||||
case switchedToConference(slug: String)
|
||||
case dropping(reason: CallSessionTerminationReason, disposable: Disposable)
|
||||
case terminated(id: Int64?, accessHash: Int64?, reason: CallSessionTerminationReason, reportRating: Bool, sendDebugLogs: Bool)
|
||||
|
||||
var stableId: Int64? {
|
||||
switch self {
|
||||
case let .ringing(id, _, _, _, _, _):
|
||||
case let .ringing(id, _, _, _, _):
|
||||
return id
|
||||
case let .accepting(id, _, _, _, _, _):
|
||||
case let .accepting(id, _, _, _, _):
|
||||
return id
|
||||
case let .awaitingConfirmation(id, _, _, _, _):
|
||||
return id
|
||||
case .requesting:
|
||||
return nil
|
||||
case let .requested(id, _, _, _, _, _, _):
|
||||
case let .requested(id, _, _, _, _, _):
|
||||
return id
|
||||
case let .confirming(id, _, _, _, _, _, _):
|
||||
case let .confirming(id, _, _, _, _, _):
|
||||
return id
|
||||
case let .active(id, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .active(id, _, _, _, _, _, _, _, _, _, _):
|
||||
return id
|
||||
case .switchedToConference:
|
||||
return nil
|
||||
@@ -138,7 +143,8 @@ public struct CallSessionRingingState: Equatable {
|
||||
public let peerId: PeerId
|
||||
public let isVideo: Bool
|
||||
public let isVideoPossible: Bool
|
||||
public let isIncomingConference: Bool
|
||||
public let conferenceSource: MessageId?
|
||||
public let otherParticipants: [EnginePeer]
|
||||
}
|
||||
|
||||
public enum DropCallReason {
|
||||
@@ -166,9 +172,9 @@ public struct CallTerminationOptions: OptionSet {
|
||||
public enum CallSessionState {
|
||||
case ringing
|
||||
case accepting
|
||||
case requesting(ringing: Bool, conferenceCall: GroupCallReference?)
|
||||
case active(id: CallId, key: Data, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, conferenceCall: GroupCallReference?, willSwitchToConference: Bool)
|
||||
case switchedToConference(key: Data, keyVisualHash: Data, conferenceCall: GroupCallReference)
|
||||
case requesting(ringing: Bool)
|
||||
case active(id: CallId, key: Data, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool)
|
||||
case switchedToConference(slug: String)
|
||||
case dropping(reason: CallSessionTerminationReason)
|
||||
case terminated(id: CallId?, reason: CallSessionTerminationReason, options: CallTerminationOptions)
|
||||
|
||||
@@ -178,33 +184,37 @@ public enum CallSessionState {
|
||||
self = .ringing
|
||||
case .accepting, .awaitingConfirmation:
|
||||
self = .accepting
|
||||
case let .requesting(_, conferenceCall, _):
|
||||
self = .requesting(ringing: false, conferenceCall: conferenceCall)
|
||||
case let .confirming(_, _, _, _, _, conferenceCall, _):
|
||||
self = .requesting(ringing: true, conferenceCall: conferenceCall)
|
||||
case let .requested(_, _, _, _, _, remoteConfirmationTimestamp, conferenceCall):
|
||||
self = .requesting(ringing: remoteConfirmationTimestamp != nil, conferenceCall: conferenceCall)
|
||||
case let .active(id, accessHash, _, key, _, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P, conferenceCall, willSwitchToConference):
|
||||
self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, conferenceCall: conferenceCall, willSwitchToConference: willSwitchToConference)
|
||||
case .requesting:
|
||||
self = .requesting(ringing: false)
|
||||
case .confirming:
|
||||
self = .requesting(ringing: true)
|
||||
case let .requested(_, _, _, _, _, remoteConfirmationTimestamp):
|
||||
self = .requesting(ringing: remoteConfirmationTimestamp != nil)
|
||||
case let .active(id, accessHash, _, key, _, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P):
|
||||
self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P)
|
||||
case let .dropping(reason, _):
|
||||
self = .dropping(reason: reason)
|
||||
case let .terminated(id, accessHash, reason, reportRating, sendDebugLogs):
|
||||
var options = CallTerminationOptions()
|
||||
if reportRating {
|
||||
options.insert(.reportRating)
|
||||
}
|
||||
if sendDebugLogs {
|
||||
options.insert(.sendDebugLogs)
|
||||
}
|
||||
let callId: CallId?
|
||||
if let id = id, let accessHash = accessHash {
|
||||
callId = CallId(id: id, accessHash: accessHash)
|
||||
if case let .ended(endedReason) = reason, case let .switchedToConference(slug) = endedReason {
|
||||
self = .switchedToConference(slug: slug)
|
||||
} else {
|
||||
callId = nil
|
||||
var options = CallTerminationOptions()
|
||||
if reportRating {
|
||||
options.insert(.reportRating)
|
||||
}
|
||||
if sendDebugLogs {
|
||||
options.insert(.sendDebugLogs)
|
||||
}
|
||||
let callId: CallId?
|
||||
if let id = id, let accessHash = accessHash {
|
||||
callId = CallId(id: id, accessHash: accessHash)
|
||||
} else {
|
||||
callId = nil
|
||||
}
|
||||
self = .terminated(id: callId, reason: reason, options: options)
|
||||
}
|
||||
self = .terminated(id: callId, reason: reason, options: options)
|
||||
case let .switchedToConference(key, keyVisualHash, conferenceCall):
|
||||
self = .switchedToConference(key: key, keyVisualHash: keyVisualHash, conferenceCall: conferenceCall)
|
||||
case let .switchedToConference(slug):
|
||||
self = .switchedToConference(slug: slug)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,7 +232,7 @@ public struct CallSession {
|
||||
public let state: CallSessionState
|
||||
public let isVideoPossible: Bool
|
||||
|
||||
init(
|
||||
public init(
|
||||
id: CallSessionInternalId,
|
||||
stableId: Int64?,
|
||||
isOutgoing: Bool,
|
||||
@@ -336,17 +346,15 @@ private func parseConnectionSet(primary: Api.PhoneConnection, alternative: [Api.
|
||||
private final class CallSessionContext {
|
||||
let peerId: PeerId
|
||||
let isOutgoing: Bool
|
||||
let isIncomingConference: Bool
|
||||
var type: CallSession.CallType
|
||||
var isVideoPossible: Bool
|
||||
let pendingConference: (conference: GroupCallReference, encryptionKey: Data)?
|
||||
var state: CallSessionInternalState
|
||||
let subscribers = Bag<(CallSession) -> Void>()
|
||||
var signalingReceiver: (([Data]) -> Void)?
|
||||
|
||||
let signalingDisposables = DisposableSet()
|
||||
var createConferenceCallDisposable: Disposable?
|
||||
let acknowledgeIncomingCallDisposable = MetaDisposable()
|
||||
var createConferenceCallDisposable: Disposable?
|
||||
|
||||
var isEmpty: Bool {
|
||||
if case .terminated = self.state {
|
||||
@@ -356,13 +364,11 @@ private final class CallSessionContext {
|
||||
}
|
||||
}
|
||||
|
||||
init(peerId: PeerId, isOutgoing: Bool, isIncomingConference: Bool, type: CallSession.CallType, isVideoPossible: Bool, pendingConference: (conference: GroupCallReference, encryptionKey: Data)?, state: CallSessionInternalState) {
|
||||
init(peerId: PeerId, isOutgoing: Bool, type: CallSession.CallType, isVideoPossible: Bool, state: CallSessionInternalState) {
|
||||
self.peerId = peerId
|
||||
self.isOutgoing = isOutgoing
|
||||
self.isIncomingConference = isIncomingConference
|
||||
self.type = type
|
||||
self.isVideoPossible = isVideoPossible
|
||||
self.pendingConference = pendingConference
|
||||
self.state = state
|
||||
}
|
||||
|
||||
@@ -373,6 +379,73 @@ private final class CallSessionContext {
|
||||
}
|
||||
}
|
||||
|
||||
private final class IncomingConferenceInvitationContext {
|
||||
enum State: Equatable {
|
||||
case pending
|
||||
case ringing(callId: Int64, otherParticipants: [EnginePeer])
|
||||
case stopped
|
||||
}
|
||||
|
||||
private let queue: Queue
|
||||
private var disposable: Disposable?
|
||||
|
||||
let internalId: CallSessionInternalId
|
||||
|
||||
private(set) var state: State = .pending
|
||||
|
||||
init(queue: Queue, postbox: Postbox, messageId: MessageId, updated: @escaping () -> Void) {
|
||||
self.queue = queue
|
||||
|
||||
self.internalId = CallSessionInternalId()
|
||||
|
||||
let key = PostboxViewKey.messages(Set([messageId]))
|
||||
self.disposable = (postbox.combinedView(keys: [key])
|
||||
|> map { view -> Message? in
|
||||
guard let view = view.views[key] as? MessagesView else {
|
||||
return nil
|
||||
}
|
||||
return view.messages[messageId]
|
||||
}
|
||||
|> deliverOn(self.queue)).startStrict(next: { [weak self] message in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
let state: State
|
||||
if let message = message {
|
||||
var foundAction: TelegramMediaAction?
|
||||
for media in message.media {
|
||||
if let action = media as? TelegramMediaAction {
|
||||
foundAction = action
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if let action = foundAction, case let .conferenceCall(callId, duration, otherParticipants) = action.action {
|
||||
if duration != nil {
|
||||
state = .stopped
|
||||
} else {
|
||||
state = .ringing(callId: callId, otherParticipants: otherParticipants.compactMap { id -> EnginePeer? in
|
||||
return message.peers[id].flatMap(EnginePeer.init)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
state = .stopped
|
||||
}
|
||||
} else {
|
||||
state = .stopped
|
||||
}
|
||||
if self.state != state {
|
||||
self.state = state
|
||||
updated()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.disposable?.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
private func selectVersionOnAccept(localVersions: [CallSessionManagerImplementationVersion], remoteVersions: [String]) -> [String]? {
|
||||
let filteredVersions = localVersions.map({ $0.version }).filter(remoteVersions.contains)
|
||||
if filteredVersions.isEmpty {
|
||||
@@ -406,10 +479,13 @@ private final class CallSessionManagerContext {
|
||||
private var futureContextSubscribers: [CallSessionInternalId: Bag<(CallSession) -> Void>] = [:]
|
||||
private var contextIdByStableId: [CallSessionStableId: CallSessionInternalId] = [:]
|
||||
|
||||
private var incomingConferenceInvitationContexts: [MessageId: IncomingConferenceInvitationContext] = [:]
|
||||
|
||||
private var enqueuedSignalingData: [Int64: [Data]] = [:]
|
||||
|
||||
private let disposables = DisposableSet()
|
||||
|
||||
private let rejectConferenceInvitationDisposables = DisposableSet()
|
||||
|
||||
init(queue: Queue, postbox: Postbox, network: Network, accountPeerId: PeerId, maxLayer: Int32, versions: [CallSessionManagerImplementationVersion], addUpdates: @escaping (Api.Updates) -> Void) {
|
||||
self.queue = queue
|
||||
self.postbox = postbox
|
||||
@@ -423,6 +499,7 @@ private final class CallSessionManagerContext {
|
||||
deinit {
|
||||
assert(self.queue.isCurrent())
|
||||
self.disposables.dispose()
|
||||
self.rejectConferenceInvitationDisposables.dispose()
|
||||
}
|
||||
|
||||
func updateVersions(versions: [CallSessionManagerImplementationVersion]) {
|
||||
@@ -556,7 +633,20 @@ private final class CallSessionManagerContext {
|
||||
peerId: context.peerId,
|
||||
isVideo: context.type == .video,
|
||||
isVideoPossible: context.isVideoPossible,
|
||||
isIncomingConference: context.isIncomingConference
|
||||
conferenceSource: nil,
|
||||
otherParticipants: []
|
||||
))
|
||||
}
|
||||
}
|
||||
for (id, context) in self.incomingConferenceInvitationContexts {
|
||||
if case let .ringing(_, otherParticipants) = context.state {
|
||||
ringingContexts.append(CallSessionRingingState(
|
||||
id: context.internalId,
|
||||
peerId: id.peerId,
|
||||
isVideo: false,
|
||||
isVideoPossible: true,
|
||||
conferenceSource: id,
|
||||
otherParticipants: otherParticipants
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -584,7 +674,7 @@ private final class CallSessionManagerContext {
|
||||
}
|
||||
}
|
||||
|
||||
private func addIncoming(peerId: PeerId, stableId: CallSessionStableId, accessHash: Int64, timestamp: Int32, gAHash: Data, versions: [String], isVideo: Bool, conferenceCall: GroupCallReference?) -> CallSessionInternalId? {
|
||||
private func addIncoming(peerId: PeerId, stableId: CallSessionStableId, accessHash: Int64, timestamp: Int32, gAHash: Data, versions: [String], isVideo: Bool) -> CallSessionInternalId? {
|
||||
if self.contextIdByStableId[stableId] != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -594,13 +684,10 @@ private final class CallSessionManagerContext {
|
||||
let b = Data(bytesNoCopy: bBytes, count: 256, deallocator: .free)
|
||||
|
||||
if randomStatus == 0 {
|
||||
var isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
|
||||
//#if DEBUG
|
||||
isVideoPossible = true
|
||||
//#endif
|
||||
let isVideoPossible = true
|
||||
|
||||
let internalId = CallSessionManager.getStableIncomingUUID(stableId: stableId)
|
||||
let context = CallSessionContext(peerId: peerId, isOutgoing: false, isIncomingConference: conferenceCall != nil, type: isVideo ? .video : .audio, isVideoPossible: isVideoPossible, pendingConference: nil, state: .ringing(id: stableId, accessHash: accessHash, gAHash: gAHash, b: b, versions: versions, conferenceCall: conferenceCall))
|
||||
let context = CallSessionContext(peerId: peerId, isOutgoing: false, type: isVideo ? .video : .audio, isVideoPossible: isVideoPossible, state: .ringing(id: stableId, accessHash: accessHash, gAHash: gAHash, b: b, versions: versions))
|
||||
self.contexts[internalId] = context
|
||||
let queue = self.queue
|
||||
|
||||
@@ -632,38 +719,62 @@ private final class CallSessionManagerContext {
|
||||
}
|
||||
|
||||
func drop(internalId: CallSessionInternalId, reason: DropCallReason, debugLog: Signal<String?, NoError>) {
|
||||
for (id, context) in self.incomingConferenceInvitationContexts {
|
||||
if context.internalId == internalId {
|
||||
self.incomingConferenceInvitationContexts.removeValue(forKey: id)
|
||||
self.ringingStatesUpdated()
|
||||
|
||||
let addUpdates = self.addUpdates
|
||||
let rejectSignal = self.network.request(Api.functions.phone.declineConferenceCallInvite(msgId: id.id))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { updates -> Signal<Never, NoError> in
|
||||
if let updates {
|
||||
addUpdates(updates)
|
||||
}
|
||||
return .complete()
|
||||
}
|
||||
|
||||
self.rejectConferenceInvitationDisposables.add(rejectSignal.startStrict())
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if let context = self.contexts[internalId] {
|
||||
var dropData: (CallSessionStableId, Int64, DropCallSessionReason)?
|
||||
var wasRinging = false
|
||||
let isVideo = context.type == .video
|
||||
switch context.state {
|
||||
case let .ringing(id, accessHash, _, _, _, _):
|
||||
case let .ringing(id, accessHash, _, _, _):
|
||||
wasRinging = true
|
||||
let internalReason: DropCallSessionReason
|
||||
switch reason {
|
||||
case .busy:
|
||||
internalReason = .busy
|
||||
case .hangUp:
|
||||
internalReason = .hangUp(0)
|
||||
case .disconnect:
|
||||
internalReason = .disconnect
|
||||
case .missed:
|
||||
internalReason = .missed
|
||||
case .busy:
|
||||
internalReason = .busy
|
||||
case .hangUp:
|
||||
internalReason = .hangUp(0)
|
||||
case .disconnect:
|
||||
internalReason = .disconnect
|
||||
case .missed:
|
||||
internalReason = .missed
|
||||
}
|
||||
dropData = (id, accessHash, internalReason)
|
||||
case let .accepting(id, accessHash, _, _, _, disposable):
|
||||
case let .accepting(id, accessHash, _, _, disposable):
|
||||
dropData = (id, accessHash, .abort)
|
||||
disposable.dispose()
|
||||
case let .active(id, accessHash, beginTimestamp, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .active(id, accessHash, beginTimestamp, _, _, _, _, _, _, _, _):
|
||||
let duration = max(0, Int32(CFAbsoluteTimeGetCurrent()) - beginTimestamp)
|
||||
let internalReason: DropCallSessionReason
|
||||
switch reason {
|
||||
case .busy, .hangUp:
|
||||
internalReason = .hangUp(duration)
|
||||
case .disconnect:
|
||||
internalReason = .disconnect
|
||||
case .missed:
|
||||
internalReason = .missed
|
||||
case .busy, .hangUp:
|
||||
internalReason = .hangUp(duration)
|
||||
case .disconnect:
|
||||
internalReason = .disconnect
|
||||
case .missed:
|
||||
internalReason = .missed
|
||||
}
|
||||
dropData = (id, accessHash, internalReason)
|
||||
case .switchedToConference:
|
||||
@@ -672,21 +783,21 @@ private final class CallSessionManagerContext {
|
||||
break
|
||||
case let .awaitingConfirmation(id, accessHash, _, _, _):
|
||||
dropData = (id, accessHash, .abort)
|
||||
case let .confirming(id, accessHash, _, _, _, _, disposable):
|
||||
case let .confirming(id, accessHash, _, _, _, disposable):
|
||||
disposable.dispose()
|
||||
dropData = (id, accessHash, .abort)
|
||||
case let .requested(id, accessHash, _, _, _, _, _):
|
||||
case let .requested(id, accessHash, _, _, _, _):
|
||||
let internalReason: DropCallSessionReason
|
||||
switch reason {
|
||||
case .busy, .hangUp:
|
||||
internalReason = .missed
|
||||
case .disconnect:
|
||||
internalReason = .disconnect
|
||||
case .missed:
|
||||
internalReason = .missed
|
||||
case .busy, .hangUp:
|
||||
internalReason = .missed
|
||||
case .disconnect:
|
||||
internalReason = .disconnect
|
||||
case .missed:
|
||||
internalReason = .missed
|
||||
}
|
||||
dropData = (id, accessHash, internalReason)
|
||||
case let .requesting(_, _, disposable):
|
||||
case let .requesting(_, disposable):
|
||||
disposable.dispose()
|
||||
context.state = .terminated(id: nil, accessHash: nil, reason: .ended(.hungUp), reportRating: false, sendDebugLogs: false)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
@@ -709,8 +820,8 @@ private final class CallSessionManagerContext {
|
||||
mappedReason = .ended(.hungUp)
|
||||
case .missed:
|
||||
mappedReason = .ended(.missed)
|
||||
case .switchToConference:
|
||||
mappedReason = .ended(.switchedToConference)
|
||||
case let .switchToConference(slug):
|
||||
mappedReason = .ended(.switchedToConference(slug: slug))
|
||||
}
|
||||
context.state = .dropping(reason: mappedReason, disposable: (dropCallSession(network: self.network, addUpdates: self.addUpdates, stableId: id, accessHash: accessHash, isVideo: isVideo, reason: reason)
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] reportRating, sendDebugLogs in
|
||||
@@ -751,12 +862,12 @@ private final class CallSessionManagerContext {
|
||||
}
|
||||
}
|
||||
|
||||
func dropToConference(internalId: CallSessionInternalId, encryptedGroupKey: Data) {
|
||||
func dropToConference(internalId: CallSessionInternalId, slug: String) {
|
||||
if let context = self.contexts[internalId] {
|
||||
var dropData: (CallSessionStableId, Int64)?
|
||||
let isVideo = context.type == .video
|
||||
switch context.state {
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _):
|
||||
dropData = (id, accessHash)
|
||||
default:
|
||||
break
|
||||
@@ -764,11 +875,11 @@ private final class CallSessionManagerContext {
|
||||
|
||||
if let (id, accessHash) = dropData {
|
||||
self.contextIdByStableId.removeValue(forKey: id)
|
||||
context.state = .dropping(reason: .ended(.switchedToConference), disposable: (dropCallSession(network: self.network, addUpdates: self.addUpdates, stableId: id, accessHash: accessHash, isVideo: isVideo, reason: .switchToConference(encryptedGroupKey: encryptedGroupKey))
|
||||
context.state = .dropping(reason: .ended(.switchedToConference(slug: slug)), disposable: (dropCallSession(network: self.network, addUpdates: self.addUpdates, stableId: id, accessHash: accessHash, isVideo: isVideo, reason: .switchToConference(slug: slug))
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] reportRating, sendDebugLogs in
|
||||
if let strongSelf = self {
|
||||
if let context = strongSelf.contexts[internalId] {
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: .ended(.hungUp), reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
context.state = .switchedToConference(slug: slug)
|
||||
strongSelf.contextUpdated(internalId: internalId)
|
||||
if context.isEmpty {
|
||||
strongSelf.contexts.removeValue(forKey: internalId)
|
||||
@@ -793,9 +904,9 @@ private final class CallSessionManagerContext {
|
||||
func accept(internalId: CallSessionInternalId) {
|
||||
if let context = self.contexts[internalId] {
|
||||
switch context.state {
|
||||
case let .ringing(id, accessHash, gAHash, b, _, conferenceCall):
|
||||
case let .ringing(id, accessHash, gAHash, b, _):
|
||||
let acceptVersions = self.versions.map({ $0.version })
|
||||
context.state = .accepting(id: id, accessHash: accessHash, gAHash: gAHash, b: b, conferenceCall: conferenceCall, disposable: (acceptCallSession(accountPeerId: self.accountPeerId, postbox: self.postbox, network: self.network, stableId: id, accessHash: accessHash, b: b, maxLayer: self.maxLayer, versions: acceptVersions) |> deliverOn(self.queue)).start(next: { [weak self] result in
|
||||
context.state = .accepting(id: id, accessHash: accessHash, gAHash: gAHash, b: b, disposable: (acceptCallSession(accountPeerId: self.accountPeerId, postbox: self.postbox, network: self.network, stableId: id, accessHash: accessHash, b: b, maxLayer: self.maxLayer, versions: acceptVersions) |> deliverOn(self.queue)).start(next: { [weak self] result in
|
||||
if let strongSelf = self, let context = strongSelf.contexts[internalId] {
|
||||
if case .accepting = context.state {
|
||||
switch result {
|
||||
@@ -806,9 +917,9 @@ private final class CallSessionManagerContext {
|
||||
case let .waiting(config):
|
||||
context.state = .awaitingConfirmation(id: id, accessHash: accessHash, gAHash: gAHash, b: b, config: config)
|
||||
strongSelf.contextUpdated(internalId: internalId)
|
||||
case let .call(config, gA, timestamp, connections, maxLayer, version, customParameters, allowsP2P, conferenceCall):
|
||||
case let .call(config, gA, timestamp, connections, maxLayer, version, customParameters, allowsP2P):
|
||||
if let (key, keyId, keyVisualHash) = strongSelf.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gA) {
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: timestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, conferenceCall: conferenceCall, willSwitchToConference: context.isIncomingConference)
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: timestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P)
|
||||
strongSelf.contextUpdated(internalId: internalId)
|
||||
} else {
|
||||
strongSelf.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
@@ -829,7 +940,7 @@ private final class CallSessionManagerContext {
|
||||
func sendSignalingData(internalId: CallSessionInternalId, data: Data) {
|
||||
if let context = self.contexts[internalId] {
|
||||
switch context.state {
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _):
|
||||
context.signalingDisposables.add(self.network.request(Api.functions.phone.sendSignalingData(peer: .inputPhoneCall(id: id, accessHash: accessHash), data: Buffer(data: data))).start())
|
||||
default:
|
||||
break
|
||||
@@ -845,33 +956,29 @@ private final class CallSessionManagerContext {
|
||||
|
||||
var idAndAccessHash: (id: Int64, accessHash: Int64)?
|
||||
switch context.state {
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, conferenceCall, _):
|
||||
if conferenceCall != nil {
|
||||
return
|
||||
}
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _):
|
||||
idAndAccessHash = (id, accessHash)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if let (id, accessHash) = idAndAccessHash {
|
||||
context.createConferenceCallDisposable = (createConferenceCall(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, callId: CallId(id: id, accessHash: accessHash))
|
||||
if idAndAccessHash != nil {
|
||||
context.createConferenceCallDisposable = (_internal_createConferenceCall(
|
||||
postbox: self.postbox,
|
||||
network: self.network,
|
||||
accountPeerId: self.accountPeerId
|
||||
)
|
||||
|> deliverOn(self.queue)).startStrict(next: { [weak self] result in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.dropToConference(internalId: internalId, slug: result.slug)
|
||||
}, error: { [weak self] error in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
guard let context = self.contexts[internalId] else {
|
||||
return
|
||||
}
|
||||
if let result {
|
||||
switch context.state {
|
||||
case let .active(id, accessHash, beginTimestamp, key, keyId, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P, _, _):
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: beginTimestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, conferenceCall: result, willSwitchToConference: false)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -889,7 +996,7 @@ private final class CallSessionManagerContext {
|
||||
switch call {
|
||||
case .phoneCallEmpty:
|
||||
break
|
||||
case let .phoneCallAccepted(_, id, _, _, _, _, gB, remoteProtocol, conferenceCall):
|
||||
case let .phoneCallAccepted(_, id, _, _, _, _, gB, remoteProtocol):
|
||||
let remoteVersions: [String]
|
||||
switch remoteProtocol {
|
||||
case let .phoneCallProtocol(_, _, _, versions):
|
||||
@@ -903,7 +1010,7 @@ private final class CallSessionManagerContext {
|
||||
|
||||
if let context = self.contexts[internalId] {
|
||||
switch context.state {
|
||||
case let .requested(_, accessHash, a, gA, config, _, _):
|
||||
case let .requested(_, accessHash, a, gA, config, _):
|
||||
let p = config.p.makeData()
|
||||
if !MTCheckIsSafeGAOrB(self.network.encryptionProvider, gA, p) {
|
||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
@@ -928,14 +1035,10 @@ private final class CallSessionManagerContext {
|
||||
|
||||
let keyVisualHash = MTSha256(key + gA)
|
||||
|
||||
context.state = .confirming(id: id, accessHash: accessHash, key: key, keyId: keyId, keyVisualHash: keyVisualHash, conferenceCall: conferenceCall.flatMap(GroupCallReference.init), disposable: (confirmCallSession(network: self.network, stableId: id, accessHash: accessHash, gA: gA, keyFingerprint: keyId, maxLayer: self.maxLayer, versions: selectedVersions) |> deliverOnMainQueue).start(next: { [weak self] updatedCall in
|
||||
context.state = .confirming(id: id, accessHash: accessHash, key: key, keyId: keyId, keyVisualHash: keyVisualHash, disposable: (confirmCallSession(network: self.network, stableId: id, accessHash: accessHash, gA: gA, keyFingerprint: keyId, maxLayer: self.maxLayer, versions: selectedVersions) |> deliverOnMainQueue).start(next: { [weak self] updatedCall in
|
||||
if let strongSelf = self, let context = strongSelf.contexts[internalId], case .confirming = context.state {
|
||||
if let updatedCall = updatedCall {
|
||||
strongSelf.updateSession(updatedCall, completion: { _ in })
|
||||
|
||||
if let pendingConference = context.pendingConference {
|
||||
strongSelf.dropToConference(internalId: internalId, encryptedGroupKey: pendingConference.encryptionKey)
|
||||
}
|
||||
} else {
|
||||
strongSelf.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
}
|
||||
@@ -949,8 +1052,7 @@ private final class CallSessionManagerContext {
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
case let .phoneCallDiscarded(flags, id, reason, _, conferenceCall):
|
||||
let _ = conferenceCall
|
||||
case let .phoneCallDiscarded(flags, id, reason, _):
|
||||
let reportRating = (flags & (1 << 2)) != 0
|
||||
let sendDebugLogs = (flags & (1 << 3)) != 0
|
||||
if let internalId = self.contextIdByStableId[id] {
|
||||
@@ -963,47 +1065,39 @@ private final class CallSessionManagerContext {
|
||||
case .phoneCallDiscardReasonDisconnect:
|
||||
parsedReason = .error(.disconnected)
|
||||
case .phoneCallDiscardReasonHangup:
|
||||
if context.pendingConference != nil {
|
||||
parsedReason = .ended(.switchedToConference)
|
||||
} else {
|
||||
parsedReason = .ended(.hungUp)
|
||||
}
|
||||
parsedReason = .ended(.hungUp)
|
||||
case .phoneCallDiscardReasonMissed:
|
||||
parsedReason = .ended(.missed)
|
||||
case .phoneCallDiscardReasonAllowGroupCall:
|
||||
parsedReason = .ended(.switchedToConference)
|
||||
case let .phoneCallDiscardReasonMigrateConferenceCall(slug):
|
||||
parsedReason = .ended(.switchedToConference(slug: slug))
|
||||
}
|
||||
} else {
|
||||
parsedReason = .ended(.hungUp)
|
||||
}
|
||||
|
||||
switch context.state {
|
||||
case let .accepting(id, accessHash, _, _, _, disposable):
|
||||
case let .accepting(id, accessHash, _, _, disposable):
|
||||
disposable.dispose()
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, conferenceCall, _):
|
||||
if let conferenceCall, case let .phoneCallDiscardReasonAllowGroupCall(encryptedGroupKey) = reason {
|
||||
context.state = .switchedToConference(key: encryptedGroupKey.makeData(), keyVisualHash: MTSha256(encryptedGroupKey.makeData()), conferenceCall: conferenceCall)
|
||||
} else {
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
}
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _):
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .awaitingConfirmation(id, accessHash, _, _, _):
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .requested(id, accessHash, _, _, _, _, _):
|
||||
case let .requested(id, accessHash, _, _, _, _):
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .confirming(id, accessHash, _, _, _, _, disposable):
|
||||
case let .confirming(id, accessHash, _, _, _, disposable):
|
||||
disposable.dispose()
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .requesting(_, _, disposable):
|
||||
case let .requesting(_, disposable):
|
||||
disposable.dispose()
|
||||
context.state = .terminated(id: nil, accessHash: nil, reason: parsedReason, reportRating: false, sendDebugLogs: false)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .ringing(id, accessHash, _, _, _, _):
|
||||
case let .ringing(id, accessHash, _, _, _):
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
self.ringingStatesUpdated()
|
||||
self.contextUpdated(internalId: internalId)
|
||||
@@ -1014,15 +1108,15 @@ private final class CallSessionManagerContext {
|
||||
//assertionFailure()
|
||||
}
|
||||
}
|
||||
case let .phoneCall(flags, id, _, _, _, _, gAOrB, keyFingerprint, callProtocol, connections, startDate, customParameters, conferenceCall):
|
||||
case let .phoneCall(flags, id, _, _, _, _, gAOrB, keyFingerprint, callProtocol, connections, startDate, customParameters):
|
||||
let allowsP2P = (flags & (1 << 5)) != 0
|
||||
if let internalId = self.contextIdByStableId[id] {
|
||||
if let context = self.contexts[internalId] {
|
||||
switch context.state {
|
||||
case .accepting, .dropping, .requesting, .ringing, .terminated, .requested, .switchedToConference:
|
||||
break
|
||||
case let .active(id, accessHash, beginTimestamp, key, keyId, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P, _, _):
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: beginTimestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, conferenceCall: conferenceCall.flatMap(GroupCallReference.init), willSwitchToConference: context.isIncomingConference)
|
||||
case let .active(id, accessHash, beginTimestamp, key, keyId, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P):
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: beginTimestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .awaitingConfirmation(_, accessHash, gAHash, b, config):
|
||||
if let (key, calculatedKeyId, keyVisualHash) = self.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gAOrB.makeData()) {
|
||||
@@ -1041,7 +1135,7 @@ private final class CallSessionManagerContext {
|
||||
let isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
|
||||
context.isVideoPossible = isVideoPossible
|
||||
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: calculatedKeyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: allowsP2P, conferenceCall: conferenceCall.flatMap(GroupCallReference.init), willSwitchToConference: context.isIncomingConference)
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: calculatedKeyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: allowsP2P)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
} else {
|
||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
@@ -1053,7 +1147,7 @@ private final class CallSessionManagerContext {
|
||||
} else {
|
||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
}
|
||||
case let .confirming(id, accessHash, key, keyId, keyVisualHash, _, _):
|
||||
case let .confirming(id, accessHash, key, keyId, keyVisualHash, _):
|
||||
switch callProtocol {
|
||||
case let .phoneCallProtocol(_, _, maxLayer, versions):
|
||||
if !versions.isEmpty {
|
||||
@@ -1068,7 +1162,7 @@ private final class CallSessionManagerContext {
|
||||
let isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
|
||||
context.isVideoPossible = isVideoPossible
|
||||
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: allowsP2P, conferenceCall: conferenceCall.flatMap(GroupCallReference.init), willSwitchToConference: context.isIncomingConference)
|
||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: allowsP2P)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
} else {
|
||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||
@@ -1079,7 +1173,7 @@ private final class CallSessionManagerContext {
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, gAHash, requestedProtocol, conferenceCall):
|
||||
case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, gAHash, requestedProtocol):
|
||||
let isVideo = (flags & (1 << 6)) != 0
|
||||
let versions: [String]
|
||||
switch requestedProtocol {
|
||||
@@ -1087,7 +1181,7 @@ private final class CallSessionManagerContext {
|
||||
versions = libraryVersions
|
||||
}
|
||||
if self.contextIdByStableId[id] == nil {
|
||||
let internalId = self.addIncoming(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)), stableId: id, accessHash: accessHash, timestamp: date, gAHash: gAHash.makeData(), versions: versions, isVideo: isVideo, conferenceCall: conferenceCall.flatMap(GroupCallReference.init))
|
||||
let internalId = self.addIncoming(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)), stableId: id, accessHash: accessHash, timestamp: date, gAHash: gAHash.makeData(), versions: versions, isVideo: isVideo)
|
||||
if let internalId = internalId {
|
||||
var resultRingingStateValue: CallSessionRingingState?
|
||||
for ringingState in self.ringingStatesValue() {
|
||||
@@ -1104,13 +1198,13 @@ private final class CallSessionManagerContext {
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .phoneCallWaiting(_, id, _, _, _, _, _, receiveDate, conferenceCall):
|
||||
case let .phoneCallWaiting(_, id, _, _, _, _, _, receiveDate):
|
||||
if let internalId = self.contextIdByStableId[id] {
|
||||
if let context = self.contexts[internalId] {
|
||||
switch context.state {
|
||||
case let .requested(id, accessHash, a, gA, config, remoteConfirmationTimestamp, _):
|
||||
case let .requested(id, accessHash, a, gA, config, remoteConfirmationTimestamp):
|
||||
if let receiveDate = receiveDate, remoteConfirmationTimestamp == nil {
|
||||
context.state = .requested(id: id, accessHash: accessHash, a: a, gA: gA, config: config, remoteConfirmationTimestamp: receiveDate, conferenceCall: conferenceCall.flatMap(GroupCallReference.init))
|
||||
context.state = .requested(id: id, accessHash: accessHash, a: a, gA: gA, config: config, remoteConfirmationTimestamp: receiveDate)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
}
|
||||
default:
|
||||
@@ -1144,6 +1238,29 @@ private final class CallSessionManagerContext {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addConferenceInvitationMessages(ids: [MessageId]) {
|
||||
for id in ids {
|
||||
if self.incomingConferenceInvitationContexts[id] == nil {
|
||||
let context = IncomingConferenceInvitationContext(queue: self.queue, postbox: self.postbox, messageId: id, updated: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let context = self.incomingConferenceInvitationContexts[id] {
|
||||
switch context.state {
|
||||
case .pending:
|
||||
break
|
||||
case .ringing:
|
||||
self.ringingStatesUpdated()
|
||||
case .stopped:
|
||||
self.incomingConferenceInvitationContexts.removeValue(forKey: id)
|
||||
}
|
||||
}
|
||||
})
|
||||
self.incomingConferenceInvitationContexts[id] = context
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func makeSessionEncryptionKey(config: SecretChatEncryptionConfig, gAHash: Data, b: Data, gA: Data) -> (key: Data, keyId: Int64, keyVisualHash: Data)? {
|
||||
var key = MTExp(self.network.encryptionProvider, gA, b, config.p.makeData())!
|
||||
@@ -1173,17 +1290,17 @@ private final class CallSessionManagerContext {
|
||||
return (key, keyId, keyVisualHash)
|
||||
}
|
||||
|
||||
func request(peerId: PeerId, internalId: CallSessionInternalId, isVideo: Bool, enableVideo: Bool, conferenceCall: (conference: GroupCallReference, encryptionKey: Data)?) -> CallSessionInternalId? {
|
||||
func request(peerId: PeerId, internalId: CallSessionInternalId, isVideo: Bool, enableVideo: Bool) -> CallSessionInternalId? {
|
||||
let aBytes = malloc(256)!
|
||||
let randomStatus = SecRandomCopyBytes(nil, 256, aBytes.assumingMemoryBound(to: UInt8.self))
|
||||
let a = Data(bytesNoCopy: aBytes, count: 256, deallocator: .free)
|
||||
if randomStatus == 0 {
|
||||
self.contexts[internalId] = CallSessionContext(peerId: peerId, isOutgoing: true, isIncomingConference: false, type: isVideo ? .video : .audio, isVideoPossible: enableVideo || isVideo, pendingConference: conferenceCall, state: .requesting(a: a, conferenceCall: conferenceCall?.conference, disposable: (requestCallSession(postbox: self.postbox, network: self.network, peerId: peerId, a: a, maxLayer: self.maxLayer, versions: self.filteredVersions(enableVideo: true), isVideo: isVideo, conferenceCall: conferenceCall?.conference) |> deliverOn(queue)).start(next: { [weak self] result in
|
||||
self.contexts[internalId] = CallSessionContext(peerId: peerId, isOutgoing: true, type: isVideo ? .video : .audio, isVideoPossible: enableVideo || isVideo, state: .requesting(a: a, disposable: (requestCallSession(postbox: self.postbox, network: self.network, peerId: peerId, a: a, maxLayer: self.maxLayer, versions: self.filteredVersions(enableVideo: true), isVideo: isVideo) |> deliverOn(queue)).start(next: { [weak self] result in
|
||||
if let strongSelf = self, let context = strongSelf.contexts[internalId] {
|
||||
if case .requesting = context.state {
|
||||
switch result {
|
||||
case let .success(id, accessHash, config, gA, remoteConfirmationTimestamp):
|
||||
context.state = .requested(id: id, accessHash: accessHash, a: a, gA: gA, config: config, remoteConfirmationTimestamp: remoteConfirmationTimestamp, conferenceCall: conferenceCall?.conference)
|
||||
context.state = .requested(id: id, accessHash: accessHash, a: a, gA: gA, config: config, remoteConfirmationTimestamp: remoteConfirmationTimestamp)
|
||||
strongSelf.contextIdByStableId[id] = internalId
|
||||
strongSelf.contextUpdated(internalId: internalId)
|
||||
strongSelf.deliverCallSignalingData(id: id)
|
||||
@@ -1253,6 +1370,12 @@ public final class CallSessionManager {
|
||||
context.addCallSignalingData(id: id, data: data)
|
||||
}
|
||||
}
|
||||
|
||||
func addConferenceInvitationMessages(ids: [MessageId]) {
|
||||
self.withContext { context in
|
||||
context.addConferenceInvitationMessages(ids: ids)
|
||||
}
|
||||
}
|
||||
|
||||
public func drop(internalId: CallSessionInternalId, reason: DropCallReason, debugLog: Signal<String?, NoError>) {
|
||||
self.withContext { context in
|
||||
@@ -1278,12 +1401,12 @@ public final class CallSessionManager {
|
||||
}
|
||||
}
|
||||
|
||||
public func request(peerId: PeerId, isVideo: Bool, enableVideo: Bool, conferenceCall: (conference: GroupCallReference, encryptionKey: Data)?, internalId: CallSessionInternalId = CallSessionInternalId()) -> Signal<CallSessionInternalId, NoError> {
|
||||
public func request(peerId: PeerId, isVideo: Bool, enableVideo: Bool, internalId: CallSessionInternalId = CallSessionInternalId()) -> Signal<CallSessionInternalId, NoError> {
|
||||
return Signal { [weak self] subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
|
||||
self?.withContext { context in
|
||||
if let internalId = context.request(peerId: peerId, internalId: internalId, isVideo: isVideo, enableVideo: enableVideo, conferenceCall: conferenceCall) {
|
||||
if let internalId = context.request(peerId: peerId, internalId: internalId, isVideo: isVideo, enableVideo: enableVideo) {
|
||||
subscriber.putNext(internalId)
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
@@ -1354,7 +1477,7 @@ public final class CallSessionManager {
|
||||
|
||||
private enum AcceptedCall {
|
||||
case waiting(config: SecretChatEncryptionConfig)
|
||||
case call(config: SecretChatEncryptionConfig, gA: Data, timestamp: Int32, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, conferenceCall: GroupCallReference?)
|
||||
case call(config: SecretChatEncryptionConfig, gA: Data, timestamp: Int32, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool)
|
||||
}
|
||||
|
||||
private enum AcceptCallResult {
|
||||
@@ -1394,7 +1517,7 @@ private func acceptCallSession(accountPeerId: PeerId, postbox: Postbox, network:
|
||||
return .failed
|
||||
case .phoneCallWaiting:
|
||||
return .success(.waiting(config: config))
|
||||
case let .phoneCall(flags, id, _, _, _, _, gAOrB, _, callProtocol, connections, startDate, customParameters, conferenceCall):
|
||||
case let .phoneCall(flags, id, _, _, _, _, gAOrB, _, callProtocol, connections, startDate, customParameters):
|
||||
if id == stableId {
|
||||
switch callProtocol{
|
||||
case let .phoneCallProtocol(_, _, maxLayer, versions):
|
||||
@@ -1407,7 +1530,7 @@ private func acceptCallSession(accountPeerId: PeerId, postbox: Postbox, network:
|
||||
customParametersValue = data
|
||||
}
|
||||
|
||||
return .success(.call(config: config, gA: gAOrB.makeData(), timestamp: startDate, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: (flags & (1 << 5)) != 0, conferenceCall: conferenceCall.flatMap(GroupCallReference.init)))
|
||||
return .success(.call(config: config, gA: gAOrB.makeData(), timestamp: startDate, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: (flags & (1 << 5)) != 0))
|
||||
} else {
|
||||
return .failed
|
||||
}
|
||||
@@ -1430,7 +1553,7 @@ private enum RequestCallSessionResult {
|
||||
case failed(CallSessionError)
|
||||
}
|
||||
|
||||
private func requestCallSession(postbox: Postbox, network: Network, peerId: PeerId, a: Data, maxLayer: Int32, versions: [String], isVideo: Bool, conferenceCall: GroupCallReference?) -> Signal<RequestCallSessionResult, NoError> {
|
||||
private func requestCallSession(postbox: Postbox, network: Network, peerId: PeerId, a: Data, maxLayer: Int32, versions: [String], isVideo: Bool) -> Signal<RequestCallSessionResult, NoError> {
|
||||
return validatedEncryptionConfig(postbox: postbox, network: network)
|
||||
|> mapToSignal { config -> Signal<RequestCallSessionResult, NoError> in
|
||||
return postbox.transaction { transaction -> Signal<RequestCallSessionResult, NoError> in
|
||||
@@ -1450,18 +1573,15 @@ private func requestCallSession(postbox: Postbox, network: Network, peerId: Peer
|
||||
if isVideo {
|
||||
callFlags |= 1 << 0
|
||||
}
|
||||
if conferenceCall != nil {
|
||||
callFlags |= 1 << 1
|
||||
}
|
||||
|
||||
return network.request(Api.functions.phone.requestCall(flags: callFlags, userId: inputUser, conferenceCall: conferenceCall.flatMap { Api.InputGroupCall.inputGroupCall(id: $0.id, accessHash: $0.accessHash) }, randomId: Int32(bitPattern: arc4random()), gAHash: Buffer(data: gAHash), protocol: .phoneCallProtocol(flags: (1 << 0) | (1 << 1), minLayer: minLayer, maxLayer: maxLayer, libraryVersions: versions)))
|
||||
return network.request(Api.functions.phone.requestCall(flags: callFlags, userId: inputUser, randomId: Int32(bitPattern: arc4random()), gAHash: Buffer(data: gAHash), protocol: .phoneCallProtocol(flags: (1 << 0) | (1 << 1), minLayer: minLayer, maxLayer: maxLayer, libraryVersions: versions)))
|
||||
|> map { result -> RequestCallSessionResult in
|
||||
switch result {
|
||||
case let .phoneCall(phoneCall, _):
|
||||
switch phoneCall {
|
||||
case let .phoneCallRequested(_, id, accessHash, _, _, _, _, _, _):
|
||||
case let .phoneCallRequested(_, id, accessHash, _, _, _, _, _):
|
||||
return .success(id: id, accessHash: accessHash, config: config, gA: ga, remoteConfirmationTimestamp: nil)
|
||||
case let .phoneCallWaiting(_, id, accessHash, _, _, _, _, receiveDate, _):
|
||||
case let .phoneCallWaiting(_, id, accessHash, _, _, _, _, receiveDate):
|
||||
return .success(id: id, accessHash: accessHash, config: config, gA: ga, remoteConfirmationTimestamp: receiveDate)
|
||||
default:
|
||||
return .failed(.generic)
|
||||
@@ -1514,7 +1634,7 @@ private enum DropCallSessionReason {
|
||||
case busy
|
||||
case disconnect
|
||||
case missed
|
||||
case switchToConference(encryptedGroupKey: Data)
|
||||
case switchToConference(slug: String)
|
||||
}
|
||||
|
||||
private func dropCallSession(network: Network, addUpdates: @escaping (Api.Updates) -> Void, stableId: CallSessionStableId, accessHash: Int64, isVideo: Bool, reason: DropCallSessionReason) -> Signal<(Bool, Bool), NoError> {
|
||||
@@ -1532,8 +1652,8 @@ private func dropCallSession(network: Network, addUpdates: @escaping (Api.Update
|
||||
mappedReason = .phoneCallDiscardReasonDisconnect
|
||||
case .missed:
|
||||
mappedReason = .phoneCallDiscardReasonMissed
|
||||
case let .switchToConference(encryptedGroupKey):
|
||||
mappedReason = .phoneCallDiscardReasonAllowGroupCall(encryptedKey: Buffer(data: encryptedGroupKey))
|
||||
case let .switchToConference(slug):
|
||||
mappedReason = .phoneCallDiscardReasonMigrateConferenceCall(slug: slug)
|
||||
}
|
||||
|
||||
var callFlags: Int32 = 0
|
||||
@@ -1556,7 +1676,7 @@ private func dropCallSession(network: Network, addUpdates: @escaping (Api.Update
|
||||
switch update {
|
||||
case .updatePhoneCall(let phoneCall):
|
||||
switch phoneCall {
|
||||
case let .phoneCallDiscarded(flags, _, _, _, _):
|
||||
case let .phoneCallDiscarded(flags, _, _, _):
|
||||
reportRating = (flags & (1 << 2)) != 0
|
||||
sendDebugLogs = (flags & (1 << 3)) != 0
|
||||
default:
|
||||
@@ -1578,34 +1698,3 @@ private func dropCallSession(network: Network, addUpdates: @escaping (Api.Update
|
||||
}
|
||||
}
|
||||
|
||||
private func createConferenceCall(postbox: Postbox, network: Network, accountPeerId: PeerId, callId: CallId) -> Signal<GroupCallReference?, NoError> {
|
||||
return network.request(Api.functions.phone.createConferenceCall(peer: .inputPhoneCall(id: callId.id, accessHash: callId.accessHash), keyFingerprint: 1))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.phone.PhoneCall?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<GroupCallReference?, NoError> in
|
||||
guard let result else {
|
||||
return .single(nil)
|
||||
}
|
||||
return postbox.transaction { transaction -> GroupCallReference? in
|
||||
switch result {
|
||||
case let .phoneCall(phoneCall, users):
|
||||
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users))
|
||||
|
||||
switch phoneCall {
|
||||
case .phoneCallEmpty, .phoneCallRequested, .phoneCallAccepted, .phoneCallDiscarded:
|
||||
return nil
|
||||
case .phoneCallWaiting:
|
||||
return nil
|
||||
case let .phoneCall(_, _, _, _, _, _, _, _, _, _, _, _, conferenceCall):
|
||||
if let conferenceCall {
|
||||
return GroupCallReference(conferenceCall)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user