| OLD | NEW |
| (Empty) |
| 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_(nullptr), | |
| 30 client_stub_(nullptr), | |
| 31 clipboard_stub_(nullptr), | |
| 32 audio_stub_(nullptr), | |
| 33 signal_strategy_(nullptr), | |
| 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(session_.get(), | |
| 188 session_->config().control_config(), this); | |
| 189 control_dispatcher_->set_client_stub(client_stub_); | |
| 190 control_dispatcher_->set_clipboard_stub(clipboard_stub_); | |
| 191 | |
| 192 event_dispatcher_.reset(new ClientEventDispatcher()); | |
| 193 event_dispatcher_->Init(session_.get(), session_->config().event_config(), | |
| 194 this); | |
| 195 | |
| 196 video_dispatcher_.reset( | |
| 197 new ClientVideoDispatcher(monitored_video_stub_.get())); | |
| 198 video_dispatcher_->Init(session_.get(), session_->config().video_config(), | |
| 199 this); | |
| 200 | |
| 201 if (session_->config().is_audio_enabled()) { | |
| 202 audio_reader_.reset(new AudioReader(audio_stub_)); | |
| 203 audio_reader_->Init(session_.get(), session_->config().audio_config(), | |
| 204 this); | |
| 205 } | |
| 206 break; | |
| 207 | |
| 208 case Session::CLOSED: | |
| 209 CloseChannels(); | |
| 210 SetState(CLOSED, OK); | |
| 211 break; | |
| 212 | |
| 213 case Session::FAILED: | |
| 214 // If we were connected then treat signaling timeout error as if | |
| 215 // the connection was closed by the peer. | |
| 216 // | |
| 217 // TODO(sergeyu): This logic belongs to the webapp, but we | |
| 218 // currently don't expose this error code to the webapp, and it | |
| 219 // would be hard to add it because client plugin and webapp | |
| 220 // versions may not be in sync. It should be easy to do after we | |
| 221 // are finished moving the client plugin to NaCl. | |
| 222 if (state_ == CONNECTED && session_->error() == SIGNALING_TIMEOUT) { | |
| 223 CloseChannels(); | |
| 224 SetState(CLOSED, OK); | |
| 225 } else { | |
| 226 CloseOnError(session_->error()); | |
| 227 } | |
| 228 break; | |
| 229 } | |
| 230 } | |
| 231 | |
| 232 void ConnectionToHost::OnSessionRouteChange(const std::string& channel_name, | |
| 233 const TransportRoute& route) { | |
| 234 event_callback_->OnRouteChanged(channel_name, route); | |
| 235 } | |
| 236 | |
| 237 void ConnectionToHost::OnChannelInitialized( | |
| 238 ChannelDispatcherBase* channel_dispatcher) { | |
| 239 NotifyIfChannelsReady(); | |
| 240 } | |
| 241 | |
| 242 void ConnectionToHost::OnChannelError( | |
| 243 ChannelDispatcherBase* channel_dispatcher, | |
| 244 ErrorCode error) { | |
| 245 LOG(ERROR) << "Failed to connect channel " << channel_dispatcher; | |
| 246 CloseOnError(CHANNEL_CONNECTION_ERROR); | |
| 247 return; | |
| 248 } | |
| 249 | |
| 250 void ConnectionToHost::OnVideoChannelStatus(bool active) { | |
| 251 event_callback_->OnConnectionReady(active); | |
| 252 } | |
| 253 | |
| 254 ConnectionToHost::State ConnectionToHost::state() const { | |
| 255 return state_; | |
| 256 } | |
| 257 | |
| 258 void ConnectionToHost::NotifyIfChannelsReady() { | |
| 259 if (!control_dispatcher_.get() || !control_dispatcher_->is_connected()) | |
| 260 return; | |
| 261 if (!event_dispatcher_.get() || !event_dispatcher_->is_connected()) | |
| 262 return; | |
| 263 if (!video_dispatcher_.get() || !video_dispatcher_->is_connected()) | |
| 264 return; | |
| 265 if ((!audio_reader_.get() || !audio_reader_->is_connected()) && | |
| 266 session_->config().is_audio_enabled()) { | |
| 267 return; | |
| 268 } | |
| 269 if (state_ != AUTHENTICATED) | |
| 270 return; | |
| 271 | |
| 272 // Start forwarding clipboard and input events. | |
| 273 clipboard_forwarder_.set_clipboard_stub(control_dispatcher_.get()); | |
| 274 event_forwarder_.set_input_stub(event_dispatcher_.get()); | |
| 275 SetState(CONNECTED, OK); | |
| 276 } | |
| 277 | |
| 278 void ConnectionToHost::CloseOnError(ErrorCode error) { | |
| 279 CloseChannels(); | |
| 280 SetState(FAILED, error); | |
| 281 } | |
| 282 | |
| 283 void ConnectionToHost::CloseChannels() { | |
| 284 control_dispatcher_.reset(); | |
| 285 event_dispatcher_.reset(); | |
| 286 clipboard_forwarder_.set_clipboard_stub(nullptr); | |
| 287 event_forwarder_.set_input_stub(nullptr); | |
| 288 video_dispatcher_.reset(); | |
| 289 audio_reader_.reset(); | |
| 290 } | |
| 291 | |
| 292 void ConnectionToHost::SetState(State state, ErrorCode error) { | |
| 293 DCHECK(CalledOnValidThread()); | |
| 294 // |error| should be specified only when |state| is set to FAILED. | |
| 295 DCHECK(state == FAILED || error == OK); | |
| 296 | |
| 297 if (state != state_) { | |
| 298 state_ = state; | |
| 299 error_ = error; | |
| 300 event_callback_->OnConnectionState(state_, error_); | |
| 301 } | |
| 302 } | |
| 303 | |
| 304 } // namespace protocol | |
| 305 } // namespace remoting | |
| OLD | NEW |