| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 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/client/ios/bridge/client_instance.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/bind_helpers.h" | |
| 9 #include "base/logging.h" | |
| 10 #include "base/memory/ptr_util.h" | |
| 11 #include "base/synchronization/waitable_event.h" | |
| 12 #include "jingle/glue/thread_wrapper.h" | |
| 13 #include "net/socket/client_socket_factory.h" | |
| 14 #include "remoting/base/chromium_url_request.h" | |
| 15 #include "remoting/base/url_request_context_getter.h" | |
| 16 #include "remoting/client/audio_player.h" | |
| 17 //#include "remoting/client/client_status_logger.h" | |
| 18 #include "remoting/client/ios/bridge/client_proxy.h" | |
| 19 #include "remoting/proto/event.pb.h" | |
| 20 #include "remoting/protocol/chromium_port_allocator_factory.h" | |
| 21 #include "remoting/protocol/client_authentication_config.h" | |
| 22 #include "remoting/protocol/host_stub.h" | |
| 23 #include "remoting/protocol/negotiating_client_authenticator.h" | |
| 24 #include "remoting/protocol/transport_context.h" | |
| 25 #include "remoting/signaling/delegating_signal_strategy.h" | |
| 26 | |
| 27 | |
| 28 //#include "remoting/client/ios/audio_player_buffer.h" // TODO(nicholss): need | |
| 29 // to also pull in the player and attach to buffer. | |
| 30 | |
| 31 // TODO(nicholss): There is another client instance used by android. Need to | |
| 32 // merge the two. | |
| 33 | |
| 34 namespace { | |
| 35 const char* const kXmppServer = "talk.google.com"; | |
| 36 // const char kDirectoryBotJid[] = "remoting@bot.talk.google.com"; | |
| 37 } // namespace | |
| 38 | |
| 39 namespace remoting { | |
| 40 | |
| 41 ClientInstance::ClientInstance(const base::WeakPtr<ClientProxy>& proxy, | |
| 42 const std::string& username, | |
| 43 const std::string& auth_token, | |
| 44 const std::string& host_jid, | |
| 45 const std::string& host_id, | |
| 46 const std::string& host_pubkey) | |
| 47 : proxyToClient_(proxy), host_jid_(host_jid), device_id_() { | |
| 48 if (!base::MessageLoop::current()) { | |
| 49 ui_loop_ = new base::MessageLoop(base::MessageLoop::TYPE_UI); | |
| 50 base::MessageLoopForUI::current()->Attach(); | |
| 51 } else { | |
| 52 ui_loop_ = base::MessageLoopForUI::current(); | |
| 53 } | |
| 54 | |
| 55 // |ui_loop_| runs on the main thread, so |ui_task_runner_| will run on the | |
| 56 // main thread. We can not kill the main thread when the message loop becomes | |
| 57 // idle so the callback function does nothing (as opposed to the typical | |
| 58 // base::MessageLoop::QuitClosure()) | |
| 59 ui_task_runner_ = new AutoThreadTaskRunner(ui_loop_->task_runner(), | |
| 60 base::Bind(&base::DoNothing)); | |
| 61 | |
| 62 network_task_runner_ = AutoThread::CreateWithType( | |
| 63 "native_net", ui_task_runner_, base::MessageLoop::TYPE_IO); | |
| 64 file_task_runner_ = AutoThread::CreateWithType("native_file", ui_task_runner_, | |
| 65 base::MessageLoop::TYPE_IO); | |
| 66 audio_task_runner_ = AutoThread::CreateWithType( | |
| 67 "native_audio", ui_task_runner_, base::MessageLoop::TYPE_IO); | |
| 68 | |
| 69 url_requester_ = | |
| 70 new URLRequestContextGetter(network_task_runner_, file_task_runner_); | |
| 71 | |
| 72 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | |
| 73 | |
| 74 // Initialize XMPP config. | |
| 75 xmpp_config_.host = kXmppServer; | |
| 76 xmpp_config_.username = username; | |
| 77 xmpp_config_.auth_token = auth_token; | |
| 78 | |
| 79 client_auth_config_.host_id = host_id; | |
| 80 client_auth_config_.fetch_secret_callback = | |
| 81 base::Bind(&ClientInstance::FetchSecret, this); | |
| 82 // client_auth_config_.fetch_third_party_token_callback = | |
| 83 // std::unique_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>(); | |
| 84 } | |
| 85 | |
| 86 ClientInstance::~ClientInstance() {} | |
| 87 | |
| 88 void ClientInstance::Start(const std::string& pairing_id, | |
| 89 const std::string& pairing_secret) { | |
| 90 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | |
| 91 | |
| 92 client_auth_config_.pairing_client_id = pairing_id; | |
| 93 client_auth_config_.pairing_secret = pairing_secret; | |
| 94 | |
| 95 view_.reset(new FrameConsumerBridge( | |
| 96 this, base::Bind(&ClientProxy::RedrawCanvas, proxyToClient_))); | |
| 97 | |
| 98 network_task_runner_->PostTask( | |
| 99 FROM_HERE, | |
| 100 base::Bind(&ClientInstance::ConnectToHostOnNetworkThread, this)); | |
| 101 } | |
| 102 | |
| 103 void ClientInstance::Cleanup() { | |
| 104 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | |
| 105 | |
| 106 // |view_| must be destroyed on the UI thread before the producer is gone. | |
| 107 view_.reset(); | |
| 108 | |
| 109 network_task_runner_->PostTask( | |
| 110 FROM_HERE, | |
| 111 base::Bind(&ClientInstance::DisconnectFromHostOnNetworkThread, this)); | |
| 112 } | |
| 113 | |
| 114 // HOST attempts to continue automatically with previously supplied credentials, | |
| 115 // if it can't it requests the user's PIN. | |
| 116 void ClientInstance::FetchSecret( | |
| 117 bool pairable, | |
| 118 const protocol::SecretFetchedCallback& callback) { | |
| 119 if (!ui_task_runner_->BelongsToCurrentThread()) { | |
| 120 ui_task_runner_->PostTask( | |
| 121 FROM_HERE, | |
| 122 base::Bind(&ClientInstance::FetchSecret, this, pairable, callback)); | |
| 123 return; | |
| 124 } | |
| 125 | |
| 126 pin_callback_ = callback; | |
| 127 | |
| 128 if (proxyToClient_) { | |
| 129 // We attempted to connect using an existing pairing that was rejected. | |
| 130 // Unless we forget about the stale credentials, we'll continue trying | |
| 131 // them. | |
| 132 proxyToClient_->CommitPairingCredentials(client_auth_config_.host_id, "", | |
| 133 ""); | |
| 134 | |
| 135 proxyToClient_->DisplayAuthenticationPrompt(pairable); | |
| 136 } | |
| 137 } | |
| 138 | |
| 139 void ClientInstance::ProvideSecret(const std::string& pin, | |
| 140 bool create_pairing, | |
| 141 const std::string& device_id) { | |
| 142 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | |
| 143 create_pairing_ = create_pairing; | |
| 144 device_id_ = device_id; | |
| 145 | |
| 146 // Before this function can be called, FetchSecret must have been called, | |
| 147 // which creates |pin_callback_| | |
| 148 DCHECK(!pin_callback_.is_null()); | |
| 149 network_task_runner_->PostTask(FROM_HERE, base::Bind(pin_callback_, pin)); | |
| 150 } | |
| 151 | |
| 152 void ClientInstance::PerformMouseAction( | |
| 153 const webrtc::DesktopVector& position, | |
| 154 const webrtc::DesktopVector& wheel_delta, | |
| 155 protocol::MouseEvent_MouseButton button, | |
| 156 bool button_down) { | |
| 157 if (!network_task_runner_->BelongsToCurrentThread()) { | |
| 158 network_task_runner_->PostTask( | |
| 159 FROM_HERE, base::Bind(&ClientInstance::PerformMouseAction, this, | |
| 160 position, wheel_delta, button, button_down)); | |
| 161 return; | |
| 162 } | |
| 163 | |
| 164 protocol::MouseEvent action; | |
| 165 action.set_x(position.x()); | |
| 166 action.set_y(position.y()); | |
| 167 action.set_wheel_delta_x(wheel_delta.x()); | |
| 168 action.set_wheel_delta_y(wheel_delta.y()); | |
| 169 action.set_button(button); | |
| 170 | |
| 171 // TODO(nicholss) this throws a npe when there is a delay on connecting to a | |
| 172 // host + touch. | |
| 173 client_->input_stub()->InjectMouseEvent(action); | |
| 174 } | |
| 175 | |
| 176 void ClientInstance::PerformKeyboardAction(int key_code, bool key_down) { | |
| 177 if (!network_task_runner_->BelongsToCurrentThread()) { | |
| 178 network_task_runner_->PostTask( | |
| 179 FROM_HERE, base::Bind(&ClientInstance::PerformKeyboardAction, this, | |
| 180 key_code, key_down)); | |
| 181 return; | |
| 182 } | |
| 183 | |
| 184 protocol::KeyEvent action; | |
| 185 action.set_usb_keycode(key_code); | |
| 186 action.set_pressed(key_down); | |
| 187 client_->input_stub()->InjectKeyEvent(action); | |
| 188 } | |
| 189 | |
| 190 void ClientInstance::OnConnectionState(protocol::ConnectionToHost::State state, | |
| 191 protocol::ErrorCode error) { | |
| 192 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 193 | |
| 194 // client_status_logger_->LogSessionStateChange(state, error); | |
| 195 | |
| 196 ui_task_runner_->PostTask( | |
| 197 FROM_HERE, base::Bind(&ClientInstance::HandleConnectionStateOnUIThread, | |
| 198 this, state, error)); | |
| 199 } | |
| 200 | |
| 201 void ClientInstance::OnConnectionReady(bool ready) { | |
| 202 // We ignore this message, since OnConnectionState tells us the same thing. | |
| 203 } | |
| 204 | |
| 205 void ClientInstance::OnRouteChanged(const std::string& channel_name, | |
| 206 const protocol::TransportRoute& route) { | |
| 207 VLOG(1) << "Using " << protocol::TransportRoute::GetTypeString(route.type) | |
| 208 << " connection for " << channel_name << " channel"; | |
| 209 } | |
| 210 | |
| 211 void ClientInstance::SetCapabilities(const std::string& capabilities) {} | |
| 212 | |
| 213 void ClientInstance::SetPairingResponse( | |
| 214 const protocol::PairingResponse& response) { | |
| 215 if (!ui_task_runner_->BelongsToCurrentThread()) { | |
| 216 ui_task_runner_->PostTask( | |
| 217 FROM_HERE, | |
| 218 base::Bind(&ClientInstance::SetPairingResponse, this, response)); | |
| 219 return; | |
| 220 } | |
| 221 | |
| 222 VLOG(1) << "Successfully established pairing with host"; | |
| 223 | |
| 224 if (proxyToClient_) | |
| 225 proxyToClient_->CommitPairingCredentials(client_auth_config_.host_id, | |
| 226 response.client_id(), | |
| 227 response.shared_secret()); | |
| 228 } | |
| 229 | |
| 230 void ClientInstance::DeliverHostMessage( | |
| 231 const protocol::ExtensionMessage& message) { | |
| 232 NOTIMPLEMENTED(); | |
| 233 } | |
| 234 | |
| 235 void ClientInstance::SetDesktopSize(const webrtc::DesktopSize& size, | |
| 236 const webrtc::DesktopVector& dpi) { | |
| 237 // ClientInstance get size from the frames and it doesn't use DPI, so this | |
| 238 // call can be ignored. | |
| 239 } | |
| 240 | |
| 241 // Returning interface of protocol::ClipboardStub. | |
| 242 protocol::ClipboardStub* ClientInstance::GetClipboardStub() { | |
| 243 return this; | |
| 244 } | |
| 245 | |
| 246 // Returning interface of protocol::CursorShapeStub. | |
| 247 protocol::CursorShapeStub* ClientInstance::GetCursorShapeStub() { | |
| 248 return this; | |
| 249 } | |
| 250 | |
| 251 void ClientInstance::InjectClipboardEvent( | |
| 252 const protocol::ClipboardEvent& event) { | |
| 253 NOTIMPLEMENTED(); | |
| 254 } | |
| 255 | |
| 256 void ClientInstance::SetCursorShape(const protocol::CursorShapeInfo& shape) { | |
| 257 if (!ui_task_runner_->BelongsToCurrentThread()) { | |
| 258 ui_task_runner_->PostTask( | |
| 259 FROM_HERE, base::Bind(&ClientInstance::SetCursorShape, this, shape)); | |
| 260 return; | |
| 261 } | |
| 262 if (proxyToClient_) | |
| 263 proxyToClient_->UpdateCursorShape(shape); | |
| 264 } | |
| 265 | |
| 266 void ClientInstance::ConnectToHostOnNetworkThread() { | |
| 267 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 268 | |
| 269 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop(); | |
| 270 | |
| 271 client_context_.reset(new ClientContext(network_task_runner_)); | |
| 272 client_context_->Start(); | |
| 273 | |
| 274 perf_tracker_.reset(new protocol::PerformanceTracker()); | |
| 275 | |
| 276 video_renderer_.reset(new SoftwareVideoRenderer(view_.get())); | |
| 277 // TODO(nicholss): SoftwareVideoRenderer was changed to now have Initialize, | |
| 278 // but it is unclear how to integrate into existing code. | |
| 279 // video_renderer_->Initialize(client_context_, perf_tracker_.get())); | |
| 280 | |
| 281 client_.reset(new ChromotingClient( | |
| 282 client_context_.get(), this, video_renderer_.get(), | |
| 283 nullptr // TODO(nicholss): GetAudioConsumer().get() | |
| 284 )); | |
| 285 | |
| 286 signaling_.reset( | |
| 287 new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(), | |
| 288 url_requester_, xmpp_config_)); | |
| 289 | |
| 290 // client_status_logger_.reset(new ClientStatusLogger( | |
| 291 // ServerLogEntry::ME2ME, signaling_.get(), kDirectoryBotJid)); | |
| 292 | |
| 293 protocol::NetworkSettings network_settings( | |
| 294 protocol::NetworkSettings::NAT_TRAVERSAL_FULL); | |
| 295 | |
| 296 // Use Chrome's network stack to allocate ports for peer-to-peer channels. | |
| 297 | |
| 298 scoped_refptr<protocol::TransportContext> transport_context = | |
| 299 new protocol::TransportContext( | |
| 300 signaling_.get(), | |
| 301 base::WrapUnique(new protocol::ChromiumPortAllocatorFactory()), | |
| 302 base::WrapUnique(new ChromiumUrlRequestFactory(url_requester_)), | |
| 303 protocol::NetworkSettings( | |
| 304 protocol::NetworkSettings::NAT_TRAVERSAL_FULL), | |
| 305 protocol::TransportRole::CLIENT); | |
| 306 | |
| 307 client_->Start(signaling_.get(), client_auth_config_, transport_context, | |
| 308 host_jid_, std::string()); | |
| 309 } | |
| 310 | |
| 311 // TODO(nicholss): Port audio impl for iOS. | |
| 312 // base::WeakPtr<protocol::AudioStub> ClientInstance::GetAudioConsumer() { | |
| 313 // if (!audio_player_) { | |
| 314 // audio_player_ = AudioPlayerIos::CreateAudioPlayer( | |
| 315 // network_task_runner_, | |
| 316 // audio_task_runner_); | |
| 317 // } | |
| 318 // | |
| 319 // return audio_player_->GetAudioConsumer(); | |
| 320 // } | |
| 321 | |
| 322 void ClientInstance::DisconnectFromHostOnNetworkThread() { | |
| 323 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 324 | |
| 325 // |client_| must be torn down before |signaling_|. | |
| 326 client_.reset(); | |
| 327 // client_status_logger_.reset(); | |
| 328 signaling_.reset(); | |
| 329 perf_tracker_.reset(); | |
| 330 // audio_consumer_->reset(); // TODO(nicholss): Or should this be a call to | |
| 331 // Stop? | |
| 332 // audio_player_->reset(); // TODO(nicholss): Or should this be a call to | |
| 333 // Stop? | |
| 334 video_renderer_.reset(); | |
| 335 client_context_->Stop(); | |
| 336 } | |
| 337 | |
| 338 void ClientInstance::HandleConnectionStateOnUIThread( | |
| 339 protocol::ConnectionToHost::State state, | |
| 340 protocol::ErrorCode error) { | |
| 341 if (create_pairing_ && device_id_.length() > 0 && | |
| 342 state == protocol::ConnectionToHost::CONNECTED) { | |
| 343 DoPairing(); | |
| 344 } | |
| 345 | |
| 346 if (proxyToClient_) | |
| 347 proxyToClient_->ReportConnectionStatus(state, error); | |
| 348 } | |
| 349 | |
| 350 void ClientInstance::DoPairing() { | |
| 351 if (!network_task_runner_->BelongsToCurrentThread()) { | |
| 352 network_task_runner_->PostTask( | |
| 353 FROM_HERE, base::Bind(&ClientInstance::DoPairing, this)); | |
| 354 return; | |
| 355 } | |
| 356 | |
| 357 VLOG(1) << "Attempting to pair with host"; | |
| 358 protocol::PairingRequest request; | |
| 359 request.set_client_name(device_id_); | |
| 360 client_->host_stub()->RequestPairing(request); | |
| 361 } | |
| 362 | |
| 363 } // namespace remoting | |
| OLD | NEW |