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
ConnectionToHost()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
~ConnectionToHost()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
Connect(SignalStrategy * signal_strategy,scoped_ptr<TransportFactory> transport_factory,scoped_ptr<Authenticator> authenticator,const std::string & host_jid,HostEventCallback * event_callback)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
set_candidate_config(scoped_ptr<CandidateSessionConfig> config)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
config()95 const SessionConfig& ConnectionToHost::config() {
96 return session_->config();
97 }
98
clipboard_forwarder()99 ClipboardStub* ConnectionToHost::clipboard_forwarder() {
100 return &clipboard_forwarder_;
101 }
102
host_stub()103 HostStub* ConnectionToHost::host_stub() {
104 // TODO(wez): Add a HostFilter class, equivalent to input filter.
105 return control_dispatcher_.get();
106 }
107
input_stub()108 InputStub* ConnectionToHost::input_stub() {
109 return &event_forwarder_;
110 }
111
set_client_stub(ClientStub * client_stub)112 void ConnectionToHost::set_client_stub(ClientStub* client_stub) {
113 client_stub_ = client_stub;
114 }
115
set_clipboard_stub(ClipboardStub * clipboard_stub)116 void ConnectionToHost::set_clipboard_stub(ClipboardStub* clipboard_stub) {
117 clipboard_stub_ = clipboard_stub;
118 }
119
set_video_stub(VideoStub * video_stub)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
set_audio_stub(AudioStub * audio_stub)130 void ConnectionToHost::set_audio_stub(AudioStub* audio_stub) {
131 audio_stub_ = audio_stub;
132 }
133
OnSignalStrategyStateChange(SignalStrategy::State state)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
OnSignalStrategyIncomingStanza(const buzz::XmlElement * stanza)147 bool ConnectionToHost::OnSignalStrategyIncomingStanza(
148 const buzz::XmlElement* stanza) {
149 return false;
150 }
151
OnSessionManagerReady()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
OnIncomingSession(Session * session,SessionManager::IncomingSessionResponse * response)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
OnSessionStateChange(Session::State state)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
OnSessionRouteChange(const std::string & channel_name,const TransportRoute & route)239 void ConnectionToHost::OnSessionRouteChange(const std::string& channel_name,
240 const TransportRoute& route) {
241 event_callback_->OnRouteChanged(channel_name, route);
242 }
243
OnVideoChannelStatus(bool active)244 void ConnectionToHost::OnVideoChannelStatus(bool active) {
245 event_callback_->OnConnectionReady(active);
246 }
247
state() const248 ConnectionToHost::State ConnectionToHost::state() const {
249 return state_;
250 }
251
OnChannelInitialized(bool successful)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
NotifyIfChannelsReady()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
CloseOnError(ErrorCode error)282 void ConnectionToHost::CloseOnError(ErrorCode error) {
283 CloseChannels();
284 SetState(FAILED, error);
285 }
286
CloseChannels()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
SetState(State state,ErrorCode error)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