mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 22:55:00 +00:00
[WIP] Conference calls
This commit is contained in:
@@ -73,32 +73,35 @@ extension GroupCallReference {
|
||||
}
|
||||
|
||||
enum CallSessionInternalState {
|
||||
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 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 awaitingConfirmation(id: Int64, accessHash: Int64, gAHash: Data, b: Data, config: SecretChatEncryptionConfig)
|
||||
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 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?)
|
||||
case switchedToConference(key: Data, keyVisualHash: Data, conferenceCall: GroupCallReference)
|
||||
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, _, _, _, _, _, _, _, _, _, _, _):
|
||||
return id
|
||||
case .switchedToConference:
|
||||
return nil
|
||||
case .dropping:
|
||||
return nil
|
||||
case let .terminated(id, _, _, _, _):
|
||||
@@ -161,8 +164,9 @@ public struct CallTerminationOptions: OptionSet {
|
||||
public enum CallSessionState {
|
||||
case ringing
|
||||
case accepting
|
||||
case requesting(ringing: Bool)
|
||||
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?)
|
||||
case switchedToConference(key: Data, keyVisualHash: Data, conferenceCall: GroupCallReference)
|
||||
case dropping(reason: CallSessionTerminationReason)
|
||||
case terminated(id: CallId?, reason: CallSessionTerminationReason, options: CallTerminationOptions)
|
||||
|
||||
@@ -172,12 +176,12 @@ public enum CallSessionState {
|
||||
self = .ringing
|
||||
case .accepting, .awaitingConfirmation:
|
||||
self = .accepting
|
||||
case .requesting:
|
||||
self = .requesting(ringing: false)
|
||||
case .confirming:
|
||||
self = .requesting(ringing: true)
|
||||
case let .requested(_, _, _, _, _, remoteConfirmationTimestamp):
|
||||
self = .requesting(ringing: remoteConfirmationTimestamp != nil)
|
||||
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):
|
||||
self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, conferenceCall: conferenceCall)
|
||||
case let .dropping(reason, _):
|
||||
@@ -197,6 +201,8 @@ public enum CallSessionState {
|
||||
callId = nil
|
||||
}
|
||||
self = .terminated(id: callId, reason: reason, options: options)
|
||||
case let .switchedToConference(key, keyVisualHash, conferenceCall):
|
||||
self = .switchedToConference(key: key, keyVisualHash: keyVisualHash, conferenceCall: conferenceCall)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -330,6 +336,7 @@ private final class CallSessionContext {
|
||||
let isOutgoing: 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)?
|
||||
@@ -346,11 +353,12 @@ private final class CallSessionContext {
|
||||
}
|
||||
}
|
||||
|
||||
init(peerId: PeerId, isOutgoing: Bool, type: CallSession.CallType, isVideoPossible: Bool, state: CallSessionInternalState) {
|
||||
init(peerId: PeerId, isOutgoing: Bool, type: CallSession.CallType, isVideoPossible: Bool, pendingConference: (conference: GroupCallReference, encryptionKey: Data)?, state: CallSessionInternalState) {
|
||||
self.peerId = peerId
|
||||
self.isOutgoing = isOutgoing
|
||||
self.type = type
|
||||
self.isVideoPossible = isVideoPossible
|
||||
self.pendingConference = pendingConference
|
||||
self.state = state
|
||||
}
|
||||
|
||||
@@ -566,7 +574,7 @@ private final class CallSessionManagerContext {
|
||||
}
|
||||
}
|
||||
|
||||
private func addIncoming(peerId: PeerId, stableId: CallSessionStableId, accessHash: Int64, timestamp: Int32, gAHash: Data, versions: [String], isVideo: Bool) -> CallSessionInternalId? {
|
||||
private func addIncoming(peerId: PeerId, stableId: CallSessionStableId, accessHash: Int64, timestamp: Int32, gAHash: Data, versions: [String], isVideo: Bool, conferenceCall: GroupCallReference?) -> CallSessionInternalId? {
|
||||
if self.contextIdByStableId[stableId] != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -582,7 +590,7 @@ private final class CallSessionManagerContext {
|
||||
//#endif
|
||||
|
||||
let internalId = CallSessionManager.getStableIncomingUUID(stableId: stableId)
|
||||
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))
|
||||
let context = CallSessionContext(peerId: peerId, isOutgoing: false, type: isVideo ? .video : .audio, isVideoPossible: isVideoPossible, pendingConference: nil, state: .ringing(id: stableId, accessHash: accessHash, gAHash: gAHash, b: b, versions: versions, conferenceCall: conferenceCall))
|
||||
self.contexts[internalId] = context
|
||||
let queue = self.queue
|
||||
|
||||
@@ -619,7 +627,7 @@ private final class CallSessionManagerContext {
|
||||
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 {
|
||||
@@ -633,7 +641,7 @@ private final class CallSessionManagerContext {
|
||||
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, _, _, _, _, _, _, _, _, _):
|
||||
@@ -648,14 +656,16 @@ private final class CallSessionManagerContext {
|
||||
internalReason = .missed
|
||||
}
|
||||
dropData = (id, accessHash, internalReason)
|
||||
case .switchedToConference:
|
||||
break
|
||||
case .dropping, .terminated:
|
||||
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:
|
||||
@@ -666,7 +676,7 @@ private final class CallSessionManagerContext {
|
||||
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)
|
||||
@@ -689,6 +699,8 @@ private final class CallSessionManagerContext {
|
||||
mappedReason = .ended(.hungUp)
|
||||
case .missed:
|
||||
mappedReason = .ended(.missed)
|
||||
case .switchToConference:
|
||||
mappedReason = .ended(.hungUp)
|
||||
}
|
||||
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
|
||||
@@ -729,6 +741,38 @@ private final class CallSessionManagerContext {
|
||||
}
|
||||
}
|
||||
|
||||
func dropToConference(internalId: CallSessionInternalId, encryptedGroupKey: Data) {
|
||||
if let context = self.contexts[internalId] {
|
||||
var dropData: (CallSessionStableId, Int64)?
|
||||
let isVideo = context.type == .video
|
||||
switch context.state {
|
||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, _):
|
||||
dropData = (id, accessHash)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if let (id, accessHash) = dropData {
|
||||
self.contextIdByStableId.removeValue(forKey: id)
|
||||
context.state = .dropping(reason: .ended(.hungUp), disposable: (dropCallSession(network: self.network, addUpdates: self.addUpdates, stableId: id, accessHash: accessHash, isVideo: isVideo, reason: .switchToConference(encryptedGroupKey: encryptedGroupKey))
|
||||
|> 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)
|
||||
strongSelf.contextUpdated(internalId: internalId)
|
||||
if context.isEmpty {
|
||||
strongSelf.contexts.removeValue(forKey: internalId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
self.contextUpdated(internalId: internalId)
|
||||
}
|
||||
} else {
|
||||
self.contextUpdated(internalId: internalId)
|
||||
}
|
||||
}
|
||||
|
||||
func dropAll() {
|
||||
let contexts = self.contexts
|
||||
for (internalId, _) in contexts {
|
||||
@@ -739,9 +783,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, _):
|
||||
case let .ringing(id, accessHash, gAHash, b, _, conferenceCall):
|
||||
let acceptVersions = self.versions.map({ $0.version })
|
||||
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
|
||||
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
|
||||
if let strongSelf = self, let context = strongSelf.contexts[internalId] {
|
||||
if case .accepting = context.state {
|
||||
switch result {
|
||||
@@ -835,7 +879,7 @@ private final class CallSessionManagerContext {
|
||||
switch call {
|
||||
case .phoneCallEmpty:
|
||||
break
|
||||
case let .phoneCallAccepted(_, id, _, _, _, _, gB, remoteProtocol):
|
||||
case let .phoneCallAccepted(_, id, _, _, _, _, gB, remoteProtocol, conferenceCall):
|
||||
let remoteVersions: [String]
|
||||
switch remoteProtocol {
|
||||
case let .phoneCallProtocol(_, _, _, versions):
|
||||
@@ -849,7 +893,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))
|
||||
@@ -874,10 +918,14 @@ private final class CallSessionManagerContext {
|
||||
|
||||
let keyVisualHash = MTSha256(key + gA)
|
||||
|
||||
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
|
||||
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
|
||||
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))
|
||||
}
|
||||
@@ -891,7 +939,8 @@ private final class CallSessionManagerContext {
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
case let .phoneCallDiscarded(flags, id, reason, _):
|
||||
case let .phoneCallDiscarded(flags, id, reason, _, conferenceCall):
|
||||
let _ = conferenceCall
|
||||
let reportRating = (flags & (1 << 2)) != 0
|
||||
let sendDebugLogs = (flags & (1 << 3)) != 0
|
||||
if let internalId = self.contextIdByStableId[id] {
|
||||
@@ -899,47 +948,53 @@ private final class CallSessionManagerContext {
|
||||
let parsedReason: CallSessionTerminationReason
|
||||
if let reason = reason {
|
||||
switch reason {
|
||||
case .phoneCallDiscardReasonBusy:
|
||||
parsedReason = .ended(.busy)
|
||||
case .phoneCallDiscardReasonDisconnect:
|
||||
parsedReason = .error(.disconnected)
|
||||
case .phoneCallDiscardReasonHangup:
|
||||
parsedReason = .ended(.hungUp)
|
||||
case .phoneCallDiscardReasonMissed:
|
||||
parsedReason = .ended(.missed)
|
||||
case .phoneCallDiscardReasonBusy:
|
||||
parsedReason = .ended(.busy)
|
||||
case .phoneCallDiscardReasonDisconnect:
|
||||
parsedReason = .error(.disconnected)
|
||||
case .phoneCallDiscardReasonHangup:
|
||||
parsedReason = .ended(.hungUp)
|
||||
case .phoneCallDiscardReasonMissed:
|
||||
parsedReason = .ended(.missed)
|
||||
case .phoneCallDiscardReasonAllowGroupCall:
|
||||
parsedReason = .ended(.hungUp)
|
||||
}
|
||||
} else {
|
||||
parsedReason = .ended(.hungUp)
|
||||
}
|
||||
|
||||
switch context.state {
|
||||
case let .accepting(id, accessHash, _, _, disposable):
|
||||
disposable.dispose()
|
||||
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)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
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, _, _, _, _):
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
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):
|
||||
disposable.dispose()
|
||||
context.state = .terminated(id: nil, accessHash: nil, reason: parsedReason, reportRating: false, sendDebugLogs: false)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .ringing(id, accessHash, _, _, _):
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
self.ringingStatesUpdated()
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case .dropping, .terminated:
|
||||
break
|
||||
}
|
||||
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, _, _, _, _, _):
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
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):
|
||||
disposable.dispose()
|
||||
context.state = .terminated(id: nil, accessHash: nil, reason: parsedReason, reportRating: false, sendDebugLogs: false)
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case let .ringing(id, accessHash, _, _, _, _):
|
||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||
self.ringingStatesUpdated()
|
||||
self.contextUpdated(internalId: internalId)
|
||||
case .dropping, .terminated, .switchedToConference:
|
||||
break
|
||||
}
|
||||
} else {
|
||||
//assertionFailure()
|
||||
@@ -950,7 +1005,7 @@ private final class CallSessionManagerContext {
|
||||
if let internalId = self.contextIdByStableId[id] {
|
||||
if let context = self.contexts[internalId] {
|
||||
switch context.state {
|
||||
case .accepting, .active, .dropping, .requesting, .ringing, .terminated, .requested:
|
||||
case .accepting, .active, .dropping, .requesting, .ringing, .terminated, .requested, .switchedToConference:
|
||||
break
|
||||
case let .awaitingConfirmation(_, accessHash, gAHash, b, config):
|
||||
if let (key, calculatedKeyId, keyVisualHash) = self.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gAOrB.makeData()) {
|
||||
@@ -981,7 +1036,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 {
|
||||
@@ -1007,7 +1062,7 @@ private final class CallSessionManagerContext {
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, gAHash, requestedProtocol):
|
||||
case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, gAHash, requestedProtocol, conferenceCall):
|
||||
let isVideo = (flags & (1 << 6)) != 0
|
||||
let versions: [String]
|
||||
switch requestedProtocol {
|
||||
@@ -1015,7 +1070,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)
|
||||
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))
|
||||
if let internalId = internalId {
|
||||
var resultRingingStateValue: CallSessionRingingState?
|
||||
for ringingState in self.ringingStatesValue() {
|
||||
@@ -1032,13 +1087,13 @@ private final class CallSessionManagerContext {
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .phoneCallWaiting(_, id, _, _, _, _, _, receiveDate):
|
||||
case let .phoneCallWaiting(_, id, _, _, _, _, _, receiveDate, conferenceCall):
|
||||
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)
|
||||
context.state = .requested(id: id, accessHash: accessHash, a: a, gA: gA, config: config, remoteConfirmationTimestamp: receiveDate, conferenceCall: conferenceCall.flatMap(GroupCallReference.init))
|
||||
self.contextUpdated(internalId: internalId)
|
||||
}
|
||||
default:
|
||||
@@ -1101,17 +1156,17 @@ private final class CallSessionManagerContext {
|
||||
return (key, keyId, keyVisualHash)
|
||||
}
|
||||
|
||||
func request(peerId: PeerId, internalId: CallSessionInternalId, isVideo: Bool, enableVideo: Bool) -> CallSessionInternalId? {
|
||||
func request(peerId: PeerId, internalId: CallSessionInternalId, isVideo: Bool, enableVideo: Bool, conferenceCall: (conference: GroupCallReference, encryptionKey: Data)?) -> 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, 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
|
||||
self.contexts[internalId] = CallSessionContext(peerId: peerId, isOutgoing: true, 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
|
||||
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)
|
||||
context.state = .requested(id: id, accessHash: accessHash, a: a, gA: gA, config: config, remoteConfirmationTimestamp: remoteConfirmationTimestamp, conferenceCall: conferenceCall?.conference)
|
||||
strongSelf.contextIdByStableId[id] = internalId
|
||||
strongSelf.contextUpdated(internalId: internalId)
|
||||
strongSelf.deliverCallSignalingData(id: id)
|
||||
@@ -1206,12 +1261,12 @@ public final class CallSessionManager {
|
||||
}
|
||||
}
|
||||
|
||||
public func request(peerId: PeerId, isVideo: Bool, enableVideo: Bool, internalId: CallSessionInternalId = CallSessionInternalId()) -> Signal<CallSessionInternalId, NoError> {
|
||||
public func request(peerId: PeerId, isVideo: Bool, enableVideo: Bool, conferenceCall: (conference: GroupCallReference, encryptionKey: Data)?, 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) {
|
||||
if let internalId = context.request(peerId: peerId, internalId: internalId, isVideo: isVideo, enableVideo: enableVideo, conferenceCall: conferenceCall) {
|
||||
subscriber.putNext(internalId)
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
@@ -1358,7 +1413,7 @@ private enum RequestCallSessionResult {
|
||||
case failed(CallSessionError)
|
||||
}
|
||||
|
||||
private func requestCallSession(postbox: Postbox, network: Network, peerId: PeerId, a: Data, maxLayer: Int32, versions: [String], isVideo: Bool) -> Signal<RequestCallSessionResult, NoError> {
|
||||
private func requestCallSession(postbox: Postbox, network: Network, peerId: PeerId, a: Data, maxLayer: Int32, versions: [String], isVideo: Bool, conferenceCall: GroupCallReference?) -> Signal<RequestCallSessionResult, NoError> {
|
||||
return validatedEncryptionConfig(postbox: postbox, network: network)
|
||||
|> mapToSignal { config -> Signal<RequestCallSessionResult, NoError> in
|
||||
return postbox.transaction { transaction -> Signal<RequestCallSessionResult, NoError> in
|
||||
@@ -1378,15 +1433,18 @@ 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, 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, 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)))
|
||||
|> 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)
|
||||
@@ -1439,23 +1497,26 @@ private enum DropCallSessionReason {
|
||||
case busy
|
||||
case disconnect
|
||||
case missed
|
||||
case switchToConference(encryptedGroupKey: Data)
|
||||
}
|
||||
|
||||
private func dropCallSession(network: Network, addUpdates: @escaping (Api.Updates) -> Void, stableId: CallSessionStableId, accessHash: Int64, isVideo: Bool, reason: DropCallSessionReason) -> Signal<(Bool, Bool), NoError> {
|
||||
var mappedReason: Api.PhoneCallDiscardReason
|
||||
var duration: Int32 = 0
|
||||
switch reason {
|
||||
case .abort:
|
||||
mappedReason = .phoneCallDiscardReasonHangup
|
||||
case let .hangUp(value):
|
||||
duration = value
|
||||
mappedReason = .phoneCallDiscardReasonHangup
|
||||
case .busy:
|
||||
mappedReason = .phoneCallDiscardReasonBusy
|
||||
case .disconnect:
|
||||
mappedReason = .phoneCallDiscardReasonDisconnect
|
||||
case .missed:
|
||||
mappedReason = .phoneCallDiscardReasonMissed
|
||||
case .abort:
|
||||
mappedReason = .phoneCallDiscardReasonHangup
|
||||
case let .hangUp(value):
|
||||
duration = value
|
||||
mappedReason = .phoneCallDiscardReasonHangup
|
||||
case .busy:
|
||||
mappedReason = .phoneCallDiscardReasonBusy
|
||||
case .disconnect:
|
||||
mappedReason = .phoneCallDiscardReasonDisconnect
|
||||
case .missed:
|
||||
mappedReason = .phoneCallDiscardReasonMissed
|
||||
case let .switchToConference(encryptedGroupKey):
|
||||
mappedReason = .phoneCallDiscardReasonAllowGroupCall(encryptedKey: Buffer(data: encryptedGroupKey))
|
||||
}
|
||||
|
||||
var callFlags: Int32 = 0
|
||||
@@ -1478,7 +1539,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:
|
||||
@@ -1501,33 +1562,7 @@ private func dropCallSession(network: Network, addUpdates: @escaping (Api.Update
|
||||
}
|
||||
|
||||
private func createConferenceCall(postbox: Postbox, network: Network, accountPeerId: PeerId, callId: CallId) -> Signal<GroupCallReference?, NoError> {
|
||||
#if DEBUG && false
|
||||
if "".isEmpty {
|
||||
return _internal_resolvePeerByName(postbox: postbox, network: network, accountPeerId: accountPeerId, name: "qwfqwfqwefqwef22", referrer: nil)
|
||||
|> mapToSignal { result -> Signal<PeerId?, NoError> in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .never()
|
||||
case let .result(peerId):
|
||||
return .single(peerId)
|
||||
}
|
||||
}
|
||||
|> mapToSignal { peerId -> Signal<GroupCallReference?, NoError> in
|
||||
guard let peerId else {
|
||||
return .single(nil)
|
||||
}
|
||||
return _internal_updatedCurrentPeerGroupCall(postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId)
|
||||
|> map { result -> GroupCallReference? in
|
||||
guard let result else {
|
||||
return nil
|
||||
}
|
||||
return GroupCallReference(id: result.id, accessHash: result.accessHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return network.request(Api.functions.phone.createConferenceCall(peer: .inputPhoneCall(id: callId.id, accessHash: callId.accessHash)))
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user