Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / remoting / protocol / jingle_session.cc
blobbff155d3d8518dc27b11925d6244f87906d334e8
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "remoting/protocol/jingle_session.h"
7 #include "base/bind.h"
8 #include "base/rand_util.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/time/time.h"
14 #include "remoting/base/constants.h"
15 #include "remoting/protocol/authenticator.h"
16 #include "remoting/protocol/channel_authenticator.h"
17 #include "remoting/protocol/channel_multiplexer.h"
18 #include "remoting/protocol/content_description.h"
19 #include "remoting/protocol/jingle_messages.h"
20 #include "remoting/protocol/jingle_session_manager.h"
21 #include "remoting/protocol/pseudotcp_channel_factory.h"
22 #include "remoting/protocol/quic_channel_factory.h"
23 #include "remoting/protocol/secure_channel_factory.h"
24 #include "remoting/protocol/session_config.h"
25 #include "remoting/protocol/stream_channel_factory.h"
26 #include "remoting/signaling/iq_sender.h"
27 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
28 #include "third_party/webrtc/p2p/base/candidate.h"
30 using buzz::XmlElement;
32 namespace remoting {
33 namespace protocol {
35 namespace {
37 // Delay after candidate creation before sending transport-info message to
38 // accumulate multiple candidates. This is an optimization to reduce number of
39 // transport-info messages.
40 const int kTransportInfoSendDelayMs = 20;
42 // How long we should wait for a response from the other end. This value is used
43 // for all requests except |transport-info|.
44 const int kDefaultMessageTimeout = 10;
46 // During a reconnection, it usually takes longer for the peer to respond due to
47 // pending messages in the channel from the previous session. From experiment,
48 // it can take up to 20s for the session to reconnect. To make it safe, setting
49 // the timeout to 30s.
50 const int kSessionInitiateAndAcceptTimeout = kDefaultMessageTimeout * 3;
52 // Timeout for the transport-info messages.
53 const int kTransportInfoTimeout = 10 * 60;
55 // Name of the multiplexed channel.
56 const char kMuxChannelName[] = "mux";
58 ErrorCode AuthRejectionReasonToErrorCode(
59 Authenticator::RejectionReason reason) {
60 switch (reason) {
61 case Authenticator::INVALID_CREDENTIALS:
62 return AUTHENTICATION_FAILED;
63 case Authenticator::PROTOCOL_ERROR:
64 return INCOMPATIBLE_PROTOCOL;
66 NOTREACHED();
67 return UNKNOWN_ERROR;
70 } // namespace
72 JingleSession::JingleSession(JingleSessionManager* session_manager)
73 : session_manager_(session_manager),
74 event_handler_(nullptr),
75 state_(INITIALIZING),
76 error_(OK),
77 weak_factory_(this) {
80 JingleSession::~JingleSession() {
81 channel_multiplexer_.reset();
82 quic_channel_factory_.reset();
83 STLDeleteContainerPointers(pending_requests_.begin(),
84 pending_requests_.end());
85 STLDeleteContainerPointers(transport_info_requests_.begin(),
86 transport_info_requests_.end());
88 DCHECK(channels_.empty());
90 session_manager_->SessionDestroyed(this);
93 void JingleSession::SetEventHandler(Session::EventHandler* event_handler) {
94 DCHECK(CalledOnValidThread());
95 DCHECK(event_handler);
96 event_handler_ = event_handler;
99 ErrorCode JingleSession::error() {
100 DCHECK(CalledOnValidThread());
101 return error_;
104 void JingleSession::StartConnection(const std::string& peer_jid,
105 scoped_ptr<Authenticator> authenticator) {
106 DCHECK(CalledOnValidThread());
107 DCHECK(authenticator.get());
108 DCHECK_EQ(authenticator->state(), Authenticator::MESSAGE_READY);
110 peer_jid_ = peer_jid;
111 authenticator_ = authenticator.Pass();
113 // Generate random session ID. There are usually not more than 1
114 // concurrent session per host, so a random 64-bit integer provides
115 // enough entropy. In the worst case connection will fail when two
116 // clients generate the same session ID concurrently.
117 session_id_ = base::Uint64ToString(base::RandGenerator(kuint64max));
119 quic_channel_factory_.reset(new QuicChannelFactory(session_id_, false));
121 // Send session-initiate message.
122 JingleMessage message(peer_jid_, JingleMessage::SESSION_INITIATE,
123 session_id_);
124 message.initiator = session_manager_->signal_strategy_->GetLocalJid();
125 message.description.reset(new ContentDescription(
126 session_manager_->protocol_config_->Clone(),
127 authenticator_->GetNextMessage(),
128 quic_channel_factory_->CreateSessionInitiateConfigMessage()));
129 SendMessage(message);
131 SetState(CONNECTING);
134 void JingleSession::InitializeIncomingConnection(
135 const JingleMessage& initiate_message,
136 scoped_ptr<Authenticator> authenticator) {
137 DCHECK(CalledOnValidThread());
138 DCHECK(initiate_message.description.get());
139 DCHECK(authenticator.get());
140 DCHECK_EQ(authenticator->state(), Authenticator::WAITING_MESSAGE);
142 peer_jid_ = initiate_message.from;
143 authenticator_ = authenticator.Pass();
144 session_id_ = initiate_message.sid;
146 SetState(ACCEPTING);
148 config_ =
149 SessionConfig::SelectCommon(initiate_message.description->config(),
150 session_manager_->protocol_config_.get());
151 if (!config_) {
152 LOG(WARNING) << "Rejecting connection from " << peer_jid_
153 << " because no compatible configuration has been found.";
154 CloseInternal(INCOMPATIBLE_PROTOCOL);
155 return;
158 if (config_->is_using_quic()) {
159 quic_channel_factory_.reset(new QuicChannelFactory(session_id_, true));
160 if (!quic_channel_factory_->ProcessSessionInitiateConfigMessage(
161 initiate_message.description->quic_config_message())) {
162 CloseInternal(INCOMPATIBLE_PROTOCOL);
167 void JingleSession::AcceptIncomingConnection(
168 const JingleMessage& initiate_message) {
169 DCHECK(config_);
171 // Process the first authentication message.
172 const buzz::XmlElement* first_auth_message =
173 initiate_message.description->authenticator_message();
175 if (!first_auth_message) {
176 CloseInternal(INCOMPATIBLE_PROTOCOL);
177 return;
180 DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
181 // |authenticator_| is owned, so Unretained() is safe here.
182 authenticator_->ProcessMessage(first_auth_message, base::Bind(
183 &JingleSession::ContinueAcceptIncomingConnection,
184 base::Unretained(this)));
187 void JingleSession::ContinueAcceptIncomingConnection() {
188 DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
189 if (authenticator_->state() == Authenticator::REJECTED) {
190 CloseInternal(AuthRejectionReasonToErrorCode(
191 authenticator_->rejection_reason()));
192 return;
195 // Send the session-accept message.
196 JingleMessage message(peer_jid_, JingleMessage::SESSION_ACCEPT,
197 session_id_);
199 scoped_ptr<buzz::XmlElement> auth_message;
200 if (authenticator_->state() == Authenticator::MESSAGE_READY)
201 auth_message = authenticator_->GetNextMessage();
203 std::string quic_config;
204 if (config_->is_using_quic())
205 quic_config = quic_channel_factory_->CreateSessionAcceptConfigMessage();
206 message.description.reset(
207 new ContentDescription(CandidateSessionConfig::CreateFrom(*config_),
208 auth_message.Pass(), quic_config));
209 SendMessage(message);
211 // Update state.
212 SetState(CONNECTED);
214 if (authenticator_->state() == Authenticator::ACCEPTED) {
215 OnAuthenticated();
216 } else {
217 DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
218 if (authenticator_->started()) {
219 SetState(AUTHENTICATING);
224 const std::string& JingleSession::jid() {
225 DCHECK(CalledOnValidThread());
226 return peer_jid_;
229 const SessionConfig& JingleSession::config() {
230 DCHECK(CalledOnValidThread());
231 return *config_;
234 StreamChannelFactory* JingleSession::GetTransportChannelFactory() {
235 DCHECK(CalledOnValidThread());
236 return secure_channel_factory_.get();
239 StreamChannelFactory* JingleSession::GetMultiplexedChannelFactory() {
240 DCHECK(CalledOnValidThread());
241 if (!channel_multiplexer_.get()) {
242 channel_multiplexer_.reset(
243 new ChannelMultiplexer(GetTransportChannelFactory(), kMuxChannelName));
245 return channel_multiplexer_.get();
248 StreamChannelFactory* JingleSession::GetQuicChannelFactory() {
249 DCHECK(CalledOnValidThread());
250 return quic_channel_factory_.get();
253 void JingleSession::Close() {
254 DCHECK(CalledOnValidThread());
256 CloseInternal(OK);
259 void JingleSession::AddPendingRemoteTransportInfo(Transport* channel) {
260 std::list<JingleMessage::IceCredentials>::iterator credentials =
261 pending_remote_ice_credentials_.begin();
262 while (credentials != pending_remote_ice_credentials_.end()) {
263 if (credentials->channel == channel->name()) {
264 channel->SetRemoteCredentials(credentials->ufrag, credentials->password);
265 credentials = pending_remote_ice_credentials_.erase(credentials);
266 } else {
267 ++credentials;
271 std::list<JingleMessage::NamedCandidate>::iterator candidate =
272 pending_remote_candidates_.begin();
273 while (candidate != pending_remote_candidates_.end()) {
274 if (candidate->name == channel->name()) {
275 channel->AddRemoteCandidate(candidate->candidate);
276 candidate = pending_remote_candidates_.erase(candidate);
277 } else {
278 ++candidate;
283 void JingleSession::CreateChannel(const std::string& name,
284 const ChannelCreatedCallback& callback) {
285 DCHECK(!channels_[name]);
287 scoped_ptr<Transport> channel =
288 session_manager_->transport_factory_->CreateTransport();
289 channel->Connect(name, this, callback);
290 AddPendingRemoteTransportInfo(channel.get());
291 channels_[name] = channel.release();
294 void JingleSession::CancelChannelCreation(const std::string& name) {
295 ChannelsMap::iterator it = channels_.find(name);
296 if (it != channels_.end()) {
297 DCHECK(!it->second->is_connected());
298 delete it->second;
299 DCHECK(channels_.find(name) == channels_.end());
303 void JingleSession::OnTransportIceCredentials(Transport* transport,
304 const std::string& ufrag,
305 const std::string& password) {
306 EnsurePendingTransportInfoMessage();
307 pending_transport_info_message_->ice_credentials.push_back(
308 JingleMessage::IceCredentials(transport->name(), ufrag, password));
311 void JingleSession::OnTransportCandidate(Transport* transport,
312 const cricket::Candidate& candidate) {
313 EnsurePendingTransportInfoMessage();
314 pending_transport_info_message_->candidates.push_back(
315 JingleMessage::NamedCandidate(transport->name(), candidate));
318 void JingleSession::OnTransportRouteChange(Transport* transport,
319 const TransportRoute& route) {
320 if (event_handler_)
321 event_handler_->OnSessionRouteChange(transport->name(), route);
324 void JingleSession::OnTransportFailed(Transport* transport) {
325 CloseInternal(CHANNEL_CONNECTION_ERROR);
328 void JingleSession::OnTransportDeleted(Transport* transport) {
329 ChannelsMap::iterator it = channels_.find(transport->name());
330 DCHECK_EQ(it->second, transport);
331 channels_.erase(it);
334 void JingleSession::SendMessage(const JingleMessage& message) {
335 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
336 message.ToXml(),
337 base::Bind(&JingleSession::OnMessageResponse,
338 base::Unretained(this),
339 message.action));
341 int timeout = kDefaultMessageTimeout;
342 if (message.action == JingleMessage::SESSION_INITIATE ||
343 message.action == JingleMessage::SESSION_ACCEPT) {
344 timeout = kSessionInitiateAndAcceptTimeout;
346 if (request) {
347 request->SetTimeout(base::TimeDelta::FromSeconds(timeout));
348 pending_requests_.insert(request.release());
349 } else {
350 LOG(ERROR) << "Failed to send a "
351 << JingleMessage::GetActionName(message.action) << " message";
355 void JingleSession::OnMessageResponse(
356 JingleMessage::ActionType request_type,
357 IqRequest* request,
358 const buzz::XmlElement* response) {
359 // Delete the request from the list of pending requests.
360 pending_requests_.erase(request);
361 delete request;
363 // Ignore all responses after session was closed.
364 if (state_ == CLOSED || state_ == FAILED)
365 return;
367 std::string type_str = JingleMessage::GetActionName(request_type);
369 // |response| will be nullptr if the request timed out.
370 if (!response) {
371 LOG(ERROR) << type_str << " request timed out.";
372 CloseInternal(SIGNALING_TIMEOUT);
373 return;
374 } else {
375 const std::string& type =
376 response->Attr(buzz::QName(std::string(), "type"));
377 if (type != "result") {
378 LOG(ERROR) << "Received error in response to " << type_str
379 << " message: \"" << response->Str()
380 << "\". Terminating the session.";
382 // TODO(sergeyu): There may be different reasons for error
383 // here. Parse the response stanza to find failure reason.
384 CloseInternal(PEER_IS_OFFLINE);
389 void JingleSession::EnsurePendingTransportInfoMessage() {
390 // |transport_info_timer_| must be running iff
391 // |pending_transport_info_message_| exists.
392 DCHECK_EQ(pending_transport_info_message_ != nullptr,
393 transport_info_timer_.IsRunning());
395 if (!pending_transport_info_message_) {
396 pending_transport_info_message_.reset(new JingleMessage(
397 peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_));
398 // Delay sending the new candidates in case we get more candidates
399 // that we can send in one message.
400 transport_info_timer_.Start(
401 FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs),
402 this, &JingleSession::SendTransportInfo);
406 void JingleSession::SendTransportInfo() {
407 DCHECK(pending_transport_info_message_);
409 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
410 pending_transport_info_message_->ToXml(),
411 base::Bind(&JingleSession::OnTransportInfoResponse,
412 base::Unretained(this)));
413 pending_transport_info_message_.reset();
414 if (request) {
415 request->SetTimeout(base::TimeDelta::FromSeconds(kTransportInfoTimeout));
416 transport_info_requests_.push_back(request.release());
417 } else {
418 LOG(ERROR) << "Failed to send a transport-info message";
422 void JingleSession::OnTransportInfoResponse(IqRequest* request,
423 const buzz::XmlElement* response) {
424 DCHECK(!transport_info_requests_.empty());
426 // Consider transport-info requests sent before this one lost and delete
427 // corresponding IqRequest objects.
428 while (transport_info_requests_.front() != request) {
429 delete transport_info_requests_.front();
430 transport_info_requests_.pop_front();
433 // Delete the |request| itself.
434 DCHECK_EQ(request, transport_info_requests_.front());
435 delete request;
436 transport_info_requests_.pop_front();
438 // Ignore transport-info timeouts.
439 if (!response) {
440 LOG(ERROR) << "transport-info request has timed out.";
441 return;
444 const std::string& type = response->Attr(buzz::QName(std::string(), "type"));
445 if (type != "result") {
446 LOG(ERROR) << "Received error in response to transport-info message: \""
447 << response->Str() << "\". Terminating the session.";
448 CloseInternal(PEER_IS_OFFLINE);
452 void JingleSession::OnIncomingMessage(const JingleMessage& message,
453 const ReplyCallback& reply_callback) {
454 DCHECK(CalledOnValidThread());
456 if (message.from != peer_jid_) {
457 // Ignore messages received from a different Jid.
458 reply_callback.Run(JingleMessageReply::INVALID_SID);
459 return;
462 switch (message.action) {
463 case JingleMessage::SESSION_ACCEPT:
464 OnAccept(message, reply_callback);
465 break;
467 case JingleMessage::SESSION_INFO:
468 OnSessionInfo(message, reply_callback);
469 break;
471 case JingleMessage::TRANSPORT_INFO:
472 reply_callback.Run(JingleMessageReply::NONE);
473 ProcessTransportInfo(message);
474 break;
476 case JingleMessage::SESSION_TERMINATE:
477 OnTerminate(message, reply_callback);
478 break;
480 default:
481 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
485 void JingleSession::OnAccept(const JingleMessage& message,
486 const ReplyCallback& reply_callback) {
487 if (state_ != CONNECTING) {
488 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
489 return;
492 reply_callback.Run(JingleMessageReply::NONE);
494 const buzz::XmlElement* auth_message =
495 message.description->authenticator_message();
496 if (!auth_message) {
497 DLOG(WARNING) << "Received session-accept without authentication message ";
498 CloseInternal(INCOMPATIBLE_PROTOCOL);
499 return;
502 if (!InitializeConfigFromDescription(message.description.get())) {
503 CloseInternal(INCOMPATIBLE_PROTOCOL);
504 return;
507 if (config_->is_using_quic()) {
508 if (!quic_channel_factory_->ProcessSessionAcceptConfigMessage(
509 message.description->quic_config_message())) {
510 CloseInternal(INCOMPATIBLE_PROTOCOL);
511 return;
513 } else {
514 quic_channel_factory_.reset();
517 SetState(CONNECTED);
519 DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE);
520 authenticator_->ProcessMessage(auth_message, base::Bind(
521 &JingleSession::ProcessAuthenticationStep,base::Unretained(this)));
524 void JingleSession::OnSessionInfo(const JingleMessage& message,
525 const ReplyCallback& reply_callback) {
526 if (!message.info.get() ||
527 !Authenticator::IsAuthenticatorMessage(message.info.get())) {
528 reply_callback.Run(JingleMessageReply::UNSUPPORTED_INFO);
529 return;
532 if ((state_ != CONNECTED && state_ != AUTHENTICATING) ||
533 authenticator_->state() != Authenticator::WAITING_MESSAGE) {
534 LOG(WARNING) << "Received unexpected authenticator message "
535 << message.info->Str();
536 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
537 CloseInternal(INCOMPATIBLE_PROTOCOL);
538 return;
541 reply_callback.Run(JingleMessageReply::NONE);
543 authenticator_->ProcessMessage(message.info.get(), base::Bind(
544 &JingleSession::ProcessAuthenticationStep, base::Unretained(this)));
547 void JingleSession::ProcessTransportInfo(const JingleMessage& message) {
548 for (std::list<JingleMessage::IceCredentials>::const_iterator it =
549 message.ice_credentials.begin();
550 it != message.ice_credentials.end(); ++it) {
551 ChannelsMap::iterator channel = channels_.find(it->channel);
552 if (channel != channels_.end()) {
553 channel->second->SetRemoteCredentials(it->ufrag, it->password);
554 } else {
555 // Transport info was received before the channel was created.
556 // This could happen due to messages being reordered on the wire.
557 pending_remote_ice_credentials_.push_back(*it);
561 for (std::list<JingleMessage::NamedCandidate>::const_iterator it =
562 message.candidates.begin();
563 it != message.candidates.end(); ++it) {
564 ChannelsMap::iterator channel = channels_.find(it->name);
565 if (channel != channels_.end()) {
566 channel->second->AddRemoteCandidate(it->candidate);
567 } else {
568 // Transport info was received before the channel was created.
569 // This could happen due to messages being reordered on the wire.
570 pending_remote_candidates_.push_back(*it);
575 void JingleSession::OnTerminate(const JingleMessage& message,
576 const ReplyCallback& reply_callback) {
577 if (!is_session_active()) {
578 LOG(WARNING) << "Received unexpected session-terminate message.";
579 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
580 return;
583 reply_callback.Run(JingleMessageReply::NONE);
585 switch (message.reason) {
586 case JingleMessage::SUCCESS:
587 if (state_ == CONNECTING) {
588 error_ = SESSION_REJECTED;
589 } else {
590 error_ = OK;
592 break;
593 case JingleMessage::DECLINE:
594 error_ = AUTHENTICATION_FAILED;
595 break;
596 case JingleMessage::CANCEL:
597 error_ = HOST_OVERLOAD;
598 break;
599 case JingleMessage::GENERAL_ERROR:
600 error_ = CHANNEL_CONNECTION_ERROR;
601 break;
602 case JingleMessage::INCOMPATIBLE_PARAMETERS:
603 error_ = INCOMPATIBLE_PROTOCOL;
604 break;
605 default:
606 error_ = UNKNOWN_ERROR;
609 if (error_ != OK) {
610 SetState(FAILED);
611 } else {
612 SetState(CLOSED);
616 bool JingleSession::InitializeConfigFromDescription(
617 const ContentDescription* description) {
618 DCHECK(description);
619 config_ = SessionConfig::GetFinalConfig(description->config());
620 if (!config_) {
621 LOG(ERROR) << "session-accept does not specify configuration";
622 return false;
624 if (!session_manager_->protocol_config_->IsSupported(*config_)) {
625 LOG(ERROR) << "session-accept specifies an invalid configuration";
626 return false;
629 return true;
632 void JingleSession::ProcessAuthenticationStep() {
633 DCHECK(CalledOnValidThread());
634 DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
636 if (state_ != CONNECTED && state_ != AUTHENTICATING) {
637 DCHECK(state_ == FAILED || state_ == CLOSED);
638 // The remote host closed the connection while the authentication was being
639 // processed asynchronously, nothing to do.
640 return;
643 if (authenticator_->state() == Authenticator::MESSAGE_READY) {
644 JingleMessage message(peer_jid_, JingleMessage::SESSION_INFO, session_id_);
645 message.info = authenticator_->GetNextMessage();
646 DCHECK(message.info.get());
647 SendMessage(message);
649 DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY);
651 // The current JingleSession object can be destroyed by event_handler of
652 // SetState(AUTHENTICATING) and cause subsequent dereferencing of the this
653 // pointer to crash. To protect against it, we run ContinueAuthenticationStep
654 // asychronously using a weak pointer.
655 base::ThreadTaskRunnerHandle::Get()->PostTask(
656 FROM_HERE,
657 base::Bind(&JingleSession::ContinueAuthenticationStep,
658 weak_factory_.GetWeakPtr()));
660 if (authenticator_->started()) {
661 SetState(AUTHENTICATING);
665 void JingleSession::ContinueAuthenticationStep() {
666 if (authenticator_->state() == Authenticator::ACCEPTED) {
667 OnAuthenticated();
668 } else if (authenticator_->state() == Authenticator::REJECTED) {
669 CloseInternal(AuthRejectionReasonToErrorCode(
670 authenticator_->rejection_reason()));
674 void JingleSession::OnAuthenticated() {
675 pseudotcp_channel_factory_.reset(new PseudoTcpChannelFactory(this));
676 secure_channel_factory_.reset(
677 new SecureChannelFactory(pseudotcp_channel_factory_.get(),
678 authenticator_.get()));
680 if (quic_channel_factory_)
681 quic_channel_factory_->Start(this, authenticator_->GetAuthKey());
683 SetState(AUTHENTICATED);
686 void JingleSession::CloseInternal(ErrorCode error) {
687 DCHECK(CalledOnValidThread());
689 if (is_session_active()) {
690 // Send session-terminate message with the appropriate error code.
691 JingleMessage::Reason reason;
692 switch (error) {
693 case OK:
694 reason = JingleMessage::SUCCESS;
695 break;
696 case SESSION_REJECTED:
697 case AUTHENTICATION_FAILED:
698 reason = JingleMessage::DECLINE;
699 break;
700 case INCOMPATIBLE_PROTOCOL:
701 reason = JingleMessage::INCOMPATIBLE_PARAMETERS;
702 break;
703 case HOST_OVERLOAD:
704 reason = JingleMessage::CANCEL;
705 break;
706 default:
707 reason = JingleMessage::GENERAL_ERROR;
710 JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE,
711 session_id_);
712 message.reason = reason;
713 SendMessage(message);
716 error_ = error;
718 if (state_ != FAILED && state_ != CLOSED) {
719 if (error != OK) {
720 SetState(FAILED);
721 } else {
722 SetState(CLOSED);
727 void JingleSession::SetState(State new_state) {
728 DCHECK(CalledOnValidThread());
730 if (new_state != state_) {
731 DCHECK_NE(state_, CLOSED);
732 DCHECK_NE(state_, FAILED);
734 state_ = new_state;
735 if (event_handler_)
736 event_handler_->OnSessionStateChange(new_state);
740 bool JingleSession::is_session_active() {
741 return state_ == CONNECTING || state_ == ACCEPTING || state_ == CONNECTED ||
742 state_ == AUTHENTICATING || state_ == AUTHENTICATED;
745 } // namespace protocol
746 } // namespace remoting