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.
5 #include "remoting/protocol/connection_to_host.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"
30 ConnectionToHost::ConnectionToHost(
31 bool allow_nat_traversal
)
32 : allow_nat_traversal_(allow_nat_traversal
),
33 event_callback_(NULL
),
35 clipboard_stub_(NULL
),
38 signal_strategy_(NULL
),
43 ConnectionToHost::~ConnectionToHost() {
49 if (session_manager_
.get())
50 session_manager_
.reset();
53 signal_strategy_
->RemoveListener(this);
56 ClipboardStub
* ConnectionToHost::clipboard_stub() {
57 return &clipboard_forwarder_
;
60 HostStub
* ConnectionToHost::host_stub() {
61 // TODO(wez): Add a HostFilter class, equivalent to input filter.
62 return control_dispatcher_
.get();
65 InputStub
* ConnectionToHost::input_stub() {
66 return &event_forwarder_
;
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();
87 // Save jid of the host. The actual connection is created later after
88 // |signal_strategy_| is connected.
90 host_public_key_
= host_public_key
;
92 signal_strategy_
->AddListener(this);
93 signal_strategy_
->Connect();
95 session_manager_
.reset(new JingleSessionManager(
96 transport_factory
.Pass(), allow_nat_traversal_
));
97 session_manager_
->Init(signal_strategy_
, this);
99 SetState(CONNECTING
, OK
);
102 const SessionConfig
& ConnectionToHost::config() {
103 return session_
->config();
106 void ConnectionToHost::OnSignalStrategyStateChange(
107 SignalStrategy::State state
) {
108 DCHECK(CalledOnValidThread());
109 DCHECK(event_callback_
);
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
);
119 bool ConnectionToHost::OnSignalStrategyIncomingStanza(
120 const buzz::XmlElement
* stanza
) {
124 void ConnectionToHost::OnSessionManagerReady() {
125 DCHECK(CalledOnValidThread());
127 // After SessionManager is initialized we can try to connect to the host.
128 scoped_ptr
<CandidateSessionConfig
> candidate_config
=
129 CandidateSessionConfig::CreateDefault();
131 CandidateSessionConfig::DisableAudioChannel(candidate_config
.get());
133 session_
= session_manager_
->Connect(
134 host_jid_
, authenticator_
.Pass(), candidate_config
.Pass());
135 session_
->SetEventHandler(this);
138 void ConnectionToHost::OnIncomingSession(
140 SessionManager::IncomingSessionResponse
* response
) {
141 DCHECK(CalledOnValidThread());
142 // Client always rejects incoming sessions.
143 *response
= SessionManager::DECLINE
;
146 void ConnectionToHost::OnSessionStateChange(
147 Session::State state
) {
148 DCHECK(CalledOnValidThread());
149 DCHECK(event_callback_
);
152 case Session::INITIALIZING
:
153 case Session::CONNECTING
:
154 case Session::ACCEPTING
:
155 case Session::CONNECTED
:
156 // Don't care about these events.
159 case Session::AUTHENTICATED
:
160 SetState(AUTHENTICATED
, OK
);
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_
);
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)));
176 video_reader_
= VideoReader::Create(session_
->config());
177 video_reader_
->Init(session_
.get(), video_stub_
, base::Bind(
178 &ConnectionToHost::OnChannelInitialized
, base::Unretained(this)));
180 audio_reader_
= AudioReader::Create(session_
->config());
181 if (audio_reader_
.get()) {
183 session_
.get(), session_
->config().audio_config(),
184 base::Bind(&ConnectionToHost::OnChannelInitialized
,
185 base::Unretained(this)));
186 audio_reader_
->set_audio_stub(audio_stub_
);
190 case Session::CLOSED
:
192 SetState(CLOSED
, OK
);
195 case Session::FAILED
:
196 // If we were connected then treat signaling timeout error as if
197 // the connection was closed by the peer.
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
) {
206 SetState(CLOSED
, OK
);
208 CloseOnError(session_
->error());
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";
220 void ConnectionToHost::OnSessionChannelReady(const std::string
& channel_name
,
223 not_ready_channels_
.erase(channel_name
);
225 not_ready_channels_
.insert(channel_name
);
228 event_callback_
->OnConnectionReady(not_ready_channels_
.empty());
231 ConnectionToHost::State
ConnectionToHost::state() const {
235 void ConnectionToHost::OnChannelInitialized(bool successful
) {
237 LOG(ERROR
) << "Failed to connect video channel";
238 CloseOnError(CHANNEL_CONNECTION_ERROR
);
242 NotifyIfChannelsReady();
245 void ConnectionToHost::NotifyIfChannelsReady() {
246 if (!control_dispatcher_
.get() || !control_dispatcher_
->is_connected())
248 if (!event_dispatcher_
.get() || !event_dispatcher_
->is_connected())
250 if (!video_reader_
.get() || !video_reader_
->is_connected())
252 if ((!audio_reader_
.get() || !audio_reader_
->is_connected()) &&
253 session_
->config().is_audio_enabled()) {
256 if (state_
!= AUTHENTICATED
)
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
);
265 void ConnectionToHost::CloseOnError(ErrorCode error
) {
267 SetState(FAILED
, error
);
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();
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
);
284 if (state
!= state_
) {
287 event_callback_
->OnConnectionState(state_
, error_
);
291 } // namespace protocol
292 } // namespace remoting