Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / remoting / protocol / jingle_session.cc
blob0f4f0a2ea6092236162ff8c7dfbfa5dcc8eb302b
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/secure_channel_factory.h"
23 #include "remoting/protocol/session_config.h"
24 #include "remoting/protocol/stream_channel_factory.h"
25 #include "remoting/signaling/iq_sender.h"
26 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
27 #include "third_party/webrtc/p2p/base/candidate.h"
29 using buzz::XmlElement;
31 namespace remoting {
32 namespace protocol {
34 namespace {
36 // Delay after candidate creation before sending transport-info message to
37 // accumulate multiple candidates. This is an optimization to reduce number of
38 // transport-info messages.
39 const int kTransportInfoSendDelayMs = 20;
41 // How long we should wait for a response from the other end. This value is used
42 // for all requests except |transport-info|.
43 const int kDefaultMessageTimeout = 10;
45 // During a reconnection, it usually takes longer for the peer to respond due to
46 // pending messages in the channel from the previous session. From experiment,
47 // it can take up to 20s for the session to reconnect. To make it safe, setting
48 // the timeout to 30s.
49 const int kSessionInitiateAndAcceptTimeout = kDefaultMessageTimeout * 3;
51 // Timeout for the transport-info messages.
52 const int kTransportInfoTimeout = 10 * 60;
54 // Name of the multiplexed channel.
55 const char kMuxChannelName[] = "mux";
57 ErrorCode AuthRejectionReasonToErrorCode(
58 Authenticator::RejectionReason reason) {
59 switch (reason) {
60 case Authenticator::INVALID_CREDENTIALS:
61 return AUTHENTICATION_FAILED;
62 case Authenticator::PROTOCOL_ERROR:
63 return INCOMPATIBLE_PROTOCOL;
65 NOTREACHED();
66 return UNKNOWN_ERROR;
69 } // namespace
71 JingleSession::JingleSession(JingleSessionManager* session_manager)
72 : session_manager_(session_manager),
73 event_handler_(nullptr),
74 state_(INITIALIZING),
75 error_(OK),
76 weak_factory_(this) {
79 JingleSession::~JingleSession() {
80 channel_multiplexer_.reset();
81 STLDeleteContainerPointers(pending_requests_.begin(),
82 pending_requests_.end());
83 STLDeleteContainerPointers(transport_info_requests_.begin(),
84 transport_info_requests_.end());
86 DCHECK(channels_.empty());
88 session_manager_->SessionDestroyed(this);
91 void JingleSession::SetEventHandler(Session::EventHandler* event_handler) {
92 DCHECK(CalledOnValidThread());
93 DCHECK(event_handler);
94 event_handler_ = event_handler;
97 ErrorCode JingleSession::error() {
98 DCHECK(CalledOnValidThread());
99 return error_;
102 void JingleSession::StartConnection(
103 const std::string& peer_jid,
104 scoped_ptr<Authenticator> authenticator,
105 scoped_ptr<CandidateSessionConfig> config) {
106 DCHECK(CalledOnValidThread());
107 DCHECK(authenticator.get());
108 DCHECK_EQ(authenticator->state(), Authenticator::MESSAGE_READY);
110 peer_jid_ = peer_jid;
111 authenticator_ = authenticator.Pass();
112 candidate_config_ = config.Pass();
114 // Generate random session ID. There are usually not more than 1
115 // concurrent session per host, so a random 64-bit integer provides
116 // enough entropy. In the worst case connection will fail when two
117 // clients generate the same session ID concurrently.
118 session_id_ = base::Int64ToString(base::RandGenerator(kint64max));
120 // Send session-initiate message.
121 JingleMessage message(peer_jid_, JingleMessage::SESSION_INITIATE,
122 session_id_);
123 message.initiator = session_manager_->signal_strategy_->GetLocalJid();
124 message.description.reset(
125 new ContentDescription(candidate_config_->Clone(),
126 authenticator_->GetNextMessage()));
127 SendMessage(message);
129 SetState(CONNECTING);
132 void JingleSession::InitializeIncomingConnection(
133 const JingleMessage& initiate_message,
134 scoped_ptr<Authenticator> authenticator) {
135 DCHECK(CalledOnValidThread());
136 DCHECK(initiate_message.description.get());
137 DCHECK(authenticator.get());
138 DCHECK_EQ(authenticator->state(), Authenticator::WAITING_MESSAGE);
140 peer_jid_ = initiate_message.from;
141 authenticator_ = authenticator.Pass();
142 session_id_ = initiate_message.sid;
143 candidate_config_ = initiate_message.description->config()->Clone();
145 SetState(ACCEPTING);
148 void JingleSession::AcceptIncomingConnection(
149 const JingleMessage& initiate_message) {
150 DCHECK(config_);
152 // Process the first authentication message.
153 const buzz::XmlElement* first_auth_message =
154 initiate_message.description->authenticator_message();
156 if (!first_auth_message) {
157 CloseInternal(INCOMPATIBLE_PROTOCOL);
158 return;
161 DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
162 // |authenticator_| is owned, so Unretained() is safe here.
163 authenticator_->ProcessMessage(first_auth_message, base::Bind(
164 &JingleSession::ContinueAcceptIncomingConnection,
165 base::Unretained(this)));
168 void JingleSession::ContinueAcceptIncomingConnection() {
169 DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
170 if (authenticator_->state() == Authenticator::REJECTED) {
171 CloseInternal(AuthRejectionReasonToErrorCode(
172 authenticator_->rejection_reason()));
173 return;
176 // Send the session-accept message.
177 JingleMessage message(peer_jid_, JingleMessage::SESSION_ACCEPT,
178 session_id_);
180 scoped_ptr<buzz::XmlElement> auth_message;
181 if (authenticator_->state() == Authenticator::MESSAGE_READY)
182 auth_message = authenticator_->GetNextMessage();
184 message.description.reset(
185 new ContentDescription(CandidateSessionConfig::CreateFrom(*config_),
186 auth_message.Pass()));
187 SendMessage(message);
189 // Update state.
190 SetState(CONNECTED);
192 if (authenticator_->state() == Authenticator::ACCEPTED) {
193 OnAuthenticated();
194 } else {
195 DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
196 if (authenticator_->started()) {
197 SetState(AUTHENTICATING);
202 const std::string& JingleSession::jid() {
203 DCHECK(CalledOnValidThread());
204 return peer_jid_;
207 const CandidateSessionConfig* JingleSession::candidate_config() {
208 DCHECK(CalledOnValidThread());
209 return candidate_config_.get();
212 const SessionConfig& JingleSession::config() {
213 DCHECK(CalledOnValidThread());
214 return *config_;
217 void JingleSession::set_config(scoped_ptr<SessionConfig> config) {
218 DCHECK(CalledOnValidThread());
219 DCHECK(!config_);
220 config_ = config.Pass();
223 StreamChannelFactory* JingleSession::GetTransportChannelFactory() {
224 DCHECK(CalledOnValidThread());
225 return secure_channel_factory_.get();
228 StreamChannelFactory* JingleSession::GetMultiplexedChannelFactory() {
229 DCHECK(CalledOnValidThread());
230 if (!channel_multiplexer_.get()) {
231 channel_multiplexer_.reset(
232 new ChannelMultiplexer(GetTransportChannelFactory(), kMuxChannelName));
234 return channel_multiplexer_.get();
237 void JingleSession::Close() {
238 DCHECK(CalledOnValidThread());
240 CloseInternal(OK);
243 void JingleSession::AddPendingRemoteTransportInfo(Transport* channel) {
244 std::list<JingleMessage::IceCredentials>::iterator credentials =
245 pending_remote_ice_credentials_.begin();
246 while (credentials != pending_remote_ice_credentials_.end()) {
247 if (credentials->channel == channel->name()) {
248 channel->SetRemoteCredentials(credentials->ufrag, credentials->password);
249 credentials = pending_remote_ice_credentials_.erase(credentials);
250 } else {
251 ++credentials;
255 std::list<JingleMessage::NamedCandidate>::iterator candidate =
256 pending_remote_candidates_.begin();
257 while (candidate != pending_remote_candidates_.end()) {
258 if (candidate->name == channel->name()) {
259 channel->AddRemoteCandidate(candidate->candidate);
260 candidate = pending_remote_candidates_.erase(candidate);
261 } else {
262 ++candidate;
267 void JingleSession::CreateChannel(const std::string& name,
268 const ChannelCreatedCallback& callback) {
269 DCHECK(!channels_[name]);
271 scoped_ptr<Transport> channel =
272 session_manager_->transport_factory_->CreateTransport();
273 channel->SetUseStandardIce(config_->standard_ice());
274 channel->Connect(name, this, callback);
275 AddPendingRemoteTransportInfo(channel.get());
276 channels_[name] = channel.release();
279 void JingleSession::CancelChannelCreation(const std::string& name) {
280 ChannelsMap::iterator it = channels_.find(name);
281 if (it != channels_.end()) {
282 DCHECK(!it->second->is_connected());
283 delete it->second;
284 DCHECK(channels_.find(name) == channels_.end());
288 void JingleSession::OnTransportIceCredentials(Transport* transport,
289 const std::string& ufrag,
290 const std::string& password) {
291 EnsurePendingTransportInfoMessage();
292 pending_transport_info_message_->ice_credentials.push_back(
293 JingleMessage::IceCredentials(transport->name(), ufrag, password));
296 void JingleSession::OnTransportCandidate(Transport* transport,
297 const cricket::Candidate& candidate) {
298 EnsurePendingTransportInfoMessage();
299 pending_transport_info_message_->candidates.push_back(
300 JingleMessage::NamedCandidate(transport->name(), candidate));
303 void JingleSession::OnTransportRouteChange(Transport* transport,
304 const TransportRoute& route) {
305 if (event_handler_)
306 event_handler_->OnSessionRouteChange(transport->name(), route);
309 void JingleSession::OnTransportFailed(Transport* transport) {
310 CloseInternal(CHANNEL_CONNECTION_ERROR);
313 void JingleSession::OnTransportDeleted(Transport* transport) {
314 ChannelsMap::iterator it = channels_.find(transport->name());
315 DCHECK_EQ(it->second, transport);
316 channels_.erase(it);
319 void JingleSession::SendMessage(const JingleMessage& message) {
320 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
321 message.ToXml(),
322 base::Bind(&JingleSession::OnMessageResponse,
323 base::Unretained(this),
324 message.action));
326 int timeout = kDefaultMessageTimeout;
327 if (message.action == JingleMessage::SESSION_INITIATE ||
328 message.action == JingleMessage::SESSION_ACCEPT) {
329 timeout = kSessionInitiateAndAcceptTimeout;
331 if (request) {
332 request->SetTimeout(base::TimeDelta::FromSeconds(timeout));
333 pending_requests_.insert(request.release());
334 } else {
335 LOG(ERROR) << "Failed to send a "
336 << JingleMessage::GetActionName(message.action) << " message";
340 void JingleSession::OnMessageResponse(
341 JingleMessage::ActionType request_type,
342 IqRequest* request,
343 const buzz::XmlElement* response) {
344 // Delete the request from the list of pending requests.
345 pending_requests_.erase(request);
346 delete request;
348 // Ignore all responses after session was closed.
349 if (state_ == CLOSED || state_ == FAILED)
350 return;
352 std::string type_str = JingleMessage::GetActionName(request_type);
354 // |response| will be nullptr if the request timed out.
355 if (!response) {
356 LOG(ERROR) << type_str << " request timed out.";
357 CloseInternal(SIGNALING_TIMEOUT);
358 return;
359 } else {
360 const std::string& type =
361 response->Attr(buzz::QName(std::string(), "type"));
362 if (type != "result") {
363 LOG(ERROR) << "Received error in response to " << type_str
364 << " message: \"" << response->Str()
365 << "\". Terminating the session.";
367 // TODO(sergeyu): There may be different reasons for error
368 // here. Parse the response stanza to find failure reason.
369 CloseInternal(PEER_IS_OFFLINE);
374 void JingleSession::EnsurePendingTransportInfoMessage() {
375 // |transport_info_timer_| must be running iff
376 // |pending_transport_info_message_| exists.
377 DCHECK_EQ(pending_transport_info_message_ != nullptr,
378 transport_info_timer_.IsRunning());
380 if (!pending_transport_info_message_) {
381 pending_transport_info_message_.reset(new JingleMessage(
382 peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_));
383 pending_transport_info_message_->standard_ice = config_->standard_ice();
385 // Delay sending the new candidates in case we get more candidates
386 // that we can send in one message.
387 transport_info_timer_.Start(
388 FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs),
389 this, &JingleSession::SendTransportInfo);
393 void JingleSession::SendTransportInfo() {
394 DCHECK(pending_transport_info_message_);
396 scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
397 pending_transport_info_message_->ToXml(),
398 base::Bind(&JingleSession::OnTransportInfoResponse,
399 base::Unretained(this)));
400 pending_transport_info_message_.reset();
401 if (request) {
402 request->SetTimeout(base::TimeDelta::FromSeconds(kTransportInfoTimeout));
403 transport_info_requests_.push_back(request.release());
404 } else {
405 LOG(ERROR) << "Failed to send a transport-info message";
409 void JingleSession::OnTransportInfoResponse(IqRequest* request,
410 const buzz::XmlElement* response) {
411 DCHECK(!transport_info_requests_.empty());
413 // Consider transport-info requests sent before this one lost and delete
414 // corresponding IqRequest objects.
415 while (transport_info_requests_.front() != request) {
416 delete transport_info_requests_.front();
417 transport_info_requests_.pop_front();
420 // Delete the |request| itself.
421 DCHECK_EQ(request, transport_info_requests_.front());
422 delete request;
423 transport_info_requests_.pop_front();
425 // Ignore transport-info timeouts.
426 if (!response) {
427 LOG(ERROR) << "transport-info request has timed out.";
428 return;
431 const std::string& type = response->Attr(buzz::QName(std::string(), "type"));
432 if (type != "result") {
433 LOG(ERROR) << "Received error in response to transport-info message: \""
434 << response->Str() << "\". Terminating the session.";
435 CloseInternal(PEER_IS_OFFLINE);
439 void JingleSession::OnIncomingMessage(const JingleMessage& message,
440 const ReplyCallback& reply_callback) {
441 DCHECK(CalledOnValidThread());
443 if (message.from != peer_jid_) {
444 // Ignore messages received from a different Jid.
445 reply_callback.Run(JingleMessageReply::INVALID_SID);
446 return;
449 switch (message.action) {
450 case JingleMessage::SESSION_ACCEPT:
451 OnAccept(message, reply_callback);
452 break;
454 case JingleMessage::SESSION_INFO:
455 OnSessionInfo(message, reply_callback);
456 break;
458 case JingleMessage::TRANSPORT_INFO:
459 reply_callback.Run(JingleMessageReply::NONE);
460 ProcessTransportInfo(message);
461 break;
463 case JingleMessage::SESSION_TERMINATE:
464 OnTerminate(message, reply_callback);
465 break;
467 default:
468 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
472 void JingleSession::OnAccept(const JingleMessage& message,
473 const ReplyCallback& reply_callback) {
474 if (state_ != CONNECTING) {
475 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
476 return;
479 reply_callback.Run(JingleMessageReply::NONE);
481 const buzz::XmlElement* auth_message =
482 message.description->authenticator_message();
483 if (!auth_message) {
484 DLOG(WARNING) << "Received session-accept without authentication message ";
485 CloseInternal(INCOMPATIBLE_PROTOCOL);
486 return;
489 if (!InitializeConfigFromDescription(message.description.get())) {
490 CloseInternal(INCOMPATIBLE_PROTOCOL);
491 return;
494 SetState(CONNECTED);
496 DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE);
497 authenticator_->ProcessMessage(auth_message, base::Bind(
498 &JingleSession::ProcessAuthenticationStep,base::Unretained(this)));
501 void JingleSession::OnSessionInfo(const JingleMessage& message,
502 const ReplyCallback& reply_callback) {
503 if (!message.info.get() ||
504 !Authenticator::IsAuthenticatorMessage(message.info.get())) {
505 reply_callback.Run(JingleMessageReply::UNSUPPORTED_INFO);
506 return;
509 if ((state_ != CONNECTED && state_ != AUTHENTICATING) ||
510 authenticator_->state() != Authenticator::WAITING_MESSAGE) {
511 LOG(WARNING) << "Received unexpected authenticator message "
512 << message.info->Str();
513 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
514 CloseInternal(INCOMPATIBLE_PROTOCOL);
515 return;
518 reply_callback.Run(JingleMessageReply::NONE);
520 authenticator_->ProcessMessage(message.info.get(), base::Bind(
521 &JingleSession::ProcessAuthenticationStep, base::Unretained(this)));
524 void JingleSession::ProcessTransportInfo(const JingleMessage& message) {
525 // Check if the transport information version matches what was negotiated.
526 if (message.standard_ice != config_->standard_ice()) {
527 LOG(ERROR) << "Received transport-info message in format different from "
528 "negotiated.";
529 CloseInternal(INCOMPATIBLE_PROTOCOL);
530 return;
533 for (std::list<JingleMessage::IceCredentials>::const_iterator it =
534 message.ice_credentials.begin();
535 it != message.ice_credentials.end(); ++it) {
536 ChannelsMap::iterator channel = channels_.find(it->channel);
537 if (channel != channels_.end()) {
538 channel->second->SetRemoteCredentials(it->ufrag, it->password);
539 } else {
540 // Transport info was received before the channel was created.
541 // This could happen due to messages being reordered on the wire.
542 pending_remote_ice_credentials_.push_back(*it);
546 for (std::list<JingleMessage::NamedCandidate>::const_iterator it =
547 message.candidates.begin();
548 it != message.candidates.end(); ++it) {
549 ChannelsMap::iterator channel = channels_.find(it->name);
550 if (channel != channels_.end()) {
551 channel->second->AddRemoteCandidate(it->candidate);
552 } else {
553 // Transport info was received before the channel was created.
554 // This could happen due to messages being reordered on the wire.
555 pending_remote_candidates_.push_back(*it);
560 void JingleSession::OnTerminate(const JingleMessage& message,
561 const ReplyCallback& reply_callback) {
562 if (!is_session_active()) {
563 LOG(WARNING) << "Received unexpected session-terminate message.";
564 reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
565 return;
568 reply_callback.Run(JingleMessageReply::NONE);
570 switch (message.reason) {
571 case JingleMessage::SUCCESS:
572 if (state_ == CONNECTING) {
573 error_ = SESSION_REJECTED;
574 } else {
575 error_ = OK;
577 break;
578 case JingleMessage::DECLINE:
579 error_ = AUTHENTICATION_FAILED;
580 break;
581 case JingleMessage::CANCEL:
582 error_ = HOST_OVERLOAD;
583 break;
584 case JingleMessage::GENERAL_ERROR:
585 error_ = CHANNEL_CONNECTION_ERROR;
586 break;
587 case JingleMessage::INCOMPATIBLE_PARAMETERS:
588 error_ = INCOMPATIBLE_PROTOCOL;
589 break;
590 default:
591 error_ = UNKNOWN_ERROR;
594 if (error_ != OK) {
595 SetState(FAILED);
596 } else {
597 SetState(CLOSED);
601 bool JingleSession::InitializeConfigFromDescription(
602 const ContentDescription* description) {
603 DCHECK(description);
604 config_ = SessionConfig::GetFinalConfig(description->config());
605 if (!config_) {
606 LOG(ERROR) << "session-accept does not specify configuration";
607 return false;
609 if (!candidate_config()->IsSupported(*config_)) {
610 LOG(ERROR) << "session-accept specifies an invalid configuration";
611 return false;
614 return true;
617 void JingleSession::ProcessAuthenticationStep() {
618 DCHECK(CalledOnValidThread());
619 DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
621 if (state_ != CONNECTED && state_ != AUTHENTICATING) {
622 DCHECK(state_ == FAILED || state_ == CLOSED);
623 // The remote host closed the connection while the authentication was being
624 // processed asynchronously, nothing to do.
625 return;
628 if (authenticator_->state() == Authenticator::MESSAGE_READY) {
629 JingleMessage message(peer_jid_, JingleMessage::SESSION_INFO, session_id_);
630 message.info = authenticator_->GetNextMessage();
631 DCHECK(message.info.get());
632 SendMessage(message);
634 DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY);
636 // The current JingleSession object can be destroyed by event_handler of
637 // SetState(AUTHENTICATING) and cause subsequent dereferencing of the this
638 // pointer to crash. To protect against it, we run ContinueAuthenticationStep
639 // asychronously using a weak pointer.
640 base::ThreadTaskRunnerHandle::Get()->PostTask(
641 FROM_HERE,
642 base::Bind(&JingleSession::ContinueAuthenticationStep,
643 weak_factory_.GetWeakPtr()));
645 if (authenticator_->started()) {
646 SetState(AUTHENTICATING);
650 void JingleSession::ContinueAuthenticationStep() {
651 if (authenticator_->state() == Authenticator::ACCEPTED) {
652 OnAuthenticated();
653 } else if (authenticator_->state() == Authenticator::REJECTED) {
654 CloseInternal(AuthRejectionReasonToErrorCode(
655 authenticator_->rejection_reason()));
659 void JingleSession::OnAuthenticated() {
660 pseudotcp_channel_factory_.reset(new PseudoTcpChannelFactory(this));
661 secure_channel_factory_.reset(
662 new SecureChannelFactory(pseudotcp_channel_factory_.get(),
663 authenticator_.get()));
665 SetState(AUTHENTICATED);
668 void JingleSession::CloseInternal(ErrorCode error) {
669 DCHECK(CalledOnValidThread());
671 if (is_session_active()) {
672 // Send session-terminate message with the appropriate error code.
673 JingleMessage::Reason reason;
674 switch (error) {
675 case OK:
676 reason = JingleMessage::SUCCESS;
677 break;
678 case SESSION_REJECTED:
679 case AUTHENTICATION_FAILED:
680 reason = JingleMessage::DECLINE;
681 break;
682 case INCOMPATIBLE_PROTOCOL:
683 reason = JingleMessage::INCOMPATIBLE_PARAMETERS;
684 break;
685 case HOST_OVERLOAD:
686 reason = JingleMessage::CANCEL;
687 break;
688 default:
689 reason = JingleMessage::GENERAL_ERROR;
692 JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE,
693 session_id_);
694 message.reason = reason;
695 SendMessage(message);
698 error_ = error;
700 if (state_ != FAILED && state_ != CLOSED) {
701 if (error != OK) {
702 SetState(FAILED);
703 } else {
704 SetState(CLOSED);
709 void JingleSession::SetState(State new_state) {
710 DCHECK(CalledOnValidThread());
712 if (new_state != state_) {
713 DCHECK_NE(state_, CLOSED);
714 DCHECK_NE(state_, FAILED);
716 state_ = new_state;
717 if (event_handler_)
718 event_handler_->OnSessionStateChange(new_state);
722 bool JingleSession::is_session_active() {
723 return state_ == CONNECTING || state_ == ACCEPTING || state_ == CONNECTED ||
724 state_ == AUTHENTICATING || state_ == AUTHENTICATED;
727 } // namespace protocol
728 } // namespace remoting