mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-01-19 05:31:22 +00:00
Discard call in case of an error.
Also add a couple of call error messages.
This commit is contained in:
parent
061bd109d2
commit
c78cc331d1
@ -1141,6 +1141,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||||||
"lng_call_error_not_available" = "Sorry, {user} doesn't accept calls.";
|
"lng_call_error_not_available" = "Sorry, {user} doesn't accept calls.";
|
||||||
"lng_call_error_incompatible" = "{user}'s app is using an incompatible protocol. They need to update their app before you can call them.";
|
"lng_call_error_incompatible" = "{user}'s app is using an incompatible protocol. They need to update their app before you can call them.";
|
||||||
"lng_call_error_outdated" = "{user}'s app does not support calls. They need to update their app before you can call them.";
|
"lng_call_error_outdated" = "{user}'s app does not support calls. They need to update their app before you can call them.";
|
||||||
|
"lng_call_error_audio_io" = "There seems to be a problem with audio playback on your computer. Please make sure that your computer's speakers and microphone are working and try again.";
|
||||||
|
|
||||||
"lng_call_bar_info" = "Show call info";
|
"lng_call_bar_info" = "Show call info";
|
||||||
"lng_call_bar_hangup" = "End call";
|
"lng_call_bar_hangup" = "End call";
|
||||||
|
@ -93,7 +93,7 @@ void Call::generateModExpFirst(base::const_byte_span randomSeed) {
|
|||||||
auto first = MTP::CreateModExp(_dhConfig.g, _dhConfig.p, randomSeed);
|
auto first = MTP::CreateModExp(_dhConfig.g, _dhConfig.p, randomSeed);
|
||||||
if (first.modexp.empty()) {
|
if (first.modexp.empty()) {
|
||||||
LOG(("Call Error: Could not compute mod-exp first."));
|
LOG(("Call Error: Could not compute mod-exp first."));
|
||||||
setState(State::Failed);
|
finish(FinishType::Failed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ void Call::start(base::const_byte_span random) {
|
|||||||
t_assert(!_dhConfig.p.empty());
|
t_assert(!_dhConfig.p.empty());
|
||||||
|
|
||||||
generateModExpFirst(random);
|
generateModExpFirst(random);
|
||||||
if (_state != State::Failed) {
|
if (_state != State::Failed && _state != State::FailedHangingUp) {
|
||||||
if (_type == Type::Outgoing) {
|
if (_type == Type::Outgoing) {
|
||||||
startOutgoing();
|
startOutgoing();
|
||||||
} else {
|
} else {
|
||||||
@ -136,11 +136,14 @@ void Call::startOutgoing() {
|
|||||||
setState(State::Requesting);
|
setState(State::Requesting);
|
||||||
request(MTPphone_RequestCall(_user->inputUser, MTP_int(rand_value<int32>()), MTP_bytes(_gaHash), MTP_phoneCallProtocol(MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p | MTPDphoneCallProtocol::Flag::f_udp_reflector), MTP_int(kMinLayer), MTP_int(kMaxLayer)))).done([this](const MTPphone_PhoneCall &result) {
|
request(MTPphone_RequestCall(_user->inputUser, MTP_int(rand_value<int32>()), MTP_bytes(_gaHash), MTP_phoneCallProtocol(MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p | MTPDphoneCallProtocol::Flag::f_udp_reflector), MTP_int(kMinLayer), MTP_int(kMaxLayer)))).done([this](const MTPphone_PhoneCall &result) {
|
||||||
Expects(result.type() == mtpc_phone_phoneCall);
|
Expects(result.type() == mtpc_phone_phoneCall);
|
||||||
|
|
||||||
|
setState(State::Waiting);
|
||||||
|
|
||||||
auto &call = result.c_phone_phoneCall();
|
auto &call = result.c_phone_phoneCall();
|
||||||
App::feedUsers(call.vusers);
|
App::feedUsers(call.vusers);
|
||||||
if (call.vphone_call.type() != mtpc_phoneCallWaiting) {
|
if (call.vphone_call.type() != mtpc_phoneCallWaiting) {
|
||||||
LOG(("Call Error: Expected phoneCallWaiting in response to phone.requestCall()"));
|
LOG(("Call Error: Expected phoneCallWaiting in response to phone.requestCall()"));
|
||||||
setState(State::Failed);
|
finish(FinishType::Failed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,10 +151,12 @@ void Call::startOutgoing() {
|
|||||||
auto &waitingCall = phoneCall.c_phoneCallWaiting();
|
auto &waitingCall = phoneCall.c_phoneCallWaiting();
|
||||||
_id = waitingCall.vid.v;
|
_id = waitingCall.vid.v;
|
||||||
_accessHash = waitingCall.vaccess_hash.v;
|
_accessHash = waitingCall.vaccess_hash.v;
|
||||||
setState(State::Waiting);
|
if (_finishAfterRequestingCall != FinishType::None) {
|
||||||
|
if (_finishAfterRequestingCall == FinishType::Failed) {
|
||||||
if (_finishAfterRequestingCall) {
|
finish(_finishAfterRequestingCall);
|
||||||
|
} else {
|
||||||
hangup();
|
hangup();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +193,7 @@ void Call::answer() {
|
|||||||
App::feedUsers(call.vusers);
|
App::feedUsers(call.vusers);
|
||||||
if (call.vphone_call.type() != mtpc_phoneCallWaiting) {
|
if (call.vphone_call.type() != mtpc_phoneCallWaiting) {
|
||||||
LOG(("Call Error: Expected phoneCallWaiting in response to phone.acceptCall()"));
|
LOG(("Call Error: Expected phoneCallWaiting in response to phone.acceptCall()"));
|
||||||
setState(State::Failed);
|
finish(FinishType::Failed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +223,7 @@ void Call::hangup() {
|
|||||||
auto declined = (_state == State::WaitingIncoming);
|
auto declined = (_state == State::WaitingIncoming);
|
||||||
auto reason = missed ? MTP_phoneCallDiscardReasonMissed() :
|
auto reason = missed ? MTP_phoneCallDiscardReasonMissed() :
|
||||||
declined ? MTP_phoneCallDiscardReasonBusy() : MTP_phoneCallDiscardReasonHangup();
|
declined ? MTP_phoneCallDiscardReasonBusy() : MTP_phoneCallDiscardReasonHangup();
|
||||||
finish(reason);
|
finish(FinishType::Ended, reason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,7 +287,7 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
|
|||||||
}
|
}
|
||||||
if (AuthSession::CurrentUserId() != data.vparticipant_id.v) {
|
if (AuthSession::CurrentUserId() != data.vparticipant_id.v) {
|
||||||
LOG(("Call Error: Wrong call participant_id %1, expected %2.").arg(data.vparticipant_id.v).arg(AuthSession::CurrentUserId()));
|
LOG(("Call Error: Wrong call participant_id %1, expected %2.").arg(data.vparticipant_id.v).arg(AuthSession::CurrentUserId()));
|
||||||
setState(State::Failed);
|
finish(FinishType::Failed);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
_id = data.vid.v;
|
_id = data.vid.v;
|
||||||
@ -291,7 +296,7 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
|
|||||||
auto gaHashBytes = bytesFromMTP(data.vg_a_hash);
|
auto gaHashBytes = bytesFromMTP(data.vg_a_hash);
|
||||||
if (gaHashBytes.size() != _gaHash.size()) {
|
if (gaHashBytes.size() != _gaHash.size()) {
|
||||||
LOG(("Call Error: Wrong g_a_hash size %1, expected %2.").arg(gaHashBytes.size()).arg(_gaHash.size()));
|
LOG(("Call Error: Wrong g_a_hash size %1, expected %2.").arg(gaHashBytes.size()).arg(_gaHash.size()));
|
||||||
setState(State::Failed);
|
finish(FinishType::Failed);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
base::copy_bytes(gsl::make_span(_gaHash), gaHashBytes);
|
base::copy_bytes(gsl::make_span(_gaHash), gaHashBytes);
|
||||||
@ -303,7 +308,7 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LOG(("Call Error: phoneCallEmpty received."));
|
LOG(("Call Error: phoneCallEmpty received."));
|
||||||
setState(State::Failed);
|
finish(FinishType::Failed);
|
||||||
} return true;
|
} return true;
|
||||||
|
|
||||||
case mtpc_phoneCallWaiting: {
|
case mtpc_phoneCallWaiting: {
|
||||||
@ -358,7 +363,7 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
|
|||||||
}
|
}
|
||||||
if (_type != Type::Outgoing) {
|
if (_type != Type::Outgoing) {
|
||||||
LOG(("Call Error: Unexpected phoneCallAccepted for an incoming call."));
|
LOG(("Call Error: Unexpected phoneCallAccepted for an incoming call."));
|
||||||
setState(State::Failed);
|
finish(FinishType::Failed);
|
||||||
} else if (checkCallFields(data)) {
|
} else if (checkCallFields(data)) {
|
||||||
confirmAcceptedCall(data);
|
confirmAcceptedCall(data);
|
||||||
}
|
}
|
||||||
@ -375,7 +380,7 @@ void Call::confirmAcceptedCall(const MTPDphoneCallAccepted &call) {
|
|||||||
auto computedAuthKey = MTP::CreateAuthKey(firstBytes, _randomPower, _dhConfig.p);
|
auto computedAuthKey = MTP::CreateAuthKey(firstBytes, _randomPower, _dhConfig.p);
|
||||||
if (computedAuthKey.empty()) {
|
if (computedAuthKey.empty()) {
|
||||||
LOG(("Call Error: Could not compute mod-exp final."));
|
LOG(("Call Error: Could not compute mod-exp final."));
|
||||||
setState(State::Failed);
|
finish(FinishType::Failed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,7 +394,7 @@ void Call::confirmAcceptedCall(const MTPDphoneCallAccepted &call) {
|
|||||||
App::feedUsers(call.vusers);
|
App::feedUsers(call.vusers);
|
||||||
if (call.vphone_call.type() != mtpc_phoneCall) {
|
if (call.vphone_call.type() != mtpc_phoneCall) {
|
||||||
LOG(("Call Error: Expected phoneCall in response to phone.confirmCall()"));
|
LOG(("Call Error: Expected phoneCall in response to phone.confirmCall()"));
|
||||||
setState(State::Failed);
|
finish(FinishType::Failed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,7 +410,7 @@ void Call::startConfirmedCall(const MTPDphoneCall &call) {
|
|||||||
auto firstBytes = bytesFromMTP(call.vg_a_or_b);
|
auto firstBytes = bytesFromMTP(call.vg_a_or_b);
|
||||||
if (_gaHash != openssl::Sha256(firstBytes)) {
|
if (_gaHash != openssl::Sha256(firstBytes)) {
|
||||||
LOG(("Call Error: Wrong g_a hash received."));
|
LOG(("Call Error: Wrong g_a hash received."));
|
||||||
setState(State::Failed);
|
finish(FinishType::Failed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_ga = base::byte_vector(firstBytes.begin(), firstBytes.end());
|
_ga = base::byte_vector(firstBytes.begin(), firstBytes.end());
|
||||||
@ -413,7 +418,7 @@ void Call::startConfirmedCall(const MTPDphoneCall &call) {
|
|||||||
auto computedAuthKey = MTP::CreateAuthKey(firstBytes, _randomPower, _dhConfig.p);
|
auto computedAuthKey = MTP::CreateAuthKey(firstBytes, _randomPower, _dhConfig.p);
|
||||||
if (computedAuthKey.empty()) {
|
if (computedAuthKey.empty()) {
|
||||||
LOG(("Call Error: Could not compute mod-exp final."));
|
LOG(("Call Error: Could not compute mod-exp final."));
|
||||||
setState(State::Failed);
|
finish(FinishType::Failed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,7 +509,7 @@ void Call::handleControllerStateChange(tgvoip::VoIPController *controller, int s
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
bool Call::checkCallCommonFields(const T &call) {
|
bool Call::checkCallCommonFields(const T &call) {
|
||||||
auto checkFailed = [this] {
|
auto checkFailed = [this] {
|
||||||
setState(State::Failed);
|
finish(FinishType::Failed);
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
if (call.vaccess_hash.v != _accessHash) {
|
if (call.vaccess_hash.v != _accessHash) {
|
||||||
@ -530,7 +535,7 @@ bool Call::checkCallFields(const MTPDphoneCall &call) {
|
|||||||
}
|
}
|
||||||
if (call.vkey_fingerprint.v != _keyFingerprint) {
|
if (call.vkey_fingerprint.v != _keyFingerprint) {
|
||||||
LOG(("Call Error: Wrong call fingerprint."));
|
LOG(("Call Error: Wrong call fingerprint."));
|
||||||
setState(State::Failed);
|
finish(FinishType::Failed);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -541,6 +546,12 @@ bool Call::checkCallFields(const MTPDphoneCallAccepted &call) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Call::setState(State state) {
|
void Call::setState(State state) {
|
||||||
|
if (_state == State::Failed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_state == State::FailedHangingUp && state != State::Failed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (_state != state) {
|
if (_state != state) {
|
||||||
_state = state;
|
_state = state;
|
||||||
_stateChanged.notify(state, true);
|
_stateChanged.notify(state, true);
|
||||||
@ -583,31 +594,37 @@ void Call::setState(State state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Call::finish(const MTPPhoneCallDiscardReason &reason) {
|
void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) {
|
||||||
|
Expects(type != FinishType::None);
|
||||||
|
auto finalState = (type == FinishType::Ended) ? State::Ended : State::Failed;
|
||||||
|
auto hangupState = (type == FinishType::Ended) ? State::HangingUp : State::FailedHangingUp;
|
||||||
if (_state == State::Requesting) {
|
if (_state == State::Requesting) {
|
||||||
_finishByTimeoutTimer.call(kHangupTimeoutMs, [this] { setState(State::Ended); });
|
_finishByTimeoutTimer.call(kHangupTimeoutMs, [this, finalState] { setState(finalState); });
|
||||||
_finishAfterRequestingCall = true;
|
_finishAfterRequestingCall = type;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_state == State::HangingUp || _state == State::Ended) {
|
if (_state == State::HangingUp
|
||||||
|
|| _state == State::FailedHangingUp
|
||||||
|
|| _state == State::Ended
|
||||||
|
|| _state == State::Failed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!_id) {
|
if (!_id) {
|
||||||
setState(State::Ended);
|
setState(finalState);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(State::HangingUp);
|
setState(hangupState);
|
||||||
auto duration = getDurationMs() / 1000;
|
auto duration = getDurationMs() / 1000;
|
||||||
auto connectionId = _controller ? _controller->GetPreferredRelayID() : 0;
|
auto connectionId = _controller ? _controller->GetPreferredRelayID() : 0;
|
||||||
_finishByTimeoutTimer.call(kHangupTimeoutMs, [this] { setState(State::Ended); });
|
_finishByTimeoutTimer.call(kHangupTimeoutMs, [this, finalState] { setState(finalState); });
|
||||||
request(MTPphone_DiscardCall(MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash)), MTP_int(duration), reason, MTP_long(connectionId))).done([this](const MTPUpdates &result) {
|
request(MTPphone_DiscardCall(MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash)), MTP_int(duration), reason, MTP_long(connectionId))).done([this, finalState](const MTPUpdates &result) {
|
||||||
// This could be destroyed by updates, so we set Ended after
|
// This could be destroyed by updates, so we set Ended after
|
||||||
// updates being handled, but in a guarded way.
|
// updates being handled, but in a guarded way.
|
||||||
InvokeQueued(this, [this] { setState(State::Ended); });
|
InvokeQueued(this, [this, finalState] { setState(finalState); });
|
||||||
App::main()->sentUpdatesReceived(result);
|
App::main()->sentUpdatesReceived(result);
|
||||||
}).fail([this](const RPCError &error) {
|
}).fail([this, finalState](const RPCError &error) {
|
||||||
setState(State::Ended);
|
setState(finalState);
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -627,14 +644,16 @@ void Call::handleRequestError(const RPCError &error) {
|
|||||||
} else if (error.type() == qstr("CALL_PROTOCOL_LAYER_INVALID")) {
|
} else if (error.type() == qstr("CALL_PROTOCOL_LAYER_INVALID")) {
|
||||||
Ui::show(Box<InformBox>(lng_call_error_incompatible(lt_user, App::peerName(_user))));
|
Ui::show(Box<InformBox>(lng_call_error_incompatible(lt_user, App::peerName(_user))));
|
||||||
}
|
}
|
||||||
setState(State::Failed);
|
finish(FinishType::Failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Call::handleControllerError(int error) {
|
void Call::handleControllerError(int error) {
|
||||||
if (error == TGVOIP_ERROR_INCOMPATIBLE) {
|
if (error == TGVOIP_ERROR_INCOMPATIBLE) {
|
||||||
Ui::show(Box<InformBox>(lng_call_error_incompatible(lt_user, App::peerName(_user))));
|
Ui::show(Box<InformBox>(lng_call_error_incompatible(lt_user, App::peerName(_user))));
|
||||||
|
} else if (error == TGVOIP_ERROR_AUDIO_IO) {
|
||||||
|
Ui::show(Box<InformBox>(lang(lng_call_error_audio_io)));
|
||||||
}
|
}
|
||||||
setState(State::Failed);
|
finish(FinishType::Failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Call::destroyController() {
|
void Call::destroyController() {
|
||||||
|
@ -87,6 +87,7 @@ public:
|
|||||||
WaitingInit,
|
WaitingInit,
|
||||||
WaitingInitAck,
|
WaitingInitAck,
|
||||||
Established,
|
Established,
|
||||||
|
FailedHangingUp,
|
||||||
Failed,
|
Failed,
|
||||||
HangingUp,
|
HangingUp,
|
||||||
Ended,
|
Ended,
|
||||||
@ -127,9 +128,14 @@ public:
|
|||||||
~Call();
|
~Call();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum class FinishType {
|
||||||
|
None,
|
||||||
|
Ended,
|
||||||
|
Failed,
|
||||||
|
};
|
||||||
void handleRequestError(const RPCError &error);
|
void handleRequestError(const RPCError &error);
|
||||||
void handleControllerError(int error);
|
void handleControllerError(int error);
|
||||||
void finish(const MTPPhoneCallDiscardReason &reason);
|
void finish(FinishType type, const MTPPhoneCallDiscardReason &reason = MTP_phoneCallDiscardReasonDisconnect());
|
||||||
void startOutgoing();
|
void startOutgoing();
|
||||||
void startIncoming();
|
void startIncoming();
|
||||||
void startWaitingTrack();
|
void startWaitingTrack();
|
||||||
@ -154,7 +160,7 @@ private:
|
|||||||
gsl::not_null<UserData*> _user;
|
gsl::not_null<UserData*> _user;
|
||||||
Type _type = Type::Outgoing;
|
Type _type = Type::Outgoing;
|
||||||
State _state = State::Starting;
|
State _state = State::Starting;
|
||||||
bool _finishAfterRequestingCall = false;
|
FinishType _finishAfterRequestingCall = FinishType::None;
|
||||||
base::Observable<State> _stateChanged;
|
base::Observable<State> _stateChanged;
|
||||||
TimeMs _startTime = 0;
|
TimeMs _startTime = 0;
|
||||||
base::DelayedCallTimer _finishByTimeoutTimer;
|
base::DelayedCallTimer _finishByTimeoutTimer;
|
||||||
|
@ -694,7 +694,10 @@ void Panel::stateChanged(State state) {
|
|||||||
updateStatusText(state);
|
updateStatusText(state);
|
||||||
|
|
||||||
if (_call) {
|
if (_call) {
|
||||||
if ((state != State::HangingUp) && (state != State::Ended) && (state != State::Failed)) {
|
if ((state != State::HangingUp)
|
||||||
|
&& (state != State::Ended)
|
||||||
|
&& (state != State::FailedHangingUp)
|
||||||
|
&& (state != State::Failed)) {
|
||||||
auto toggleButton = [this](auto &&button, bool visible) {
|
auto toggleButton = [this](auto &&button, bool visible) {
|
||||||
if (isHidden()) {
|
if (isHidden()) {
|
||||||
button->toggleFast(visible);
|
button->toggleFast(visible);
|
||||||
@ -765,6 +768,7 @@ void Panel::updateStatusText(State state) {
|
|||||||
}
|
}
|
||||||
return lang(lng_call_status_ended);
|
return lang(lng_call_status_ended);
|
||||||
} break;
|
} break;
|
||||||
|
case State::FailedHangingUp:
|
||||||
case State::Failed: return lang(lng_call_status_failed);
|
case State::Failed: return lang(lng_call_status_failed);
|
||||||
case State::HangingUp: return lang(lng_call_status_hanging);
|
case State::HangingUp: return lang(lng_call_status_hanging);
|
||||||
case State::Ended: return lang(lng_call_status_ended);
|
case State::Ended: return lang(lng_call_status_ended);
|
||||||
|
Loading…
Reference in New Issue
Block a user