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