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