Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / remoting / protocol / connection_to_host_impl.cc
blobc524779228b21fe5c03769be259ceb14c5b0f54f
1 // Copyright 2015 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_impl.h"
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"
25 namespace remoting {
26 namespace protocol {
28 ConnectionToHostImpl::ConnectionToHostImpl()
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) {
38 ConnectionToHostImpl::~ConnectionToHostImpl() {
39 CloseChannels();
41 if (session_.get())
42 session_.reset();
44 if (session_manager_.get())
45 session_manager_.reset();
47 if (signal_strategy_)
48 signal_strategy_->RemoveListener(this);
51 void ConnectionToHostImpl::Connect(
52 SignalStrategy* signal_strategy,
53 scoped_ptr<TransportFactory> transport_factory,
54 scoped_ptr<Authenticator> authenticator,
55 const std::string& host_jid,
56 HostEventCallback* event_callback) {
57 DCHECK(client_stub_);
58 DCHECK(clipboard_stub_);
59 DCHECK(monitored_video_stub_);
61 // Initialize default |candidate_config_| if set_candidate_config() wasn't
62 // called.
63 if (!candidate_config_) {
64 candidate_config_ = CandidateSessionConfig::CreateDefault();
65 if (!audio_stub_) {
66 candidate_config_->DisableAudioChannel();
68 candidate_config_->EnableVideoCodec(ChannelConfig::CODEC_VP9);
71 signal_strategy_ = signal_strategy;
72 event_callback_ = event_callback;
73 authenticator_ = authenticator.Pass();
75 // Save jid of the host. The actual connection is created later after
76 // |signal_strategy_| is connected.
77 host_jid_ = host_jid;
79 signal_strategy_->AddListener(this);
80 signal_strategy_->Connect();
82 session_manager_.reset(new JingleSessionManager(transport_factory.Pass()));
83 session_manager_->Init(signal_strategy_, this);
85 SetState(CONNECTING, OK);
88 void ConnectionToHostImpl::set_candidate_config(
89 scoped_ptr<CandidateSessionConfig> config) {
90 DCHECK_EQ(state_, INITIALIZING);
92 candidate_config_ = config.Pass();
95 const SessionConfig& ConnectionToHostImpl::config() {
96 return session_->config();
99 ClipboardStub* ConnectionToHostImpl::clipboard_forwarder() {
100 return &clipboard_forwarder_;
103 HostStub* ConnectionToHostImpl::host_stub() {
104 // TODO(wez): Add a HostFilter class, equivalent to input filter.
105 return control_dispatcher_.get();
108 InputStub* ConnectionToHostImpl::input_stub() {
109 return &event_forwarder_;
112 void ConnectionToHostImpl::set_client_stub(ClientStub* client_stub) {
113 client_stub_ = client_stub;
116 void ConnectionToHostImpl::set_clipboard_stub(ClipboardStub* clipboard_stub) {
117 clipboard_stub_ = clipboard_stub;
120 void ConnectionToHostImpl::set_video_stub(VideoStub* video_stub) {
121 DCHECK(video_stub);
122 monitored_video_stub_.reset(new MonitoredVideoStub(
123 video_stub, base::TimeDelta::FromSeconds(
124 MonitoredVideoStub::kConnectivityCheckDelaySeconds),
125 base::Bind(&ConnectionToHostImpl::OnVideoChannelStatus,
126 base::Unretained(this))));
129 void ConnectionToHostImpl::set_audio_stub(AudioStub* audio_stub) {
130 audio_stub_ = audio_stub;
133 void ConnectionToHostImpl::OnSignalStrategyStateChange(
134 SignalStrategy::State state) {
135 DCHECK(CalledOnValidThread());
136 DCHECK(event_callback_);
138 if (state == SignalStrategy::CONNECTED) {
139 VLOG(1) << "Connected as: " << signal_strategy_->GetLocalJid();
140 } else if (state == SignalStrategy::DISCONNECTED) {
141 VLOG(1) << "Connection closed.";
142 CloseOnError(SIGNALING_ERROR);
146 bool ConnectionToHostImpl::OnSignalStrategyIncomingStanza(
147 const buzz::XmlElement* stanza) {
148 return false;
151 void ConnectionToHostImpl::OnSessionManagerReady() {
152 DCHECK(CalledOnValidThread());
154 // After SessionManager is initialized we can try to connect to the host.
155 session_ = session_manager_->Connect(host_jid_, authenticator_.Pass(),
156 candidate_config_.Pass());
157 session_->SetEventHandler(this);
160 void ConnectionToHostImpl::OnIncomingSession(
161 Session* session,
162 SessionManager::IncomingSessionResponse* response) {
163 DCHECK(CalledOnValidThread());
164 // Client always rejects incoming sessions.
165 *response = SessionManager::DECLINE;
168 void ConnectionToHostImpl::OnSessionStateChange(Session::State state) {
169 DCHECK(CalledOnValidThread());
170 DCHECK(event_callback_);
172 switch (state) {
173 case Session::INITIALIZING:
174 case Session::CONNECTING:
175 case Session::ACCEPTING:
176 case Session::CONNECTED:
177 case Session::AUTHENTICATING:
178 // Don't care about these events.
179 break;
181 case Session::AUTHENTICATED:
182 SetState(AUTHENTICATED, OK);
184 control_dispatcher_.reset(new ClientControlDispatcher());
185 control_dispatcher_->Init(session_.get(),
186 session_->config().control_config(), this);
187 control_dispatcher_->set_client_stub(client_stub_);
188 control_dispatcher_->set_clipboard_stub(clipboard_stub_);
190 event_dispatcher_.reset(new ClientEventDispatcher());
191 event_dispatcher_->Init(session_.get(), session_->config().event_config(),
192 this);
194 video_dispatcher_.reset(
195 new ClientVideoDispatcher(monitored_video_stub_.get()));
196 video_dispatcher_->Init(session_.get(), session_->config().video_config(),
197 this);
199 if (session_->config().is_audio_enabled()) {
200 audio_reader_.reset(new AudioReader(audio_stub_));
201 audio_reader_->Init(session_.get(), session_->config().audio_config(),
202 this);
204 break;
206 case Session::CLOSED:
207 CloseChannels();
208 SetState(CLOSED, OK);
209 break;
211 case Session::FAILED:
212 // If we were connected then treat signaling timeout error as if
213 // the connection was closed by the peer.
215 // TODO(sergeyu): This logic belongs to the webapp, but we
216 // currently don't expose this error code to the webapp, and it
217 // would be hard to add it because client plugin and webapp
218 // versions may not be in sync. It should be easy to do after we
219 // are finished moving the client plugin to NaCl.
220 if (state_ == CONNECTED && session_->error() == SIGNALING_TIMEOUT) {
221 CloseChannels();
222 SetState(CLOSED, OK);
223 } else {
224 CloseOnError(session_->error());
226 break;
230 void ConnectionToHostImpl::OnSessionRouteChange(const std::string& channel_name,
231 const TransportRoute& route) {
232 event_callback_->OnRouteChanged(channel_name, route);
235 void ConnectionToHostImpl::OnChannelInitialized(
236 ChannelDispatcherBase* channel_dispatcher) {
237 NotifyIfChannelsReady();
240 void ConnectionToHostImpl::OnChannelError(
241 ChannelDispatcherBase* channel_dispatcher,
242 ErrorCode error) {
243 LOG(ERROR) << "Failed to connect channel " << channel_dispatcher;
244 CloseOnError(CHANNEL_CONNECTION_ERROR);
245 return;
248 void ConnectionToHostImpl::OnVideoChannelStatus(bool active) {
249 event_callback_->OnConnectionReady(active);
252 ConnectionToHost::State ConnectionToHostImpl::state() const {
253 return state_;
256 void ConnectionToHostImpl::NotifyIfChannelsReady() {
257 if (!control_dispatcher_.get() || !control_dispatcher_->is_connected())
258 return;
259 if (!event_dispatcher_.get() || !event_dispatcher_->is_connected())
260 return;
261 if (!video_dispatcher_.get() || !video_dispatcher_->is_connected())
262 return;
263 if ((!audio_reader_.get() || !audio_reader_->is_connected()) &&
264 session_->config().is_audio_enabled()) {
265 return;
267 if (state_ != AUTHENTICATED)
268 return;
270 // Start forwarding clipboard and input events.
271 clipboard_forwarder_.set_clipboard_stub(control_dispatcher_.get());
272 event_forwarder_.set_input_stub(event_dispatcher_.get());
273 SetState(CONNECTED, OK);
276 void ConnectionToHostImpl::CloseOnError(ErrorCode error) {
277 CloseChannels();
278 SetState(FAILED, error);
281 void ConnectionToHostImpl::CloseChannels() {
282 control_dispatcher_.reset();
283 event_dispatcher_.reset();
284 clipboard_forwarder_.set_clipboard_stub(nullptr);
285 event_forwarder_.set_input_stub(nullptr);
286 video_dispatcher_.reset();
287 audio_reader_.reset();
290 void ConnectionToHostImpl::SetState(State state, ErrorCode error) {
291 DCHECK(CalledOnValidThread());
292 // |error| should be specified only when |state| is set to FAILED.
293 DCHECK(state == FAILED || error == OK);
295 if (state != state_) {
296 state_ = state;
297 error_ = error;
298 event_callback_->OnConnectionState(state_, error_);
302 } // namespace protocol
303 } // namespace remoting