Home | History | Annotate | Download | only in protocol
      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.
      4 
      5 #include "remoting/protocol/jingle_session.h"
      6 
      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/libjingle/source/talk/p2p/base/candidate.h"
     27 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
     28 
     29 using buzz::XmlElement;
     30 
     31 namespace remoting {
     32 namespace protocol {
     33 
     34 namespace {
     35 // Delay after candidate creation before sending transport-info
     36 // message. This is neccessary to be able to pack multiple candidates
     37 // into one transport-info messages. The value needs to be greater
     38 // than zero because ports are opened asynchronously in the browser
     39 // process.
     40 const int kTransportInfoSendDelayMs = 2;
     41 
     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;
     45 
     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;
     51 
     52 // Timeout for the transport-info messages.
     53 const int kTransportInfoTimeout = 10 * 60;
     54 
     55 // Name of the multiplexed channel.
     56 const char kMuxChannelName[] = "mux";
     57 
     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;
     65   }
     66   NOTREACHED();
     67   return UNKNOWN_ERROR;
     68 }
     69 
     70 }  // namespace
     71 
     72 JingleSession::JingleSession(JingleSessionManager* session_manager)
     73     : session_manager_(session_manager),
     74       event_handler_(NULL),
     75       state_(INITIALIZING),
     76       error_(OK),
     77       config_is_set_(false),
     78       weak_factory_(this) {
     79 }
     80 
     81 JingleSession::~JingleSession() {
     82   channel_multiplexer_.reset();
     83   STLDeleteContainerPointers(pending_requests_.begin(),
     84                              pending_requests_.end());
     85   STLDeleteContainerPointers(transport_info_requests_.begin(),
     86                              transport_info_requests_.end());
     87 
     88   DCHECK(channels_.empty());
     89 
     90   session_manager_->SessionDestroyed(this);
     91 }
     92 
     93 void JingleSession::SetEventHandler(Session::EventHandler* event_handler) {
     94   DCHECK(CalledOnValidThread());
     95   DCHECK(event_handler);
     96   event_handler_ = event_handler;
     97 }
     98 
     99 ErrorCode JingleSession::error() {
    100   DCHECK(CalledOnValidThread());
    101   return error_;
    102 }
    103 
    104 void JingleSession::StartConnection(
    105     const std::string& peer_jid,
    106     scoped_ptr<Authenticator> authenticator,
    107     scoped_ptr<CandidateSessionConfig> config) {
    108   DCHECK(CalledOnValidThread());
    109   DCHECK(authenticator.get());
    110   DCHECK_EQ(authenticator->state(), Authenticator::MESSAGE_READY);
    111 
    112   peer_jid_ = peer_jid;
    113   authenticator_ = authenticator.Pass();
    114   candidate_config_ = config.Pass();
    115 
    116   // Generate random session ID. There are usually not more than 1
    117   // concurrent session per host, so a random 64-bit integer provides
    118   // enough entropy. In the worst case connection will fail when two
    119   // clients generate the same session ID concurrently.
    120   session_id_ = base::Int64ToString(base::RandGenerator(kint64max));
    121 
    122   // Send session-initiate message.
    123   JingleMessage message(peer_jid_, JingleMessage::SESSION_INITIATE,
    124                         session_id_);
    125   message.initiator = session_manager_->signal_strategy_->GetLocalJid();
    126   message.description.reset(
    127       new ContentDescription(candidate_config_->Clone(),
    128                              authenticator_->GetNextMessage()));
    129   SendMessage(message);
    130 
    131   SetState(CONNECTING);
    132 }
    133 
    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);
    141 
    142   peer_jid_ = initiate_message.from;
    143   authenticator_ = authenticator.Pass();
    144   session_id_ = initiate_message.sid;
    145   candidate_config_ = initiate_message.description->config()->Clone();
    146 
    147   SetState(ACCEPTING);
    148 }
    149 
    150 void JingleSession::AcceptIncomingConnection(
    151     const JingleMessage& initiate_message) {
    152   DCHECK(config_is_set_);
    153 
    154   // Process the first authentication message.
    155   const buzz::XmlElement* first_auth_message =
    156       initiate_message.description->authenticator_message();
    157 
    158   if (!first_auth_message) {
    159     CloseInternal(INCOMPATIBLE_PROTOCOL);
    160     return;
    161   }
    162 
    163   DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
    164   // |authenticator_| is owned, so Unretained() is safe here.
    165   authenticator_->ProcessMessage(first_auth_message, base::Bind(
    166       &JingleSession::ContinueAcceptIncomingConnection,
    167       base::Unretained(this)));
    168 }
    169 
    170 void JingleSession::ContinueAcceptIncomingConnection() {
    171   DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
    172   if (authenticator_->state() == Authenticator::REJECTED) {
    173     CloseInternal(AuthRejectionReasonToErrorCode(
    174         authenticator_->rejection_reason()));
    175     return;
    176   }
    177 
    178   // Send the session-accept message.
    179   JingleMessage message(peer_jid_, JingleMessage::SESSION_ACCEPT,
    180                         session_id_);
    181 
    182   scoped_ptr<buzz::XmlElement> auth_message;
    183   if (authenticator_->state() == Authenticator::MESSAGE_READY)
    184     auth_message = authenticator_->GetNextMessage();
    185 
    186   message.description.reset(
    187       new ContentDescription(CandidateSessionConfig::CreateFrom(config_),
    188                              auth_message.Pass()));
    189   SendMessage(message);
    190 
    191   // Update state.
    192   SetState(CONNECTED);
    193 
    194   if (authenticator_->state() == Authenticator::ACCEPTED) {
    195     OnAuthenticated();
    196   } else {
    197     DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
    198     if (authenticator_->started()) {
    199       SetState(AUTHENTICATING);
    200     }
    201   }
    202 }
    203 
    204 const std::string& JingleSession::jid() {
    205   DCHECK(CalledOnValidThread());
    206   return peer_jid_;
    207 }
    208 
    209 const CandidateSessionConfig* JingleSession::candidate_config() {
    210   DCHECK(CalledOnValidThread());
    211   return candidate_config_.get();
    212 }
    213 
    214 const SessionConfig& JingleSession::config() {
    215   DCHECK(CalledOnValidThread());
    216   return config_;
    217 }
    218 
    219 void JingleSession::set_config(const SessionConfig& config) {
    220   DCHECK(CalledOnValidThread());
    221   DCHECK(!config_is_set_);
    222   config_ = config;
    223   config_is_set_ = true;
    224 }
    225 
    226 StreamChannelFactory* JingleSession::GetTransportChannelFactory() {
    227   DCHECK(CalledOnValidThread());
    228   return secure_channel_factory_.get();
    229 }
    230 
    231 StreamChannelFactory* JingleSession::GetMultiplexedChannelFactory() {
    232   DCHECK(CalledOnValidThread());
    233   if (!channel_multiplexer_.get()) {
    234     channel_multiplexer_.reset(
    235         new ChannelMultiplexer(GetTransportChannelFactory(), kMuxChannelName));
    236   }
    237   return channel_multiplexer_.get();
    238 }
    239 
    240 void JingleSession::Close() {
    241   DCHECK(CalledOnValidThread());
    242 
    243   CloseInternal(OK);
    244 }
    245 
    246 void JingleSession::AddPendingRemoteCandidates(Transport* channel,
    247                                                const std::string& name) {
    248   std::list<JingleMessage::NamedCandidate>::iterator it =
    249       pending_remote_candidates_.begin();
    250   while(it != pending_remote_candidates_.end()) {
    251     if (it->name == name) {
    252       channel->AddRemoteCandidate(it->candidate);
    253       it = pending_remote_candidates_.erase(it);
    254     } else {
    255       ++it;
    256     }
    257   }
    258 }
    259 
    260 void JingleSession::CreateChannel(const std::string& name,
    261                                   const ChannelCreatedCallback& callback) {
    262   DCHECK(!channels_[name]);
    263 
    264   scoped_ptr<Transport> channel =
    265       session_manager_->transport_factory_->CreateTransport();
    266   channel->Connect(name, this, callback);
    267   AddPendingRemoteCandidates(channel.get(), name);
    268   channels_[name] = channel.release();
    269 }
    270 
    271 void JingleSession::CancelChannelCreation(const std::string& name) {
    272   ChannelsMap::iterator it = channels_.find(name);
    273   if (it != channels_.end()) {
    274     DCHECK(!it->second->is_connected());
    275     delete it->second;
    276     DCHECK(channels_.find(name) == channels_.end());
    277   }
    278 }
    279 
    280 void JingleSession::OnTransportCandidate(Transport* transport,
    281                                          const cricket::Candidate& candidate) {
    282   pending_candidates_.push_back(JingleMessage::NamedCandidate(
    283       transport->name(), candidate));
    284 
    285   if (!transport_infos_timer_.IsRunning()) {
    286     // Delay sending the new candidates in case we get more candidates
    287     // that we can send in one message.
    288     transport_infos_timer_.Start(
    289         FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs),
    290         this, &JingleSession::SendTransportInfo);
    291   }
    292 }
    293 
    294 void JingleSession::OnTransportRouteChange(Transport* transport,
    295                                            const TransportRoute& route) {
    296   if (event_handler_)
    297     event_handler_->OnSessionRouteChange(transport->name(), route);
    298 }
    299 
    300 void JingleSession::OnTransportFailed(Transport* transport) {
    301   CloseInternal(CHANNEL_CONNECTION_ERROR);
    302 }
    303 
    304 void JingleSession::OnTransportDeleted(Transport* transport) {
    305   ChannelsMap::iterator it = channels_.find(transport->name());
    306   DCHECK_EQ(it->second, transport);
    307   channels_.erase(it);
    308 }
    309 
    310 void JingleSession::SendMessage(const JingleMessage& message) {
    311   scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
    312       message.ToXml(),
    313       base::Bind(&JingleSession::OnMessageResponse,
    314                  base::Unretained(this),
    315                  message.action));
    316 
    317   int timeout = kDefaultMessageTimeout;
    318   if (message.action == JingleMessage::SESSION_INITIATE ||
    319       message.action == JingleMessage::SESSION_ACCEPT) {
    320     timeout = kSessionInitiateAndAcceptTimeout;
    321   }
    322   if (request) {
    323     request->SetTimeout(base::TimeDelta::FromSeconds(timeout));
    324     pending_requests_.insert(request.release());
    325   } else {
    326     LOG(ERROR) << "Failed to send a "
    327                << JingleMessage::GetActionName(message.action) << " message";
    328   }
    329 }
    330 
    331 void JingleSession::OnMessageResponse(
    332     JingleMessage::ActionType request_type,
    333     IqRequest* request,
    334     const buzz::XmlElement* response) {
    335   // Delete the request from the list of pending requests.
    336   pending_requests_.erase(request);
    337   delete request;
    338 
    339   // Ignore all responses after session was closed.
    340   if (state_ == CLOSED || state_ == FAILED)
    341     return;
    342 
    343   std::string type_str = JingleMessage::GetActionName(request_type);
    344 
    345   // |response| will be NULL if the request timed out.
    346   if (!response) {
    347     LOG(ERROR) << type_str << " request timed out.";
    348     CloseInternal(SIGNALING_TIMEOUT);
    349     return;
    350   } else {
    351     const std::string& type =
    352         response->Attr(buzz::QName(std::string(), "type"));
    353     if (type != "result") {
    354       LOG(ERROR) << "Received error in response to " << type_str
    355                  << " message: \"" << response->Str()
    356                  << "\". Terminating the session.";
    357 
    358       // TODO(sergeyu): There may be different reasons for error
    359       // here. Parse the response stanza to find failure reason.
    360       CloseInternal(PEER_IS_OFFLINE);
    361     }
    362   }
    363 }
    364 
    365 void JingleSession::SendTransportInfo() {
    366   JingleMessage message(peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_);
    367   message.candidates.swap(pending_candidates_);
    368 
    369   scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
    370       message.ToXml(),
    371       base::Bind(&JingleSession::OnTransportInfoResponse,
    372                  base::Unretained(this)));
    373   if (request) {
    374     request->SetTimeout(base::TimeDelta::FromSeconds(kTransportInfoTimeout));
    375     transport_info_requests_.push_back(request.release());
    376   } else {
    377     LOG(ERROR) << "Failed to send a transport-info message";
    378   }
    379 }
    380 
    381 void JingleSession::OnTransportInfoResponse(IqRequest* request,
    382                                             const buzz::XmlElement* response) {
    383   DCHECK(!transport_info_requests_.empty());
    384 
    385   // Consider transport-info requests sent before this one lost and delete
    386   // corresponding IqRequest objects.
    387   while (transport_info_requests_.front() != request) {
    388     delete transport_info_requests_.front();
    389     transport_info_requests_.pop_front();
    390   }
    391 
    392   // Delete the |request| itself.
    393   DCHECK_EQ(request, transport_info_requests_.front());
    394   delete request;
    395   transport_info_requests_.pop_front();
    396 
    397   // Ignore transport-info timeouts.
    398   if (!response) {
    399     LOG(ERROR) << "transport-info request has timed out.";
    400     return;
    401   }
    402 
    403   const std::string& type = response->Attr(buzz::QName(std::string(), "type"));
    404   if (type != "result") {
    405     LOG(ERROR) << "Received error in response to transport-info message: \""
    406                << response->Str() << "\". Terminating the session.";
    407     CloseInternal(PEER_IS_OFFLINE);
    408   }
    409 }
    410 
    411 void JingleSession::OnIncomingMessage(const JingleMessage& message,
    412                                       const ReplyCallback& reply_callback) {
    413   DCHECK(CalledOnValidThread());
    414 
    415   if (message.from != peer_jid_) {
    416     // Ignore messages received from a different Jid.
    417     reply_callback.Run(JingleMessageReply::INVALID_SID);
    418     return;
    419   }
    420 
    421   switch (message.action) {
    422     case JingleMessage::SESSION_ACCEPT:
    423       OnAccept(message, reply_callback);
    424       break;
    425 
    426     case JingleMessage::SESSION_INFO:
    427       OnSessionInfo(message, reply_callback);
    428       break;
    429 
    430     case JingleMessage::TRANSPORT_INFO:
    431       reply_callback.Run(JingleMessageReply::NONE);
    432       ProcessTransportInfo(message);
    433       break;
    434 
    435     case JingleMessage::SESSION_TERMINATE:
    436       OnTerminate(message, reply_callback);
    437       break;
    438 
    439     default:
    440       reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
    441   }
    442 }
    443 
    444 void JingleSession::OnAccept(const JingleMessage& message,
    445                              const ReplyCallback& reply_callback) {
    446   if (state_ != CONNECTING) {
    447     reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
    448     return;
    449   }
    450 
    451   reply_callback.Run(JingleMessageReply::NONE);
    452 
    453   const buzz::XmlElement* auth_message =
    454       message.description->authenticator_message();
    455   if (!auth_message) {
    456     DLOG(WARNING) << "Received session-accept without authentication message ";
    457     CloseInternal(INCOMPATIBLE_PROTOCOL);
    458     return;
    459   }
    460 
    461   if (!InitializeConfigFromDescription(message.description.get())) {
    462     CloseInternal(INCOMPATIBLE_PROTOCOL);
    463     return;
    464   }
    465 
    466   // In case there is transport information in the accept message.
    467   ProcessTransportInfo(message);
    468 
    469   SetState(CONNECTED);
    470 
    471   DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE);
    472   authenticator_->ProcessMessage(auth_message, base::Bind(
    473       &JingleSession::ProcessAuthenticationStep,base::Unretained(this)));
    474 }
    475 
    476 void JingleSession::OnSessionInfo(const JingleMessage& message,
    477                                   const ReplyCallback& reply_callback) {
    478   if (!message.info.get() ||
    479       !Authenticator::IsAuthenticatorMessage(message.info.get())) {
    480     reply_callback.Run(JingleMessageReply::UNSUPPORTED_INFO);
    481     return;
    482   }
    483 
    484   if ((state_ != CONNECTED && state_ != AUTHENTICATING) ||
    485       authenticator_->state() != Authenticator::WAITING_MESSAGE) {
    486     LOG(WARNING) << "Received unexpected authenticator message "
    487                  << message.info->Str();
    488     reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
    489     CloseInternal(INCOMPATIBLE_PROTOCOL);
    490     return;
    491   }
    492 
    493   reply_callback.Run(JingleMessageReply::NONE);
    494 
    495   authenticator_->ProcessMessage(message.info.get(), base::Bind(
    496       &JingleSession::ProcessAuthenticationStep, base::Unretained(this)));
    497 }
    498 
    499 void JingleSession::ProcessTransportInfo(const JingleMessage& message) {
    500   for (std::list<JingleMessage::NamedCandidate>::const_iterator it =
    501            message.candidates.begin();
    502        it != message.candidates.end(); ++it) {
    503     ChannelsMap::iterator channel = channels_.find(it->name);
    504     if (channel != channels_.end()) {
    505       channel->second->AddRemoteCandidate(it->candidate);
    506     } else {
    507       // Transport info was received before the channel was created.
    508       // This could happen due to messages being reordered on the wire.
    509       pending_remote_candidates_.push_back(*it);
    510     }
    511   }
    512 }
    513 
    514 void JingleSession::OnTerminate(const JingleMessage& message,
    515                                 const ReplyCallback& reply_callback) {
    516   if (!is_session_active()) {
    517     LOG(WARNING) << "Received unexpected session-terminate message.";
    518     reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
    519     return;
    520   }
    521 
    522   reply_callback.Run(JingleMessageReply::NONE);
    523 
    524   switch (message.reason) {
    525     case JingleMessage::SUCCESS:
    526       if (state_ == CONNECTING) {
    527         error_ = SESSION_REJECTED;
    528       } else {
    529         error_ = OK;
    530       }
    531       break;
    532     case JingleMessage::DECLINE:
    533       error_ = AUTHENTICATION_FAILED;
    534       break;
    535     case JingleMessage::CANCEL:
    536       error_ = HOST_OVERLOAD;
    537       break;
    538     case JingleMessage::GENERAL_ERROR:
    539       error_ = CHANNEL_CONNECTION_ERROR;
    540       break;
    541     case JingleMessage::INCOMPATIBLE_PARAMETERS:
    542       error_ = INCOMPATIBLE_PROTOCOL;
    543       break;
    544     default:
    545       error_ = UNKNOWN_ERROR;
    546   }
    547 
    548   if (error_ != OK) {
    549     SetState(FAILED);
    550   } else {
    551     SetState(CLOSED);
    552   }
    553 }
    554 
    555 bool JingleSession::InitializeConfigFromDescription(
    556     const ContentDescription* description) {
    557   DCHECK(description);
    558 
    559   if (!description->config()->GetFinalConfig(&config_)) {
    560     LOG(ERROR) << "session-accept does not specify configuration";
    561     return false;
    562   }
    563   if (!candidate_config()->IsSupported(config_)) {
    564     LOG(ERROR) << "session-accept specifies an invalid configuration";
    565     return false;
    566   }
    567 
    568   return true;
    569 }
    570 
    571 void JingleSession::ProcessAuthenticationStep() {
    572   DCHECK(CalledOnValidThread());
    573   DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
    574 
    575   if (state_ != CONNECTED && state_ != AUTHENTICATING) {
    576     DCHECK(state_ == FAILED || state_ == CLOSED);
    577     // The remote host closed the connection while the authentication was being
    578     // processed asynchronously, nothing to do.
    579     return;
    580   }
    581 
    582   if (authenticator_->state() == Authenticator::MESSAGE_READY) {
    583     JingleMessage message(peer_jid_, JingleMessage::SESSION_INFO, session_id_);
    584     message.info = authenticator_->GetNextMessage();
    585     DCHECK(message.info.get());
    586     SendMessage(message);
    587   }
    588   DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY);
    589 
    590   // The current JingleSession object can be destroyed by event_handler of
    591   // SetState(AUTHENTICATING) and cause subsequent dereferencing of the this
    592   // pointer to crash.  To protect against it, we run ContinueAuthenticationStep
    593   // asychronously using a weak pointer.
    594   base::ThreadTaskRunnerHandle::Get()->PostTask(
    595     FROM_HERE,
    596     base::Bind(&JingleSession::ContinueAuthenticationStep,
    597                weak_factory_.GetWeakPtr()));
    598 
    599   if (authenticator_->started()) {
    600     SetState(AUTHENTICATING);
    601   }
    602 }
    603 
    604 void JingleSession::ContinueAuthenticationStep() {
    605   if (authenticator_->state() == Authenticator::ACCEPTED) {
    606     OnAuthenticated();
    607   } else if (authenticator_->state() == Authenticator::REJECTED) {
    608     CloseInternal(AuthRejectionReasonToErrorCode(
    609         authenticator_->rejection_reason()));
    610   }
    611 }
    612 
    613 void JingleSession::OnAuthenticated() {
    614   pseudotcp_channel_factory_.reset(new PseudoTcpChannelFactory(this));
    615   secure_channel_factory_.reset(
    616       new SecureChannelFactory(pseudotcp_channel_factory_.get(),
    617                                authenticator_.get()));
    618 
    619   SetState(AUTHENTICATED);
    620 }
    621 
    622 void JingleSession::CloseInternal(ErrorCode error) {
    623   DCHECK(CalledOnValidThread());
    624 
    625   if (is_session_active()) {
    626     // Send session-terminate message with the appropriate error code.
    627     JingleMessage::Reason reason;
    628     switch (error) {
    629       case OK:
    630         reason = JingleMessage::SUCCESS;
    631         break;
    632       case SESSION_REJECTED:
    633       case AUTHENTICATION_FAILED:
    634         reason = JingleMessage::DECLINE;
    635         break;
    636       case INCOMPATIBLE_PROTOCOL:
    637         reason = JingleMessage::INCOMPATIBLE_PARAMETERS;
    638         break;
    639       case HOST_OVERLOAD:
    640         reason = JingleMessage::CANCEL;
    641         break;
    642       default:
    643         reason = JingleMessage::GENERAL_ERROR;
    644     }
    645 
    646     JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE,
    647                           session_id_);
    648     message.reason = reason;
    649     SendMessage(message);
    650   }
    651 
    652   error_ = error;
    653 
    654   if (state_ != FAILED && state_ != CLOSED) {
    655     if (error != OK) {
    656       SetState(FAILED);
    657     } else {
    658       SetState(CLOSED);
    659     }
    660   }
    661 }
    662 
    663 void JingleSession::SetState(State new_state) {
    664   DCHECK(CalledOnValidThread());
    665 
    666   if (new_state != state_) {
    667     DCHECK_NE(state_, CLOSED);
    668     DCHECK_NE(state_, FAILED);
    669 
    670     state_ = new_state;
    671     if (event_handler_)
    672       event_handler_->OnSessionStateChange(new_state);
    673   }
    674 }
    675 
    676 bool JingleSession::is_session_active() {
    677   return state_ == CONNECTING || state_ == ACCEPTING || state_ == CONNECTED ||
    678         state_ == AUTHENTICATING || state_ == AUTHENTICATED;
    679 }
    680 
    681 }  // namespace protocol
    682 }  // namespace remoting
    683