1 // Copyright 2014 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/ios/bridge/client_instance.h"
8 #include "base/logging.h"
9 #include "base/synchronization/waitable_event.h"
10 #include "net/socket/client_socket_factory.h"
11 #include "remoting/base/url_request_context_getter.h"
12 #include "remoting/client/audio_player.h"
13 #include "remoting/client/plugin/delegating_signal_strategy.h"
14 #include "remoting/ios/bridge/client_proxy.h"
15 #include "remoting/protocol/chromium_port_allocator.h"
16 #include "remoting/protocol/host_stub.h"
17 #include "remoting/protocol/libjingle_transport_factory.h"
18 #include "remoting/protocol/negotiating_client_authenticator.h"
21 const char* const kXmppServer
= "talk.google.com";
22 const int kXmppPort
= 5222;
23 const bool kXmppUseTls
= true;
30 ClientInstance::ClientInstance(const base::WeakPtr
<ClientProxy
>& proxy
,
31 const std::string
& username
,
32 const std::string
& auth_token
,
33 const std::string
& host_jid
,
34 const std::string
& host_id
,
35 const std::string
& host_pubkey
,
36 const std::string
& pairing_id
,
37 const std::string
& pairing_secret
)
38 : proxyToClient_(proxy
),
41 create_pairing_(false) {
42 if (!base::MessageLoop::current()) {
43 VLOG(1) << "Starting main message loop";
44 ui_loop_
= new base::MessageLoopForUI();
47 VLOG(1) << "Using existing main message loop";
48 ui_loop_
= base::MessageLoopForUI::current();
51 VLOG(1) << "Spawning additional threads";
53 // |ui_loop_| runs on the main thread, so |ui_task_runner_| will run on the
54 // main thread. We can not kill the main thread when the message loop becomes
55 // idle so the callback function does nothing (as opposed to the typical
56 // base::MessageLoop::QuitClosure())
57 ui_task_runner_
= new AutoThreadTaskRunner(ui_loop_
->message_loop_proxy(),
58 base::Bind(&::DoNothing
));
60 network_task_runner_
= AutoThread::CreateWithType(
61 "native_net", ui_task_runner_
, base::MessageLoop::TYPE_IO
);
63 url_requester_
= new URLRequestContextGetter(network_task_runner_
);
65 client_context_
.reset(new ClientContext(network_task_runner_
));
67 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
69 // Initialize XMPP config.
70 xmpp_config_
.host
= kXmppServer
;
71 xmpp_config_
.port
= kXmppPort
;
72 xmpp_config_
.use_tls
= kXmppUseTls
;
73 xmpp_config_
.username
= username
;
74 xmpp_config_
.auth_token
= auth_token
;
75 xmpp_config_
.auth_service
= "oauth2";
77 // Initialize |authenticator_|.
78 scoped_ptr
<protocol::ThirdPartyClientAuthenticator::TokenFetcher
>
79 token_fetcher(new TokenFetcherProxy(
80 base::Bind(&ChromotingJniInstance::FetchThirdPartyToken
,
81 weak_factory_
.GetWeakPtr()),
84 std::vector
<protocol::AuthenticationMethod
> auth_methods
;
85 auth_methods
.push_back(protocol::AuthenticationMethod::Spake2Pair());
86 auth_methods
.push_back(protocol::AuthenticationMethod::Spake2(
87 protocol::AuthenticationMethod::HMAC_SHA256
));
88 auth_methods
.push_back(protocol::AuthenticationMethod::Spake2(
89 protocol::AuthenticationMethod::NONE
));
91 authenticator_
.reset(new protocol::NegotiatingClientAuthenticator(
92 pairing_id
, pairing_secret
, host_id_
,
93 base::Bind(&ClientInstance::FetchSecret
, this),
94 token_fetcher
.Pass(), auth_methods
));
97 ClientInstance::~ClientInstance() {}
99 void ClientInstance::Start() {
100 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
102 view_
.reset(new FrameConsumerBridge(
103 base::Bind(&ClientProxy::RedrawCanvas
, proxyToClient_
)));
105 // |consumer_proxy| must be created on the UI thread to proxy calls from the
106 // network or decode thread to the UI thread, but ownership will belong to a
107 // SoftwareVideoRenderer which runs on the network thread.
108 scoped_refptr
<FrameConsumerProxy
> consumer_proxy
=
109 new FrameConsumerProxy(ui_task_runner_
, view_
->AsWeakPtr());
111 // Post a task to start connection
112 base::WaitableEvent
done_event(true, false);
113 network_task_runner_
->PostTask(
115 base::Bind(&ClientInstance::ConnectToHostOnNetworkThread
,
118 base::Bind(&base::WaitableEvent::Signal
,
119 base::Unretained(&done_event
))));
120 // Wait until initialization completes before continuing
124 void ClientInstance::Cleanup() {
125 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
127 // |view_| must be destroyed on the UI thread before the producer is gone.
130 base::WaitableEvent
done_event(true, false);
131 network_task_runner_
->PostTask(
133 base::Bind(&ClientInstance::DisconnectFromHostOnNetworkThread
,
135 base::Bind(&base::WaitableEvent::Signal
,
136 base::Unretained(&done_event
))));
137 // Wait until we are fully disconnected before continuing
141 // HOST attempts to continue automatically with previously supplied credentials,
142 // if it can't it requests the user's PIN.
143 void ClientInstance::FetchSecret(
145 const protocol::SecretFetchedCallback
& callback
) {
146 if (!ui_task_runner_
->BelongsToCurrentThread()) {
147 ui_task_runner_
->PostTask(
149 base::Bind(&ClientInstance::FetchSecret
, this, pairable
, callback
));
153 pin_callback_
= callback
;
155 if (proxyToClient_
) {
156 // Delete pairing credentials if they exist.
157 proxyToClient_
->CommitPairingCredentials(host_id_
, "", "");
159 proxyToClient_
->DisplayAuthenticationPrompt(pairable
);
163 void ClientInstance::ProvideSecret(const std::string
& pin
,
164 bool create_pairing
) {
165 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
166 create_pairing_
= create_pairing
;
168 // Before this function can complete, FetchSecret must be called
169 DCHECK(!pin_callback_
.is_null());
170 network_task_runner_
->PostTask(FROM_HERE
, base::Bind(pin_callback_
, pin
));
173 void ClientInstance::PerformMouseAction(
174 const webrtc::DesktopVector
& position
,
175 const webrtc::DesktopVector
& wheel_delta
,
176 int /* protocol::MouseEvent_MouseButton */ whichButton
,
178 if (!network_task_runner_
->BelongsToCurrentThread()) {
179 network_task_runner_
->PostTask(
181 base::Bind(&ClientInstance::PerformMouseAction
,
190 protocol::MouseEvent_MouseButton mButton
;
192 // Button must be within the bounds of the MouseEvent_MouseButton enum.
193 switch (whichButton
) {
194 case protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_LEFT
:
196 protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_LEFT
;
198 case protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_MAX
:
200 protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_MAX
;
202 case protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_MIDDLE
:
203 mButton
= protocol::MouseEvent_MouseButton::
204 MouseEvent_MouseButton_BUTTON_MIDDLE
;
206 case protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_RIGHT
:
208 protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_RIGHT
;
210 case protocol::MouseEvent_MouseButton::
211 MouseEvent_MouseButton_BUTTON_UNDEFINED
:
212 mButton
= protocol::MouseEvent_MouseButton::
213 MouseEvent_MouseButton_BUTTON_UNDEFINED
;
216 LOG(FATAL
) << "Invalid constant for MouseEvent_MouseButton";
217 mButton
= protocol::MouseEvent_MouseButton::
218 MouseEvent_MouseButton_BUTTON_UNDEFINED
;
222 protocol::MouseEvent action
;
223 action
.set_x(position
.x());
224 action
.set_y(position
.y());
225 action
.set_wheel_delta_x(wheel_delta
.x());
226 action
.set_wheel_delta_y(wheel_delta
.y());
227 action
.set_button(mButton
);
228 if (mButton
!= protocol::MouseEvent::BUTTON_UNDEFINED
)
229 action
.set_button_down(button_down
);
231 client_
->input_stub()->InjectMouseEvent(action
);
234 void ClientInstance::PerformKeyboardAction(int key_code
, bool key_down
) {
235 if (!network_task_runner_
->BelongsToCurrentThread()) {
236 network_task_runner_
->PostTask(
239 &ClientInstance::PerformKeyboardAction
, this, key_code
, key_down
));
243 protocol::KeyEvent action
;
244 action
.set_usb_keycode(key_code
);
245 action
.set_pressed(key_down
);
246 client_
->input_stub()->InjectKeyEvent(action
);
249 void ClientInstance::OnConnectionState(protocol::ConnectionToHost::State state
,
250 protocol::ErrorCode error
) {
251 if (!ui_task_runner_
->BelongsToCurrentThread()) {
252 ui_task_runner_
->PostTask(
254 base::Bind(&ClientInstance::OnConnectionState
, this, state
, error
));
258 // TODO (aboone) This functionality is not scheduled for QA yet.
259 // if (create_pairing_ && state == protocol::ConnectionToHost::CONNECTED) {
260 // VLOG(1) << "Attempting to pair with host";
261 // protocol::PairingRequest request;
262 // request.set_client_name("iOS");
263 // client_->host_stub()->RequestPairing(request);
267 proxyToClient_
->ReportConnectionStatus(state
, error
);
270 void ClientInstance::OnConnectionReady(bool ready
) {
271 // We ignore this message, since OnConnectionState tells us the same thing.
274 void ClientInstance::OnRouteChanged(const std::string
& channel_name
,
275 const protocol::TransportRoute
& route
) {
276 VLOG(1) << "Using " << protocol::TransportRoute::GetTypeString(route
.type
)
277 << " connection for " << channel_name
<< " channel";
280 void ClientInstance::SetCapabilities(const std::string
& capabilities
) {
283 void ClientInstance::SetPairingResponse(
284 const protocol::PairingResponse
& response
) {
285 if (!ui_task_runner_
->BelongsToCurrentThread()) {
286 ui_task_runner_
->PostTask(
288 base::Bind(&ClientInstance::SetPairingResponse
, this, response
));
292 VLOG(1) << "Successfully established pairing with host";
295 proxyToClient_
->CommitPairingCredentials(
296 host_id_
, response
.client_id(), response
.shared_secret());
299 void ClientInstance::DeliverHostMessage(
300 const protocol::ExtensionMessage
& message
) {
304 // Returning interface of protocol::ClipboardStub
305 protocol::ClipboardStub
* ClientInstance::GetClipboardStub() { return this; }
307 // Returning interface of protocol::CursorShapeStub
308 protocol::CursorShapeStub
* ClientInstance::GetCursorShapeStub() { return this; }
310 void ClientInstance::InjectClipboardEvent(
311 const protocol::ClipboardEvent
& event
) {
315 void ClientInstance::SetCursorShape(const protocol::CursorShapeInfo
& shape
) {
316 if (!ui_task_runner_
->BelongsToCurrentThread()) {
317 ui_task_runner_
->PostTask(
318 FROM_HERE
, base::Bind(&ClientInstance::SetCursorShape
, this, shape
));
322 proxyToClient_
->UpdateCursorShape(shape
);
325 void ClientInstance::ConnectToHostOnNetworkThread(
326 scoped_refptr
<FrameConsumerProxy
> consumer_proxy
,
327 const base::Closure
& done
) {
328 DCHECK(network_task_runner_
->BelongsToCurrentThread());
330 client_context_
->Start();
332 video_renderer_
.reset(
333 new SoftwareVideoRenderer(client_context_
->main_task_runner(),
334 client_context_
->decode_task_runner(),
337 view_
->Initialize(video_renderer_
.get());
339 client_
.reset(new ChromotingClient(client_context_
.get(),
341 video_renderer_
.get(),
342 scoped_ptr
<AudioPlayer
>()));
345 new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(),
349 protocol::NetworkSettings
network_settings(
350 protocol::NetworkSettings::NAT_TRAVERSAL_ENABLED
);
352 scoped_ptr
<protocol::ChromiumPortAllocator
> port_allocator(
353 protocol::ChromiumPortAllocator::Create(url_requester_
,
356 scoped_ptr
<protocol::TransportFactory
> transport_factory(
357 new protocol::LibjingleTransportFactory(
359 port_allocator
.PassAs
<cricket::HttpPortAllocatorBase
>(),
362 client_
->Start(signaling_
.get(), authenticator_
.Pass(),
363 transport_factory
.Pass(), host_jid_
, std::string());
369 void ClientInstance::DisconnectFromHostOnNetworkThread(
370 const base::Closure
& done
) {
371 DCHECK(network_task_runner_
->BelongsToCurrentThread());
375 // |client_| must be torn down before |signaling_|.
378 video_renderer_
.reset();
379 client_context_
->Stop();
384 } // namespace remoting