1// Copyright 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_client.h"
6
7#include "base/bind.h"
8#include "base/location.h"
9#include "base/message_loop/message_loop_proxy.h"
10#include "net/base/io_buffer.h"
11#include "remoting/protocol/clipboard_stub.h"
12#include "remoting/protocol/host_control_dispatcher.h"
13#include "remoting/protocol/host_event_dispatcher.h"
14#include "remoting/protocol/host_stub.h"
15#include "remoting/protocol/host_video_dispatcher.h"
16#include "remoting/protocol/input_stub.h"
17
18namespace remoting {
19namespace protocol {
20
21ConnectionToClient::ConnectionToClient(protocol::Session* session)
22    : handler_(NULL),
23      clipboard_stub_(NULL),
24      host_stub_(NULL),
25      input_stub_(NULL),
26      session_(session) {
27  session_->SetEventHandler(this);
28}
29
30ConnectionToClient::~ConnectionToClient() {
31}
32
33void ConnectionToClient::SetEventHandler(EventHandler* event_handler) {
34  DCHECK(CalledOnValidThread());
35  handler_ = event_handler;
36}
37
38protocol::Session* ConnectionToClient::session() {
39  DCHECK(CalledOnValidThread());
40  return session_.get();
41}
42
43void ConnectionToClient::Disconnect() {
44  DCHECK(CalledOnValidThread());
45
46  CloseChannels();
47
48  // This should trigger OnConnectionClosed() event and this object
49  // may be destroyed as the result.
50  session_->Close();
51}
52
53void ConnectionToClient::UpdateSequenceNumber(int64 sequence_number) {
54  DCHECK(CalledOnValidThread());
55  handler_->OnSequenceNumberUpdated(this, sequence_number);
56}
57
58VideoStub* ConnectionToClient::video_stub() {
59  DCHECK(CalledOnValidThread());
60  return video_dispatcher_.get();
61}
62
63AudioStub* ConnectionToClient::audio_stub() {
64  DCHECK(CalledOnValidThread());
65  return audio_writer_.get();
66}
67
68// Return pointer to ClientStub.
69ClientStub* ConnectionToClient::client_stub() {
70  DCHECK(CalledOnValidThread());
71  return control_dispatcher_.get();
72}
73
74void ConnectionToClient::set_clipboard_stub(
75    protocol::ClipboardStub* clipboard_stub) {
76  DCHECK(CalledOnValidThread());
77  clipboard_stub_ = clipboard_stub;
78}
79
80ClipboardStub* ConnectionToClient::clipboard_stub() {
81  DCHECK(CalledOnValidThread());
82  return clipboard_stub_;
83}
84
85void ConnectionToClient::set_host_stub(protocol::HostStub* host_stub) {
86  DCHECK(CalledOnValidThread());
87  host_stub_ = host_stub;
88}
89
90HostStub* ConnectionToClient::host_stub() {
91  DCHECK(CalledOnValidThread());
92  return host_stub_;
93}
94
95void ConnectionToClient::set_input_stub(protocol::InputStub* input_stub) {
96  DCHECK(CalledOnValidThread());
97  input_stub_ = input_stub;
98}
99
100InputStub* ConnectionToClient::input_stub() {
101  DCHECK(CalledOnValidThread());
102  return input_stub_;
103}
104
105void ConnectionToClient::OnSessionStateChange(Session::State state) {
106  DCHECK(CalledOnValidThread());
107
108  DCHECK(handler_);
109  switch(state) {
110    case Session::INITIALIZING:
111    case Session::CONNECTING:
112    case Session::ACCEPTING:
113    case Session::CONNECTED:
114      // Don't care about these events.
115      break;
116    case Session::AUTHENTICATING:
117      handler_->OnConnectionAuthenticating(this);
118      break;
119    case Session::AUTHENTICATED:
120      // Initialize channels.
121      control_dispatcher_.reset(new HostControlDispatcher());
122      control_dispatcher_->Init(
123          session_.get(), session_->config().control_config(),
124          base::Bind(&ConnectionToClient::OnChannelInitialized,
125                     base::Unretained(this)));
126      control_dispatcher_->set_clipboard_stub(clipboard_stub_);
127      control_dispatcher_->set_host_stub(host_stub_);
128
129      event_dispatcher_.reset(new HostEventDispatcher());
130      event_dispatcher_->Init(
131          session_.get(), session_->config().event_config(),
132          base::Bind(&ConnectionToClient::OnChannelInitialized,
133                     base::Unretained(this)));
134      event_dispatcher_->set_input_stub(input_stub_);
135      event_dispatcher_->set_sequence_number_callback(base::Bind(
136          &ConnectionToClient::UpdateSequenceNumber, base::Unretained(this)));
137
138      video_dispatcher_.reset(new HostVideoDispatcher());
139      video_dispatcher_->Init(
140          session_.get(), session_->config().video_config(),
141          base::Bind(&ConnectionToClient::OnChannelInitialized,
142                     base::Unretained(this)));
143
144      audio_writer_ = AudioWriter::Create(session_->config());
145      if (audio_writer_.get()) {
146        audio_writer_->Init(
147            session_.get(), session_->config().audio_config(),
148            base::Bind(&ConnectionToClient::OnChannelInitialized,
149                       base::Unretained(this)));
150      }
151
152      // Notify the handler after initializing the channels, so that
153      // ClientSession can get a client clipboard stub.
154      handler_->OnConnectionAuthenticated(this);
155      break;
156
157    case Session::CLOSED:
158      Close(OK);
159      break;
160
161    case Session::FAILED:
162      Close(session_->error());
163      break;
164  }
165}
166
167void ConnectionToClient::OnSessionRouteChange(
168    const std::string& channel_name,
169    const TransportRoute& route) {
170  handler_->OnRouteChange(this, channel_name, route);
171}
172
173void ConnectionToClient::OnChannelInitialized(bool successful) {
174  DCHECK(CalledOnValidThread());
175
176  if (!successful) {
177    LOG(ERROR) << "Failed to connect a channel";
178    Close(CHANNEL_CONNECTION_ERROR);
179    return;
180  }
181
182  NotifyIfChannelsReady();
183}
184
185void ConnectionToClient::NotifyIfChannelsReady() {
186  DCHECK(CalledOnValidThread());
187
188  if (!control_dispatcher_.get() || !control_dispatcher_->is_connected())
189    return;
190  if (!event_dispatcher_.get() || !event_dispatcher_->is_connected())
191    return;
192  if (!video_dispatcher_.get() || !video_dispatcher_->is_connected())
193    return;
194  if ((!audio_writer_.get() || !audio_writer_->is_connected()) &&
195      session_->config().is_audio_enabled()) {
196    return;
197  }
198  handler_->OnConnectionChannelsConnected(this);
199}
200
201void ConnectionToClient::Close(ErrorCode error) {
202  CloseChannels();
203  handler_->OnConnectionClosed(this, error);
204}
205
206void ConnectionToClient::CloseChannels() {
207  control_dispatcher_.reset();
208  event_dispatcher_.reset();
209  video_dispatcher_.reset();
210  audio_writer_.reset();
211}
212
213}  // namespace protocol
214}  // namespace remoting
215