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/connection_to_host.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/callback.h"
      9 #include "base/location.h"
     10 #include "remoting/base/constants.h"
     11 #include "remoting/protocol/audio_reader.h"
     12 #include "remoting/protocol/audio_stub.h"
     13 #include "remoting/protocol/auth_util.h"
     14 #include "remoting/protocol/authenticator.h"
     15 #include "remoting/protocol/client_control_dispatcher.h"
     16 #include "remoting/protocol/client_event_dispatcher.h"
     17 #include "remoting/protocol/client_stub.h"
     18 #include "remoting/protocol/client_video_dispatcher.h"
     19 #include "remoting/protocol/clipboard_stub.h"
     20 #include "remoting/protocol/errors.h"
     21 #include "remoting/protocol/jingle_session_manager.h"
     22 #include "remoting/protocol/transport.h"
     23 #include "remoting/protocol/video_stub.h"
     24 
     25 namespace remoting {
     26 namespace protocol {
     27 
     28 ConnectionToHost::ConnectionToHost()
     29     : event_callback_(NULL),
     30       client_stub_(NULL),
     31       clipboard_stub_(NULL),
     32       audio_stub_(NULL),
     33       signal_strategy_(NULL),
     34       state_(INITIALIZING),
     35       error_(OK) {
     36 }
     37 
     38 ConnectionToHost::~ConnectionToHost() {
     39   CloseChannels();
     40 
     41   if (session_.get())
     42     session_.reset();
     43 
     44   if (session_manager_.get())
     45     session_manager_.reset();
     46 
     47   if (signal_strategy_)
     48     signal_strategy_->RemoveListener(this);
     49 }
     50 
     51 void ConnectionToHost::Connect(SignalStrategy* signal_strategy,
     52                                scoped_ptr<TransportFactory> transport_factory,
     53                                scoped_ptr<Authenticator> authenticator,
     54                                const std::string& host_jid,
     55                                HostEventCallback* event_callback) {
     56   DCHECK(client_stub_);
     57   DCHECK(clipboard_stub_);
     58   DCHECK(monitored_video_stub_);
     59 
     60   // Initialize default |candidate_config_| if set_candidate_config() wasn't
     61   // called.
     62   if (!candidate_config_) {
     63     candidate_config_ = CandidateSessionConfig::CreateDefault();
     64     if (!audio_stub_) {
     65       candidate_config_->DisableAudioChannel();
     66     }
     67     candidate_config_->EnableVideoCodec(ChannelConfig::CODEC_VP9);
     68   }
     69 
     70   signal_strategy_ = signal_strategy;
     71   event_callback_ = event_callback;
     72   authenticator_ = authenticator.Pass();
     73 
     74   // Save jid of the host. The actual connection is created later after
     75   // |signal_strategy_| is connected.
     76   host_jid_ = host_jid;
     77 
     78   signal_strategy_->AddListener(this);
     79   signal_strategy_->Connect();
     80 
     81   session_manager_.reset(new JingleSessionManager(transport_factory.Pass()));
     82   session_manager_->Init(signal_strategy_, this);
     83 
     84   SetState(CONNECTING, OK);
     85 }
     86 
     87 void ConnectionToHost::set_candidate_config(
     88     scoped_ptr<CandidateSessionConfig> config) {
     89   DCHECK_EQ(state_, INITIALIZING);
     90 
     91   candidate_config_ = config.Pass();
     92 }
     93 
     94 
     95 const SessionConfig& ConnectionToHost::config() {
     96   return session_->config();
     97 }
     98 
     99 ClipboardStub* ConnectionToHost::clipboard_forwarder() {
    100   return &clipboard_forwarder_;
    101 }
    102 
    103 HostStub* ConnectionToHost::host_stub() {
    104   // TODO(wez): Add a HostFilter class, equivalent to input filter.
    105   return control_dispatcher_.get();
    106 }
    107 
    108 InputStub* ConnectionToHost::input_stub() {
    109   return &event_forwarder_;
    110 }
    111 
    112 void ConnectionToHost::set_client_stub(ClientStub* client_stub) {
    113   client_stub_ = client_stub;
    114 }
    115 
    116 void ConnectionToHost::set_clipboard_stub(ClipboardStub* clipboard_stub) {
    117   clipboard_stub_ = clipboard_stub;
    118 }
    119 
    120 void ConnectionToHost::set_video_stub(VideoStub* video_stub) {
    121   DCHECK(video_stub);
    122   monitored_video_stub_.reset(new MonitoredVideoStub(
    123       video_stub,
    124       base::TimeDelta::FromSeconds(
    125           MonitoredVideoStub::kConnectivityCheckDelaySeconds),
    126       base::Bind(&ConnectionToHost::OnVideoChannelStatus,
    127                  base::Unretained(this))));
    128 }
    129 
    130 void ConnectionToHost::set_audio_stub(AudioStub* audio_stub) {
    131   audio_stub_ = audio_stub;
    132 }
    133 
    134 void ConnectionToHost::OnSignalStrategyStateChange(
    135     SignalStrategy::State state) {
    136   DCHECK(CalledOnValidThread());
    137   DCHECK(event_callback_);
    138 
    139   if (state == SignalStrategy::CONNECTED) {
    140     VLOG(1) << "Connected as: " << signal_strategy_->GetLocalJid();
    141   } else if (state == SignalStrategy::DISCONNECTED) {
    142     VLOG(1) << "Connection closed.";
    143     CloseOnError(SIGNALING_ERROR);
    144   }
    145 }
    146 
    147 bool ConnectionToHost::OnSignalStrategyIncomingStanza(
    148    const buzz::XmlElement* stanza) {
    149   return false;
    150 }
    151 
    152 void ConnectionToHost::OnSessionManagerReady() {
    153   DCHECK(CalledOnValidThread());
    154 
    155   // After SessionManager is initialized we can try to connect to the host.
    156   session_ = session_manager_->Connect(
    157       host_jid_, authenticator_.Pass(), candidate_config_.Pass());
    158   session_->SetEventHandler(this);
    159 }
    160 
    161 void ConnectionToHost::OnIncomingSession(
    162     Session* session,
    163     SessionManager::IncomingSessionResponse* response) {
    164   DCHECK(CalledOnValidThread());
    165   // Client always rejects incoming sessions.
    166   *response = SessionManager::DECLINE;
    167 }
    168 
    169 void ConnectionToHost::OnSessionStateChange(
    170     Session::State state) {
    171   DCHECK(CalledOnValidThread());
    172   DCHECK(event_callback_);
    173 
    174   switch (state) {
    175     case Session::INITIALIZING:
    176     case Session::CONNECTING:
    177     case Session::ACCEPTING:
    178     case Session::CONNECTED:
    179     case Session::AUTHENTICATING:
    180       // Don't care about these events.
    181       break;
    182 
    183     case Session::AUTHENTICATED:
    184       SetState(AUTHENTICATED, OK);
    185 
    186       control_dispatcher_.reset(new ClientControlDispatcher());
    187       control_dispatcher_->Init(
    188           session_.get(), session_->config().control_config(),
    189           base::Bind(&ConnectionToHost::OnChannelInitialized,
    190                      base::Unretained(this)));
    191       control_dispatcher_->set_client_stub(client_stub_);
    192       control_dispatcher_->set_clipboard_stub(clipboard_stub_);
    193 
    194       event_dispatcher_.reset(new ClientEventDispatcher());
    195       event_dispatcher_->Init(
    196           session_.get(), session_->config().event_config(),
    197           base::Bind(&ConnectionToHost::OnChannelInitialized,
    198                      base::Unretained(this)));
    199 
    200       video_dispatcher_.reset(
    201           new ClientVideoDispatcher(monitored_video_stub_.get()));
    202       video_dispatcher_->Init(session_.get(), session_->config().video_config(),
    203                           base::Bind(&ConnectionToHost::OnChannelInitialized,
    204                                      base::Unretained(this)));
    205 
    206       audio_reader_ = AudioReader::Create(session_->config());
    207       if (audio_reader_.get()) {
    208         audio_reader_->Init(session_.get(), session_->config().audio_config(),
    209                             base::Bind(&ConnectionToHost::OnChannelInitialized,
    210                                        base::Unretained(this)));
    211         audio_reader_->set_audio_stub(audio_stub_);
    212       }
    213       break;
    214 
    215     case Session::CLOSED:
    216       CloseChannels();
    217       SetState(CLOSED, OK);
    218       break;
    219 
    220     case Session::FAILED:
    221       // If we were connected then treat signaling timeout error as if
    222       // the connection was closed by the peer.
    223       //
    224       // TODO(sergeyu): This logic belongs to the webapp, but we
    225       // currently don't expose this error code to the webapp, and it
    226       // would be hard to add it because client plugin and webapp
    227       // versions may not be in sync. It should be easy to do after we
    228       // are finished moving the client plugin to NaCl.
    229       if (state_ == CONNECTED && session_->error() == SIGNALING_TIMEOUT) {
    230         CloseChannels();
    231         SetState(CLOSED, OK);
    232       } else {
    233         CloseOnError(session_->error());
    234       }
    235       break;
    236   }
    237 }
    238 
    239 void ConnectionToHost::OnSessionRouteChange(const std::string& channel_name,
    240                                             const TransportRoute& route) {
    241   event_callback_->OnRouteChanged(channel_name, route);
    242 }
    243 
    244 void ConnectionToHost::OnVideoChannelStatus(bool active) {
    245   event_callback_->OnConnectionReady(active);
    246 }
    247 
    248 ConnectionToHost::State ConnectionToHost::state() const {
    249   return state_;
    250 }
    251 
    252 void ConnectionToHost::OnChannelInitialized(bool successful) {
    253   if (!successful) {
    254     LOG(ERROR) << "Failed to connect video channel";
    255     CloseOnError(CHANNEL_CONNECTION_ERROR);
    256     return;
    257   }
    258 
    259   NotifyIfChannelsReady();
    260 }
    261 
    262 void ConnectionToHost::NotifyIfChannelsReady() {
    263   if (!control_dispatcher_.get() || !control_dispatcher_->is_connected())
    264     return;
    265   if (!event_dispatcher_.get() || !event_dispatcher_->is_connected())
    266     return;
    267   if (!video_dispatcher_.get() || !video_dispatcher_->is_connected())
    268     return;
    269   if ((!audio_reader_.get() || !audio_reader_->is_connected()) &&
    270       session_->config().is_audio_enabled()) {
    271     return;
    272   }
    273   if (state_ != AUTHENTICATED)
    274     return;
    275 
    276   // Start forwarding clipboard and input events.
    277   clipboard_forwarder_.set_clipboard_stub(control_dispatcher_.get());
    278   event_forwarder_.set_input_stub(event_dispatcher_.get());
    279   SetState(CONNECTED, OK);
    280 }
    281 
    282 void ConnectionToHost::CloseOnError(ErrorCode error) {
    283   CloseChannels();
    284   SetState(FAILED, error);
    285 }
    286 
    287 void ConnectionToHost::CloseChannels() {
    288   control_dispatcher_.reset();
    289   event_dispatcher_.reset();
    290   clipboard_forwarder_.set_clipboard_stub(NULL);
    291   event_forwarder_.set_input_stub(NULL);
    292   video_dispatcher_.reset();
    293   audio_reader_.reset();
    294 }
    295 
    296 void ConnectionToHost::SetState(State state, ErrorCode error) {
    297   DCHECK(CalledOnValidThread());
    298   // |error| should be specified only when |state| is set to FAILED.
    299   DCHECK(state == FAILED || error == OK);
    300 
    301   if (state != state_) {
    302     state_ = state;
    303     error_ = error;
    304     event_callback_->OnConnectionState(state_, error_);
    305   }
    306 }
    307 
    308 }  // namespace protocol
    309 }  // namespace remoting
    310