• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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