Discard call in case of an error.
Also add a couple of call error messages.
This commit is contained in:
		
							parent
							
								
									061bd109d2
								
							
						
					
					
						commit
						c78cc331d1
					
				
					 4 changed files with 65 additions and 35 deletions
				
			
		|  | @ -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_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_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_hangup" = "End call"; | ||||
|  |  | |||
|  | @ -93,7 +93,7 @@ void Call::generateModExpFirst(base::const_byte_span randomSeed) { | |||
| 	auto first = MTP::CreateModExp(_dhConfig.g, _dhConfig.p, randomSeed); | ||||
| 	if (first.modexp.empty()) { | ||||
| 		LOG(("Call Error: Could not compute mod-exp first.")); | ||||
| 		setState(State::Failed); | ||||
| 		finish(FinishType::Failed); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -121,7 +121,7 @@ void Call::start(base::const_byte_span random) { | |||
| 	t_assert(!_dhConfig.p.empty()); | ||||
| 
 | ||||
| 	generateModExpFirst(random); | ||||
| 	if (_state != State::Failed) { | ||||
| 	if (_state != State::Failed && _state != State::FailedHangingUp) { | ||||
| 		if (_type == Type::Outgoing) { | ||||
| 			startOutgoing(); | ||||
| 		} else { | ||||
|  | @ -136,11 +136,14 @@ void Call::startOutgoing() { | |||
| 	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) { | ||||
| 		Expects(result.type() == mtpc_phone_phoneCall); | ||||
| 
 | ||||
| 		setState(State::Waiting); | ||||
| 
 | ||||
| 		auto &call = result.c_phone_phoneCall(); | ||||
| 		App::feedUsers(call.vusers); | ||||
| 		if (call.vphone_call.type() != mtpc_phoneCallWaiting) { | ||||
| 			LOG(("Call Error: Expected phoneCallWaiting in response to phone.requestCall()")); | ||||
| 			setState(State::Failed); | ||||
| 			finish(FinishType::Failed); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
|  | @ -148,10 +151,12 @@ void Call::startOutgoing() { | |||
| 		auto &waitingCall = phoneCall.c_phoneCallWaiting(); | ||||
| 		_id = waitingCall.vid.v; | ||||
| 		_accessHash = waitingCall.vaccess_hash.v; | ||||
| 		setState(State::Waiting); | ||||
| 
 | ||||
| 		if (_finishAfterRequestingCall) { | ||||
| 		if (_finishAfterRequestingCall != FinishType::None) { | ||||
| 			if (_finishAfterRequestingCall == FinishType::Failed) { | ||||
| 				finish(_finishAfterRequestingCall); | ||||
| 			} else { | ||||
| 				hangup(); | ||||
| 			} | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
|  | @ -188,7 +193,7 @@ void Call::answer() { | |||
| 		App::feedUsers(call.vusers); | ||||
| 		if (call.vphone_call.type() != mtpc_phoneCallWaiting) { | ||||
| 			LOG(("Call Error: Expected phoneCallWaiting in response to phone.acceptCall()")); | ||||
| 			setState(State::Failed); | ||||
| 			finish(FinishType::Failed); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
|  | @ -218,7 +223,7 @@ void Call::hangup() { | |||
| 		auto declined = (_state == State::WaitingIncoming); | ||||
| 		auto reason = missed ? MTP_phoneCallDiscardReasonMissed() : | ||||
| 			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) { | ||||
| 			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; | ||||
| 		} | ||||
| 		_id = data.vid.v; | ||||
|  | @ -291,7 +296,7 @@ bool Call::handleUpdate(const MTPPhoneCall &call) { | |||
| 		auto gaHashBytes = bytesFromMTP(data.vg_a_hash); | ||||
| 		if (gaHashBytes.size() != _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; | ||||
| 		} | ||||
| 		base::copy_bytes(gsl::make_span(_gaHash), gaHashBytes); | ||||
|  | @ -303,7 +308,7 @@ bool Call::handleUpdate(const MTPPhoneCall &call) { | |||
| 			return false; | ||||
| 		} | ||||
| 		LOG(("Call Error: phoneCallEmpty received.")); | ||||
| 		setState(State::Failed); | ||||
| 		finish(FinishType::Failed); | ||||
| 	} return true; | ||||
| 
 | ||||
| 	case mtpc_phoneCallWaiting: { | ||||
|  | @ -358,7 +363,7 @@ bool Call::handleUpdate(const MTPPhoneCall &call) { | |||
| 		} | ||||
| 		if (_type != Type::Outgoing) { | ||||
| 			LOG(("Call Error: Unexpected phoneCallAccepted for an incoming call.")); | ||||
| 			setState(State::Failed); | ||||
| 			finish(FinishType::Failed); | ||||
| 		} else if (checkCallFields(data)) { | ||||
| 			confirmAcceptedCall(data); | ||||
| 		} | ||||
|  | @ -375,7 +380,7 @@ void Call::confirmAcceptedCall(const MTPDphoneCallAccepted &call) { | |||
| 	auto computedAuthKey = MTP::CreateAuthKey(firstBytes, _randomPower, _dhConfig.p); | ||||
| 	if (computedAuthKey.empty()) { | ||||
| 		LOG(("Call Error: Could not compute mod-exp final.")); | ||||
| 		setState(State::Failed); | ||||
| 		finish(FinishType::Failed); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -389,7 +394,7 @@ void Call::confirmAcceptedCall(const MTPDphoneCallAccepted &call) { | |||
| 		App::feedUsers(call.vusers); | ||||
| 		if (call.vphone_call.type() != mtpc_phoneCall) { | ||||
| 			LOG(("Call Error: Expected phoneCall in response to phone.confirmCall()")); | ||||
| 			setState(State::Failed); | ||||
| 			finish(FinishType::Failed); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
|  | @ -405,7 +410,7 @@ void Call::startConfirmedCall(const MTPDphoneCall &call) { | |||
| 	auto firstBytes = bytesFromMTP(call.vg_a_or_b); | ||||
| 	if (_gaHash != openssl::Sha256(firstBytes)) { | ||||
| 		LOG(("Call Error: Wrong g_a hash received.")); | ||||
| 		setState(State::Failed); | ||||
| 		finish(FinishType::Failed); | ||||
| 		return; | ||||
| 	} | ||||
| 	_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); | ||||
| 	if (computedAuthKey.empty()) { | ||||
| 		LOG(("Call Error: Could not compute mod-exp final.")); | ||||
| 		setState(State::Failed); | ||||
| 		finish(FinishType::Failed); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -504,7 +509,7 @@ void Call::handleControllerStateChange(tgvoip::VoIPController *controller, int s | |||
| template <typename T> | ||||
| bool Call::checkCallCommonFields(const T &call) { | ||||
| 	auto checkFailed = [this] { | ||||
| 		setState(State::Failed); | ||||
| 		finish(FinishType::Failed); | ||||
| 		return false; | ||||
| 	}; | ||||
| 	if (call.vaccess_hash.v != _accessHash) { | ||||
|  | @ -530,7 +535,7 @@ bool Call::checkCallFields(const MTPDphoneCall &call) { | |||
| 	} | ||||
| 	if (call.vkey_fingerprint.v != _keyFingerprint) { | ||||
| 		LOG(("Call Error: Wrong call fingerprint.")); | ||||
| 		setState(State::Failed); | ||||
| 		finish(FinishType::Failed); | ||||
| 		return false; | ||||
| 	} | ||||
| 	return true; | ||||
|  | @ -541,6 +546,12 @@ bool Call::checkCallFields(const MTPDphoneCallAccepted &call) { | |||
| } | ||||
| 
 | ||||
| void Call::setState(State state) { | ||||
| 	if (_state == State::Failed) { | ||||
| 		return; | ||||
| 	} | ||||
| 	if (_state == State::FailedHangingUp && state != State::Failed) { | ||||
| 		return; | ||||
| 	} | ||||
| 	if (_state != state) { | ||||
| 		_state = state; | ||||
| 		_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) { | ||||
| 		_finishByTimeoutTimer.call(kHangupTimeoutMs, [this] { setState(State::Ended); }); | ||||
| 		_finishAfterRequestingCall = true; | ||||
| 		_finishByTimeoutTimer.call(kHangupTimeoutMs, [this, finalState] { setState(finalState); }); | ||||
| 		_finishAfterRequestingCall = type; | ||||
| 		return; | ||||
| 	} | ||||
| 	if (_state == State::HangingUp || _state == State::Ended) { | ||||
| 	if (_state == State::HangingUp | ||||
| 		|| _state == State::FailedHangingUp | ||||
| 		|| _state == State::Ended | ||||
| 		|| _state == State::Failed) { | ||||
| 		return; | ||||
| 	} | ||||
| 	if (!_id) { | ||||
| 		setState(State::Ended); | ||||
| 		setState(finalState); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	setState(State::HangingUp); | ||||
| 	setState(hangupState); | ||||
| 	auto duration = getDurationMs() / 1000; | ||||
| 	auto connectionId = _controller ? _controller->GetPreferredRelayID() : 0; | ||||
| 	_finishByTimeoutTimer.call(kHangupTimeoutMs, [this] { setState(State::Ended); }); | ||||
| 	request(MTPphone_DiscardCall(MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash)), MTP_int(duration), reason, MTP_long(connectionId))).done([this](const MTPUpdates &result) { | ||||
| 	_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, finalState](const MTPUpdates &result) { | ||||
| 		// This could be destroyed by updates, so we set Ended after
 | ||||
| 		// updates being handled, but in a guarded way.
 | ||||
| 		InvokeQueued(this, [this] { setState(State::Ended); }); | ||||
| 		InvokeQueued(this, [this, finalState] { setState(finalState); }); | ||||
| 		App::main()->sentUpdatesReceived(result); | ||||
| 	}).fail([this](const RPCError &error) { | ||||
| 		setState(State::Ended); | ||||
| 	}).fail([this, finalState](const RPCError &error) { | ||||
| 		setState(finalState); | ||||
| 	}).send(); | ||||
| } | ||||
| 
 | ||||
|  | @ -627,14 +644,16 @@ void Call::handleRequestError(const RPCError &error) { | |||
| 	} else if (error.type() == qstr("CALL_PROTOCOL_LAYER_INVALID")) { | ||||
| 		Ui::show(Box<InformBox>(lng_call_error_incompatible(lt_user, App::peerName(_user)))); | ||||
| 	} | ||||
| 	setState(State::Failed); | ||||
| 	finish(FinishType::Failed); | ||||
| } | ||||
| 
 | ||||
| void Call::handleControllerError(int error) { | ||||
| 	if (error == TGVOIP_ERROR_INCOMPATIBLE) { | ||||
| 		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() { | ||||
|  |  | |||
|  | @ -87,6 +87,7 @@ public: | |||
| 		WaitingInit, | ||||
| 		WaitingInitAck, | ||||
| 		Established, | ||||
| 		FailedHangingUp, | ||||
| 		Failed, | ||||
| 		HangingUp, | ||||
| 		Ended, | ||||
|  | @ -127,9 +128,14 @@ public: | |||
| 	~Call(); | ||||
| 
 | ||||
| private: | ||||
| 	enum class FinishType { | ||||
| 		None, | ||||
| 		Ended, | ||||
| 		Failed, | ||||
| 	}; | ||||
| 	void handleRequestError(const RPCError &error); | ||||
| 	void handleControllerError(int error); | ||||
| 	void finish(const MTPPhoneCallDiscardReason &reason); | ||||
| 	void finish(FinishType type, const MTPPhoneCallDiscardReason &reason = MTP_phoneCallDiscardReasonDisconnect()); | ||||
| 	void startOutgoing(); | ||||
| 	void startIncoming(); | ||||
| 	void startWaitingTrack(); | ||||
|  | @ -154,7 +160,7 @@ private: | |||
| 	gsl::not_null<UserData*> _user; | ||||
| 	Type _type = Type::Outgoing; | ||||
| 	State _state = State::Starting; | ||||
| 	bool _finishAfterRequestingCall = false; | ||||
| 	FinishType _finishAfterRequestingCall = FinishType::None; | ||||
| 	base::Observable<State> _stateChanged; | ||||
| 	TimeMs _startTime = 0; | ||||
| 	base::DelayedCallTimer _finishByTimeoutTimer; | ||||
|  |  | |||
|  | @ -694,7 +694,10 @@ void Panel::stateChanged(State state) { | |||
| 	updateStatusText(state); | ||||
| 
 | ||||
| 	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) { | ||||
| 				if (isHidden()) { | ||||
| 					button->toggleFast(visible); | ||||
|  | @ -765,6 +768,7 @@ void Panel::updateStatusText(State state) { | |||
| 			} | ||||
| 			return lang(lng_call_status_ended); | ||||
| 		} break; | ||||
| 		case State::FailedHangingUp: | ||||
| 		case State::Failed: return lang(lng_call_status_failed); | ||||
| 		case State::HangingUp: return lang(lng_call_status_hanging); | ||||
| 		case State::Ended: return lang(lng_call_status_ended); | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 John Preston
						John Preston