mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-17 19:09:56 +00:00
call fixes
This commit is contained in:
parent
c36925e399
commit
be4a6c4c69
@ -1,14 +1,17 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
import PostboxMac
|
import PostboxMac
|
||||||
import MtProtoKitMac
|
import MtProtoKitMac
|
||||||
import SwiftSignalKitMac
|
import SwiftSignalKitMac
|
||||||
#else
|
#else
|
||||||
import Postbox
|
import Postbox
|
||||||
import MtProtoKitDynamic
|
import MtProtoKitDynamic
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
let kCallMinLayer:Int32 = 65;
|
||||||
|
let kCallMaxLayer:Int32 = 75;
|
||||||
|
|
||||||
public enum CallSessionError: Equatable {
|
public enum CallSessionError: Equatable {
|
||||||
case generic
|
case generic
|
||||||
case privacyRestricted
|
case privacyRestricted
|
||||||
@ -18,36 +21,36 @@ public enum CallSessionError: Equatable {
|
|||||||
|
|
||||||
public static func ==(lhs: CallSessionError, rhs: CallSessionError) -> Bool {
|
public static func ==(lhs: CallSessionError, rhs: CallSessionError) -> Bool {
|
||||||
switch lhs {
|
switch lhs {
|
||||||
case .generic:
|
case .generic:
|
||||||
if case .generic = rhs {
|
if case .generic = rhs {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case .privacyRestricted:
|
case .privacyRestricted:
|
||||||
if case .privacyRestricted = rhs {
|
if case .privacyRestricted = rhs {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case .notSupportedByPeer:
|
case .notSupportedByPeer:
|
||||||
if case .notSupportedByPeer = rhs {
|
if case .notSupportedByPeer = rhs {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .serverProvided(text):
|
case let .serverProvided(text):
|
||||||
if case .serverProvided(text) = rhs {
|
if case .serverProvided(text) = rhs {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case .disconnected:
|
case .disconnected:
|
||||||
if case .disconnected = rhs {
|
if case .disconnected = rhs {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,18 +67,18 @@ public enum CallSessionTerminationReason: Equatable {
|
|||||||
|
|
||||||
public static func ==(lhs: CallSessionTerminationReason, rhs: CallSessionTerminationReason) -> Bool {
|
public static func ==(lhs: CallSessionTerminationReason, rhs: CallSessionTerminationReason) -> Bool {
|
||||||
switch lhs {
|
switch lhs {
|
||||||
case let .ended(type):
|
case let .ended(type):
|
||||||
if case .ended(type) = rhs {
|
if case .ended(type) = rhs {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .error(error):
|
case let .error(error):
|
||||||
if case .error(error) = rhs {
|
if case .error(error) = rhs {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,20 +128,20 @@ public enum CallSessionState {
|
|||||||
|
|
||||||
fileprivate init(_ context: CallSessionContext) {
|
fileprivate init(_ context: CallSessionContext) {
|
||||||
switch context.state {
|
switch context.state {
|
||||||
case .ringing:
|
case .ringing:
|
||||||
self = .ringing
|
self = .ringing
|
||||||
case .accepting, .awaitingConfirmation:
|
case .accepting, .awaitingConfirmation:
|
||||||
self = .accepting
|
self = .accepting
|
||||||
case .requesting, .confirming:
|
case .requesting, .confirming:
|
||||||
self = .requesting(ringing: false)
|
self = .requesting(ringing: false)
|
||||||
case let .requested(_, _, _, _, _, remoteConfirmationTimestamp):
|
case let .requested(_, _, _, _, _, remoteConfirmationTimestamp):
|
||||||
self = .requesting(ringing: remoteConfirmationTimestamp != nil)
|
self = .requesting(ringing: remoteConfirmationTimestamp != nil)
|
||||||
case let .active(_, _, _, key, _, keyVisualHash, connections, maxLayer):
|
case let .active(_, _, _, key, _, keyVisualHash, connections, maxLayer):
|
||||||
self = .active(key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer)
|
self = .active(key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer)
|
||||||
case .dropping:
|
case .dropping:
|
||||||
self = .dropping
|
self = .dropping
|
||||||
case let .terminated(reason, reportRating):
|
case let .terminated(reason, reportRating):
|
||||||
self = .terminated(reason: reason, reportRating: reportRating)
|
self = .terminated(reason: reason, reportRating: reportRating)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,8 +162,8 @@ public struct CallSessionConnection {
|
|||||||
|
|
||||||
private func parseConnection(_ apiConnection: Api.PhoneConnection) -> CallSessionConnection {
|
private func parseConnection(_ apiConnection: Api.PhoneConnection) -> CallSessionConnection {
|
||||||
switch apiConnection {
|
switch apiConnection {
|
||||||
case let .phoneConnection(id, ip, ipv6, port, peerTag):
|
case let .phoneConnection(id, ip, ipv6, port, peerTag):
|
||||||
return CallSessionConnection(id: id, ip: ip, ipv6: ipv6, port: port, peerTag: peerTag.makeData())
|
return CallSessionConnection(id: id, ip: ip, ipv6: ipv6, port: port, peerTag: peerTag.makeData())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,6 +204,7 @@ private final class CallSessionContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final class CallSessionManagerContext {
|
private final class CallSessionManagerContext {
|
||||||
|
|
||||||
private let queue: Queue
|
private let queue: Queue
|
||||||
private let postbox: Postbox
|
private let postbox: Postbox
|
||||||
private let network: Network
|
private let network: Network
|
||||||
@ -324,38 +328,38 @@ private final class CallSessionManagerContext {
|
|||||||
var dropData: (CallSessionStableId, Int64, DropCallSessionReason)?
|
var dropData: (CallSessionStableId, Int64, DropCallSessionReason)?
|
||||||
var wasRinging = false
|
var wasRinging = false
|
||||||
switch context.state {
|
switch context.state {
|
||||||
case let .ringing(id, accessHash, _, _):
|
case let .ringing(id, accessHash, _, _):
|
||||||
wasRinging = true
|
wasRinging = true
|
||||||
dropData = (id, accessHash, .busy)
|
dropData = (id, accessHash, .busy)
|
||||||
case let .accepting(id, accessHash, _, _, disposable):
|
case let .accepting(id, accessHash, _, _, disposable):
|
||||||
dropData = (id, accessHash, .abort)
|
dropData = (id, accessHash, .abort)
|
||||||
disposable.dispose()
|
disposable.dispose()
|
||||||
case let .active(id, accessHash, beginTimestamp, _, _, _, _, _):
|
case let .active(id, accessHash, beginTimestamp, _, _, _, _, _):
|
||||||
let duration = max(0, Int32(CFAbsoluteTimeGetCurrent()) - beginTimestamp)
|
let duration = max(0, Int32(CFAbsoluteTimeGetCurrent()) - beginTimestamp)
|
||||||
let internalReason: DropCallSessionReason
|
let internalReason: DropCallSessionReason
|
||||||
switch reason {
|
switch reason {
|
||||||
case .busy, .hangUp:
|
case .busy, .hangUp:
|
||||||
internalReason = .hangUp(duration)
|
internalReason = .hangUp(duration)
|
||||||
case .disconnect:
|
case .disconnect:
|
||||||
internalReason = .disconnect
|
internalReason = .disconnect
|
||||||
}
|
}
|
||||||
dropData = (id, accessHash, internalReason)
|
dropData = (id, accessHash, internalReason)
|
||||||
case .dropping, .terminated:
|
case .dropping, .terminated:
|
||||||
break
|
break
|
||||||
case let .awaitingConfirmation(id, accessHash, _, _, _):
|
case let .awaitingConfirmation(id, accessHash, _, _, _):
|
||||||
dropData = (id, accessHash, .abort)
|
dropData = (id, accessHash, .abort)
|
||||||
case let .confirming(id, accessHash, _, _, _, disposable):
|
case let .confirming(id, accessHash, _, _, _, disposable):
|
||||||
disposable.dispose()
|
disposable.dispose()
|
||||||
dropData = (id, accessHash, .abort)
|
dropData = (id, accessHash, .abort)
|
||||||
case let .requested(id, accessHash, _, _, _, _):
|
case let .requested(id, accessHash, _, _, _, _):
|
||||||
dropData = (id, accessHash, .busy)
|
dropData = (id, accessHash, .busy)
|
||||||
case let .requesting(_, disposable):
|
case let .requesting(_, disposable):
|
||||||
disposable.dispose()
|
disposable.dispose()
|
||||||
context.state = .terminated(reason: .ended(.hungUp), reportRating: nil)
|
context.state = .terminated(reason: .ended(.hungUp), reportRating: nil)
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
if context.isEmpty {
|
if context.isEmpty {
|
||||||
self.contexts.removeValue(forKey: internalId)
|
self.contexts.removeValue(forKey: internalId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (id, accessHash, reason) = dropData {
|
if let (id, accessHash, reason) = dropData {
|
||||||
@ -396,182 +400,191 @@ private final class CallSessionManagerContext {
|
|||||||
func accept(internalId: CallSessionInternalId) {
|
func accept(internalId: CallSessionInternalId) {
|
||||||
if let context = self.contexts[internalId] {
|
if let context = self.contexts[internalId] {
|
||||||
switch context.state {
|
switch context.state {
|
||||||
case let .ringing(id, accessHash, gAHash, b):
|
case let .ringing(id, accessHash, gAHash, b):
|
||||||
context.state = .accepting(id: id, accessHash: accessHash, gAHash: gAHash, b: b, disposable: (acceptCallSession(postbox: self.postbox, network: self.network, stableId: id, accessHash: accessHash, b: b) |> deliverOn(self.queue)).start(next: { [weak self] result in
|
context.state = .accepting(id: id, accessHash: accessHash, gAHash: gAHash, b: b, disposable: (acceptCallSession(postbox: self.postbox, network: self.network, stableId: id, accessHash: accessHash, b: b) |> deliverOn(self.queue)).start(next: { [weak self] result in
|
||||||
if let strongSelf = self, let context = strongSelf.contexts[internalId] {
|
if let strongSelf = self, let context = strongSelf.contexts[internalId] {
|
||||||
if case .accepting = context.state {
|
if case .accepting = context.state {
|
||||||
switch result {
|
switch result {
|
||||||
case .failed:
|
case .failed:
|
||||||
|
strongSelf.drop(internalId: internalId, reason: .disconnect)
|
||||||
|
case let .success(call):
|
||||||
|
switch call {
|
||||||
|
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):
|
||||||
|
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)
|
||||||
|
strongSelf.contextUpdated(internalId: internalId)
|
||||||
|
} else {
|
||||||
strongSelf.drop(internalId: internalId, reason: .disconnect)
|
strongSelf.drop(internalId: internalId, reason: .disconnect)
|
||||||
case let .success(call):
|
}
|
||||||
switch call {
|
|
||||||
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):
|
|
||||||
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: 77)
|
|
||||||
strongSelf.contextUpdated(internalId: internalId)
|
|
||||||
} else {
|
|
||||||
strongSelf.drop(internalId: internalId, reason: .disconnect)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}
|
||||||
self.contextUpdated(internalId: internalId)
|
}))
|
||||||
self.ringingStatesUpdated()
|
self.contextUpdated(internalId: internalId)
|
||||||
default:
|
self.ringingStatesUpdated()
|
||||||
break
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateSession(_ call: Api.PhoneCall) {
|
func updateSession(_ call: Api.PhoneCall) {
|
||||||
switch call {
|
switch call {
|
||||||
case .phoneCallEmpty:
|
case .phoneCallEmpty:
|
||||||
break
|
break
|
||||||
case let .phoneCallAccepted(id, _, _, _, _, gB, _):
|
case let .phoneCallAccepted(id, _, _, _, _, gB, _):
|
||||||
if let internalId = self.contextIdByStableId[id] {
|
if let internalId = self.contextIdByStableId[id] {
|
||||||
if let context = self.contexts[internalId] {
|
if let context = self.contexts[internalId] {
|
||||||
switch context.state {
|
switch context.state {
|
||||||
case let .requested(_, accessHash, a, gA, config, _):
|
case let .requested(_, accessHash, a, gA, config, _):
|
||||||
var key = MTExp(gB.makeData(), a, config.p.makeData())!
|
var key = MTExp(gB.makeData(), a, config.p.makeData())!
|
||||||
|
|
||||||
if key.count > 256 {
|
if key.count > 256 {
|
||||||
key.count = 256
|
key.count = 256
|
||||||
} else {
|
} else {
|
||||||
while key.count < 256 {
|
while key.count < 256 {
|
||||||
key.insert(0, at: 0)
|
key.insert(0, at: 0)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let keyHash = MTSha1(key)!
|
|
||||||
|
|
||||||
var keyId: Int64 = 0
|
|
||||||
keyHash.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
|
||||||
memcpy(&keyId, bytes.advanced(by: keyHash.count - 8), 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
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) |> 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)
|
|
||||||
} else {
|
|
||||||
strongSelf.drop(internalId: internalId, reason: .disconnect)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
self.contextUpdated(internalId: internalId)
|
|
||||||
default:
|
|
||||||
self.drop(internalId: internalId, reason: .disconnect)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assertionFailure()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case let .phoneCallDiscarded(flags, id, reason, duration):
|
|
||||||
let reportRating = (flags & (1 << 2)) != 0
|
|
||||||
if let internalId = self.contextIdByStableId[id] {
|
|
||||||
if let context = self.contexts[internalId] {
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
parsedReason = .ended(.hungUp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let keyHash = MTSha1(key)!
|
||||||
switch context.state {
|
|
||||||
case let .accepting(id, accessHash, _, _, disposable):
|
var keyId: Int64 = 0
|
||||||
disposable.dispose()
|
keyHash.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
||||||
context.state = .terminated(reason: parsedReason, reportRating: reportRating ? ReportCallRating(id: id, accessHash: accessHash) : nil)
|
memcpy(&keyId, bytes.advanced(by: keyHash.count - 8), 8)
|
||||||
self.contextUpdated(internalId: internalId)
|
|
||||||
case .active(let id, let accessHash, _, _, _, _, _, _):
|
|
||||||
context.state = .terminated(reason: parsedReason, reportRating: reportRating ? ReportCallRating(id: id, accessHash: accessHash) : nil)
|
|
||||||
self.contextUpdated(internalId: internalId)
|
|
||||||
case .awaitingConfirmation(let id, let accessHash, _, _, _):
|
|
||||||
context.state = .terminated(reason: parsedReason, reportRating: reportRating ? ReportCallRating(id: id, accessHash: accessHash) : nil)
|
|
||||||
self.contextUpdated(internalId: internalId)
|
|
||||||
case .requested(let id, let accessHash, _, _, _, _):
|
|
||||||
context.state = .terminated(reason: parsedReason, reportRating: reportRating ? ReportCallRating(id: id, accessHash: accessHash) : nil)
|
|
||||||
self.contextUpdated(internalId: internalId)
|
|
||||||
case let .confirming(id, accessHash, _, _, _, disposable):
|
|
||||||
disposable.dispose()
|
|
||||||
context.state = .terminated(reason: parsedReason, reportRating: reportRating ? ReportCallRating(id: id, accessHash: accessHash) : nil)
|
|
||||||
self.contextUpdated(internalId: internalId)
|
|
||||||
case let .requesting(_, disposable):
|
|
||||||
disposable.dispose()
|
|
||||||
context.state = .terminated(reason: parsedReason, reportRating: nil)
|
|
||||||
self.contextUpdated(internalId: internalId)
|
|
||||||
case .ringing(let id, let accesshash, _, _):
|
|
||||||
context.state = .terminated(reason: parsedReason, reportRating: reportRating ? ReportCallRating(id: id, accessHash: accesshash) : nil)
|
|
||||||
self.ringingStatesUpdated()
|
|
||||||
self.contextUpdated(internalId: internalId)
|
|
||||||
case .dropping, .terminated:
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
//assertionFailure()
|
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) |> deliverOnMainQueue).start(next: { [weak self] updatedCall in
|
||||||
case let .phoneCall(id, _, _, _, _, gAOrB, keyFingerprint, _, connection, alternativeConnections, startDate):
|
if let strongSelf = self, let context = strongSelf.contexts[internalId], case .confirming = context.state {
|
||||||
if let internalId = self.contextIdByStableId[id] {
|
if let updatedCall = updatedCall {
|
||||||
if let context = self.contexts[internalId] {
|
strongSelf.updateSession(updatedCall)
|
||||||
switch context.state {
|
|
||||||
case .accepting, .active, .dropping, .requesting, .ringing, .terminated, .requested:
|
|
||||||
break
|
|
||||||
case let .awaitingConfirmation(_, accessHash, gAHash, b, config):
|
|
||||||
if let (key, calculatedKeyId, keyVisualHash) = self.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gAOrB.makeData()) {
|
|
||||||
if keyFingerprint == calculatedKeyId {
|
|
||||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: calculatedKeyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connection, alternative: alternativeConnections), maxLayer: 77)
|
|
||||||
self.contextUpdated(internalId: internalId)
|
|
||||||
} else {
|
|
||||||
self.drop(internalId: internalId, reason: .disconnect)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
self.drop(internalId: internalId, reason: .disconnect)
|
strongSelf.drop(internalId: internalId, reason: .disconnect)
|
||||||
}
|
}
|
||||||
case let .confirming(id, accessHash, key, keyId, keyVisualHash, _):
|
}
|
||||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connection, alternative: alternativeConnections), maxLayer: 77)
|
}))
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
|
default:
|
||||||
|
self.drop(internalId: internalId, reason: .disconnect)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assertionFailure()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case let .phoneCallDiscarded(flags, id, reason, duration):
|
||||||
|
let reportRating = (flags & (1 << 2)) != 0
|
||||||
|
if let internalId = self.contextIdByStableId[id] {
|
||||||
|
if let context = self.contexts[internalId] {
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assertionFailure()
|
parsedReason = .ended(.hungUp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
switch context.state {
|
||||||
|
case let .accepting(id, accessHash, _, _, disposable):
|
||||||
|
disposable.dispose()
|
||||||
|
context.state = .terminated(reason: parsedReason, reportRating: reportRating ? ReportCallRating(id: id, accessHash: accessHash) : nil)
|
||||||
|
self.contextUpdated(internalId: internalId)
|
||||||
|
case .active(let id, let accessHash, _, _, _, _, _, _):
|
||||||
|
context.state = .terminated(reason: parsedReason, reportRating: reportRating ? ReportCallRating(id: id, accessHash: accessHash) : nil)
|
||||||
|
self.contextUpdated(internalId: internalId)
|
||||||
|
case .awaitingConfirmation(let id, let accessHash, _, _, _):
|
||||||
|
context.state = .terminated(reason: parsedReason, reportRating: reportRating ? ReportCallRating(id: id, accessHash: accessHash) : nil)
|
||||||
|
self.contextUpdated(internalId: internalId)
|
||||||
|
case .requested(let id, let accessHash, _, _, _, _):
|
||||||
|
context.state = .terminated(reason: parsedReason, reportRating: reportRating ? ReportCallRating(id: id, accessHash: accessHash) : nil)
|
||||||
|
self.contextUpdated(internalId: internalId)
|
||||||
|
case let .confirming(id, accessHash, _, _, _, disposable):
|
||||||
|
disposable.dispose()
|
||||||
|
context.state = .terminated(reason: parsedReason, reportRating: reportRating ? ReportCallRating(id: id, accessHash: accessHash) : nil)
|
||||||
|
self.contextUpdated(internalId: internalId)
|
||||||
|
case let .requesting(_, disposable):
|
||||||
|
disposable.dispose()
|
||||||
|
context.state = .terminated(reason: parsedReason, reportRating: nil)
|
||||||
|
self.contextUpdated(internalId: internalId)
|
||||||
|
case .ringing(let id, let accesshash, _, _):
|
||||||
|
context.state = .terminated(reason: parsedReason, reportRating: reportRating ? ReportCallRating(id: id, accessHash: accesshash) : nil)
|
||||||
|
self.ringingStatesUpdated()
|
||||||
|
self.contextUpdated(internalId: internalId)
|
||||||
|
case .dropping, .terminated:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//assertionFailure()
|
||||||
}
|
}
|
||||||
case let .phoneCallRequested(id, accessHash, date, adminId, _, gAHash, _):
|
}
|
||||||
if self.contextIdByStableId[id] == nil {
|
case let .phoneCall(id, _, _, _, _, gAOrB, keyFingerprint, callProtocol, connection, alternativeConnections, startDate):
|
||||||
self.addIncoming(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: adminId), stableId: id, accessHash: accessHash, timestamp: date, gAHash: gAHash.makeData())
|
if let internalId = self.contextIdByStableId[id] {
|
||||||
}
|
if let context = self.contexts[internalId] {
|
||||||
case let .phoneCallWaiting(_, id, _, _, _, _, _, receiveDate):
|
switch context.state {
|
||||||
if let internalId = self.contextIdByStableId[id] {
|
case .accepting, .active, .dropping, .requesting, .ringing, .terminated, .requested:
|
||||||
if let context = self.contexts[internalId] {
|
break
|
||||||
switch context.state {
|
case let .awaitingConfirmation(_, accessHash, gAHash, b, config):
|
||||||
case let .requested(id, accessHash, a, gA, config, remoteConfirmationTimestamp):
|
if let (key, calculatedKeyId, keyVisualHash) = self.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gAOrB.makeData()) {
|
||||||
if let receiveDate = receiveDate, remoteConfirmationTimestamp == nil {
|
if keyFingerprint == calculatedKeyId {
|
||||||
context.state = .requested(id: id, accessHash: accessHash, a: a, gA: gA, config: config, remoteConfirmationTimestamp: receiveDate)
|
switch callProtocol {
|
||||||
|
case let .phoneCallProtocol(_, _, maxLayer):
|
||||||
|
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: calculatedKeyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connection, alternative: alternativeConnections), maxLayer: maxLayer)
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
default:
|
} else {
|
||||||
break
|
self.drop(internalId: internalId, reason: .disconnect)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.drop(internalId: internalId, reason: .disconnect)
|
||||||
|
}
|
||||||
|
case let .confirming(id, accessHash, key, keyId, keyVisualHash, _):
|
||||||
|
switch callProtocol {
|
||||||
|
case let .phoneCallProtocol(_, _, maxLayer):
|
||||||
|
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connection, alternative: alternativeConnections), maxLayer: maxLayer)
|
||||||
|
self.contextUpdated(internalId: internalId)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
assertionFailure()
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
assertionFailure()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
case let .phoneCallRequested(id, accessHash, date, adminId, _, gAHash, _):
|
||||||
|
if self.contextIdByStableId[id] == nil {
|
||||||
|
self.addIncoming(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: adminId), stableId: id, accessHash: accessHash, timestamp: date, gAHash: gAHash.makeData())
|
||||||
|
}
|
||||||
|
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):
|
||||||
|
if let receiveDate = receiveDate, remoteConfirmationTimestamp == nil {
|
||||||
|
context.state = .requested(id: id, accessHash: accessHash, a: a, gA: gA, config: config, remoteConfirmationTimestamp: receiveDate)
|
||||||
|
self.contextUpdated(internalId: internalId)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assertionFailure()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,16 +624,16 @@ private final class CallSessionManagerContext {
|
|||||||
if let strongSelf = self, let context = strongSelf.contexts[internalId] {
|
if let strongSelf = self, let context = strongSelf.contexts[internalId] {
|
||||||
if case .requesting = context.state {
|
if case .requesting = context.state {
|
||||||
switch result {
|
switch result {
|
||||||
case let .success(id, accessHash, config, gA, remoteConfirmationTimestamp):
|
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)
|
||||||
strongSelf.contextIdByStableId[id] = internalId
|
strongSelf.contextIdByStableId[id] = internalId
|
||||||
strongSelf.contextUpdated(internalId: internalId)
|
strongSelf.contextUpdated(internalId: internalId)
|
||||||
case let .failed(error):
|
case let .failed(error):
|
||||||
context.state = .terminated(reason: .error(error), reportRating: nil)
|
context.state = .terminated(reason: .error(error), reportRating: nil)
|
||||||
strongSelf.contextUpdated(internalId: internalId)
|
strongSelf.contextUpdated(internalId: internalId)
|
||||||
if context.isEmpty {
|
if context.isEmpty {
|
||||||
strongSelf.contexts.removeValue(forKey: internalId)
|
strongSelf.contexts.removeValue(forKey: internalId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -736,7 +749,7 @@ public final class CallSessionManager {
|
|||||||
|
|
||||||
private enum AcceptedCall {
|
private enum AcceptedCall {
|
||||||
case waiting(config: SecretChatEncryptionConfig)
|
case waiting(config: SecretChatEncryptionConfig)
|
||||||
case call(config: SecretChatEncryptionConfig, gA: Data, timestamp: Int32, connections: CallSessionConnectionSet)
|
case call(config: SecretChatEncryptionConfig, gA: Data, timestamp: Int32, connections: CallSessionConnectionSet, maxLayer: Int32)
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum AcceptCallResult {
|
private enum AcceptCallResult {
|
||||||
@ -755,7 +768,7 @@ private func acceptCallSession(postbox: Postbox, network: Network, stableId: Cal
|
|||||||
|
|
||||||
let gb = MTExp(g, bData, p)!
|
let gb = MTExp(g, bData, p)!
|
||||||
|
|
||||||
return network.request(Api.functions.phone.acceptCall(peer: .inputPhoneCall(id: stableId, accessHash: accessHash), gB: Buffer(data: gb), protocol: .phoneCallProtocol(flags: (1 << 0) | (1 << 1), minLayer: 65, maxLayer: 66)))
|
return network.request(Api.functions.phone.acceptCall(peer: .inputPhoneCall(id: stableId, accessHash: accessHash), gB: Buffer(data: gb), protocol: .phoneCallProtocol(flags: (1 << 0) | (1 << 1), minLayer: kCallMinLayer, maxLayer: kCallMaxLayer)))
|
||||||
|> map { Optional($0) }
|
|> map { Optional($0) }
|
||||||
|> `catch` { _ -> Signal<Api.phone.PhoneCall?, NoError> in
|
|> `catch` { _ -> Signal<Api.phone.PhoneCall?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
@ -764,34 +777,37 @@ private func acceptCallSession(postbox: Postbox, network: Network, stableId: Cal
|
|||||||
if let call = call {
|
if let call = call {
|
||||||
return postbox.modify { modifier -> AcceptCallResult in
|
return postbox.modify { modifier -> AcceptCallResult in
|
||||||
switch call {
|
switch call {
|
||||||
case let .phoneCall(phoneCall, users):
|
case let .phoneCall(phoneCall, users):
|
||||||
var parsedUsers: [Peer] = []
|
var parsedUsers: [Peer] = []
|
||||||
for user in users {
|
for user in users {
|
||||||
parsedUsers.append(TelegramUser(user: user))
|
parsedUsers.append(TelegramUser(user: user))
|
||||||
}
|
}
|
||||||
updatePeers(modifier: modifier, peers: parsedUsers, update: { _, updated in
|
updatePeers(modifier: modifier, peers: parsedUsers, update: { _, updated in
|
||||||
return updated
|
return updated
|
||||||
})
|
})
|
||||||
|
|
||||||
switch phoneCall {
|
switch phoneCall {
|
||||||
case .phoneCallEmpty, .phoneCallRequested, .phoneCallAccepted, .phoneCallDiscarded:
|
case .phoneCallEmpty, .phoneCallRequested, .phoneCallAccepted, .phoneCallDiscarded:
|
||||||
return .failed
|
return .failed
|
||||||
case .phoneCallWaiting:
|
case .phoneCallWaiting:
|
||||||
return .success(.waiting(config: config))
|
return .success(.waiting(config: config))
|
||||||
case let .phoneCall(id, _, _, _, _, gAOrB, keyFingerprint, `protocol`, connection, alternativeConnections, startDate):
|
case let .phoneCall(id, _, _, _, _, gAOrB, keyFingerprint, callProtocol, connection, alternativeConnections, startDate):
|
||||||
if id == stableId {
|
if id == stableId {
|
||||||
return .success(.call(config: config, gA: gAOrB.makeData(), timestamp: startDate, connections: parseConnectionSet(primary: connection, alternative: alternativeConnections)))
|
switch callProtocol{
|
||||||
} else {
|
case let .phoneCallProtocol(_, _, maxLayer):
|
||||||
return .failed
|
return .success(.call(config: config, gA: gAOrB.makeData(), timestamp: startDate, connections: parseConnectionSet(primary: connection, alternative: alternativeConnections), maxLayer: maxLayer))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return .failed
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return .single(.failed)
|
return .single(.failed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum RequestCallSessionResult {
|
private enum RequestCallSessionResult {
|
||||||
@ -812,43 +828,43 @@ private func requestCallSession(postbox: Postbox, network: Network, peerId: Peer
|
|||||||
|
|
||||||
let gAHash = MTSha256(ga)!
|
let gAHash = MTSha256(ga)!
|
||||||
|
|
||||||
return network.request(Api.functions.phone.requestCall(userId: inputUser, randomId: Int32(bitPattern: arc4random()), gAHash: Buffer(data: gAHash), protocol: .phoneCallProtocol(flags: (1 << 0) | (1 << 1), minLayer: 65, maxLayer: 66)))
|
return network.request(Api.functions.phone.requestCall(userId: inputUser, randomId: Int32(bitPattern: arc4random()), gAHash: Buffer(data: gAHash), protocol: .phoneCallProtocol(flags: (1 << 0) | (1 << 1), minLayer: kCallMinLayer, maxLayer: kCallMaxLayer)))
|
||||||
|> map { result -> RequestCallSessionResult in
|
|> map { result -> RequestCallSessionResult in
|
||||||
switch result {
|
switch result {
|
||||||
case let .phoneCall(phoneCall, _):
|
case let .phoneCall(phoneCall, _):
|
||||||
switch 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)
|
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)
|
return .success(id: id, accessHash: accessHash, config: config, gA: ga, remoteConfirmationTimestamp: receiveDate)
|
||||||
default:
|
default:
|
||||||
return .failed(.generic)
|
return .failed(.generic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> `catch` { error -> Signal<RequestCallSessionResult, NoError> in
|
|> `catch` { error -> Signal<RequestCallSessionResult, NoError> in
|
||||||
switch error.errorDescription {
|
switch error.errorDescription {
|
||||||
case "PARTICIPANT_VERSION_OUTDATED":
|
case "PARTICIPANT_VERSION_OUTDATED":
|
||||||
return .single(.failed(.notSupportedByPeer))
|
return .single(.failed(.notSupportedByPeer))
|
||||||
case "USER_PRIVACY_RESTRICTED":
|
case "USER_PRIVACY_RESTRICTED":
|
||||||
return .single(.failed(.privacyRestricted))
|
return .single(.failed(.privacyRestricted))
|
||||||
default:
|
default:
|
||||||
if error.errorCode == 406 {
|
if error.errorCode == 406 {
|
||||||
return .single(.failed(.serverProvided(error.errorDescription)))
|
return .single(.failed(.serverProvided(error.errorDescription)))
|
||||||
} else {
|
} else {
|
||||||
return .single(.failed(.generic))
|
return .single(.failed(.generic))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return .single(.failed(.generic))
|
return .single(.failed(.generic))
|
||||||
}
|
}
|
||||||
} |> switchToLatest
|
} |> switchToLatest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func confirmCallSession(network: Network, stableId: CallSessionStableId, accessHash: Int64, gA: Data, keyFingerprint: Int64) -> Signal<Api.PhoneCall?, NoError> {
|
private func confirmCallSession(network: Network, stableId: CallSessionStableId, accessHash: Int64, gA: Data, keyFingerprint: Int64) -> Signal<Api.PhoneCall?, NoError> {
|
||||||
return network.request(Api.functions.phone.confirmCall(peer: Api.InputPhoneCall.inputPhoneCall(id: stableId, accessHash: accessHash), gA: Buffer(data: gA), keyFingerprint: keyFingerprint, protocol: .phoneCallProtocol(flags: (1 << 0) | (1 << 1), minLayer: 65, maxLayer: 66)))
|
return network.request(Api.functions.phone.confirmCall(peer: Api.InputPhoneCall.inputPhoneCall(id: stableId, accessHash: accessHash), gA: Buffer(data: gA), keyFingerprint: keyFingerprint, protocol: .phoneCallProtocol(flags: (1 << 0) | (1 << 1), minLayer: kCallMinLayer, maxLayer: kCallMaxLayer)))
|
||||||
|> map { Optional($0) }
|
|> map { Optional($0) }
|
||||||
|> `catch` { _ -> Signal<Api.phone.PhoneCall?, NoError> in
|
|> `catch` { _ -> Signal<Api.phone.PhoneCall?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
@ -856,13 +872,13 @@ private func confirmCallSession(network: Network, stableId: CallSessionStableId,
|
|||||||
|> map { result -> Api.PhoneCall? in
|
|> map { result -> Api.PhoneCall? in
|
||||||
if let result = result {
|
if let result = result {
|
||||||
switch result {
|
switch result {
|
||||||
case let .phoneCall(phoneCall, _):
|
case let .phoneCall(phoneCall, _):
|
||||||
return phoneCall
|
return phoneCall
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum DropCallSessionReason {
|
private enum DropCallSessionReason {
|
||||||
@ -876,15 +892,15 @@ private func dropCallSession(network: Network, addUpdates: @escaping (Api.Update
|
|||||||
var mappedReason: Api.PhoneCallDiscardReason
|
var mappedReason: Api.PhoneCallDiscardReason
|
||||||
var duration: Int32 = 0
|
var duration: Int32 = 0
|
||||||
switch reason {
|
switch reason {
|
||||||
case .abort:
|
case .abort:
|
||||||
mappedReason = .phoneCallDiscardReasonHangup
|
mappedReason = .phoneCallDiscardReasonHangup
|
||||||
case let .hangUp(value):
|
case let .hangUp(value):
|
||||||
duration = value
|
duration = value
|
||||||
mappedReason = .phoneCallDiscardReasonHangup
|
mappedReason = .phoneCallDiscardReasonHangup
|
||||||
case .busy:
|
case .busy:
|
||||||
mappedReason = .phoneCallDiscardReasonBusy
|
mappedReason = .phoneCallDiscardReasonBusy
|
||||||
case .disconnect:
|
case .disconnect:
|
||||||
mappedReason = .phoneCallDiscardReasonDisconnect
|
mappedReason = .phoneCallDiscardReasonDisconnect
|
||||||
}
|
}
|
||||||
return network.request(Api.functions.phone.discardCall(peer: Api.InputPhoneCall.inputPhoneCall(id: stableId, accessHash: accessHash), duration: duration, reason: mappedReason, connectionId: 0))
|
return network.request(Api.functions.phone.discardCall(peer: Api.InputPhoneCall.inputPhoneCall(id: stableId, accessHash: accessHash), duration: duration, reason: mappedReason, connectionId: 0))
|
||||||
|> map { Optional($0) }
|
|> map { Optional($0) }
|
||||||
@ -913,10 +929,10 @@ private func dropCallSession(network: Network, addUpdates: @escaping (Api.Update
|
|||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
addUpdates(updates)
|
addUpdates(updates)
|
||||||
|
|
||||||
}
|
}
|
||||||
return .single(report)
|
return .single(report)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user