1 // Copyright 2013 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/jni/chromoting_jni_instance.h" 6 7 #include "base/bind.h" 8 #include "base/logging.h" 9 #include "remoting/client/audio_player.h" 10 #include "remoting/client/jni/android_keymap.h" 11 #include "remoting/client/jni/chromoting_jni_runtime.h" 12 #include "remoting/protocol/host_stub.h" 13 #include "remoting/protocol/libjingle_transport_factory.h" 14 15 // TODO(solb) Move into location shared with client plugin. 16 const char* const kXmppServer = "talk.google.com"; 17 const int kXmppPort = 5222; 18 const bool kXmppUseTls = true; 19 20 namespace remoting { 21 22 ChromotingJniInstance::ChromotingJniInstance(ChromotingJniRuntime* jni_runtime, 23 const char* username, 24 const char* auth_token, 25 const char* host_jid, 26 const char* host_id, 27 const char* host_pubkey, 28 const char* pairing_id, 29 const char* pairing_secret) 30 : jni_runtime_(jni_runtime), 31 username_(username), 32 auth_token_(auth_token), 33 host_jid_(host_jid), 34 host_id_(host_id), 35 host_pubkey_(host_pubkey), 36 pairing_id_(pairing_id), 37 pairing_secret_(pairing_secret), 38 create_pairing_(false) { 39 DCHECK(jni_runtime_->ui_task_runner()->BelongsToCurrentThread()); 40 41 jni_runtime_->display_task_runner()->PostTask( 42 FROM_HERE, 43 base::Bind(&ChromotingJniInstance::ConnectToHostOnDisplayThread, 44 this)); 45 } 46 47 ChromotingJniInstance::~ChromotingJniInstance() {} 48 49 void ChromotingJniInstance::Cleanup() { 50 if (!jni_runtime_->display_task_runner()->BelongsToCurrentThread()) { 51 jni_runtime_->display_task_runner()->PostTask( 52 FROM_HERE, 53 base::Bind(&ChromotingJniInstance::Cleanup, this)); 54 return; 55 } 56 57 // This must be destroyed on the display thread before the producer is gone. 58 view_.reset(); 59 60 // The weak pointers must be invalidated on the same thread they were used. 61 view_weak_factory_->InvalidateWeakPtrs(); 62 63 jni_runtime_->network_task_runner()->PostTask( 64 FROM_HERE, 65 base::Bind(&ChromotingJniInstance::DisconnectFromHostOnNetworkThread, 66 this)); 67 } 68 69 void ChromotingJniInstance::ProvideSecret(const std::string& pin, 70 bool create_pairing) { 71 DCHECK(jni_runtime_->ui_task_runner()->BelongsToCurrentThread()); 72 DCHECK(!pin_callback_.is_null()); 73 74 create_pairing_ = create_pairing; 75 76 jni_runtime_->network_task_runner()->PostTask(FROM_HERE, 77 base::Bind(pin_callback_, pin)); 78 } 79 80 void ChromotingJniInstance::RedrawDesktop() { 81 if (!jni_runtime_->display_task_runner()->BelongsToCurrentThread()) { 82 jni_runtime_->display_task_runner()->PostTask( 83 FROM_HERE, 84 base::Bind(&ChromotingJniInstance::RedrawDesktop, this)); 85 return; 86 } 87 88 jni_runtime_->RedrawCanvas(); 89 } 90 91 void ChromotingJniInstance::PerformMouseAction( 92 int x, 93 int y, 94 protocol::MouseEvent_MouseButton button, 95 bool button_down) { 96 if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) { 97 jni_runtime_->network_task_runner()->PostTask( 98 FROM_HERE, 99 base::Bind(&ChromotingJniInstance::PerformMouseAction, 100 this, 101 x, 102 y, 103 button, 104 button_down)); 105 return; 106 } 107 108 protocol::MouseEvent action; 109 action.set_x(x); 110 action.set_y(y); 111 action.set_button(button); 112 if (button != protocol::MouseEvent::BUTTON_UNDEFINED) 113 action.set_button_down(button_down); 114 115 connection_->input_stub()->InjectMouseEvent(action); 116 } 117 118 void ChromotingJniInstance::PerformKeyboardAction(int key_code, bool key_down) { 119 if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) { 120 jni_runtime_->network_task_runner()->PostTask( 121 FROM_HERE, 122 base::Bind(&ChromotingJniInstance::PerformKeyboardAction, 123 this, 124 key_code, 125 key_down)); 126 return; 127 } 128 129 uint32 usb_code = AndroidKeycodeToUsbKeycode(key_code); 130 if (usb_code) { 131 protocol::KeyEvent action; 132 action.set_usb_keycode(usb_code); 133 action.set_pressed(key_down); 134 connection_->input_stub()->InjectKeyEvent(action); 135 } else { 136 LOG(WARNING) << "Ignoring unknown keycode: " << key_code; 137 } 138 } 139 140 void ChromotingJniInstance::OnConnectionState( 141 protocol::ConnectionToHost::State state, 142 protocol::ErrorCode error) { 143 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread()); 144 145 if (create_pairing_ && state == protocol::ConnectionToHost::CONNECTED) { 146 LOG(INFO) << "Attempting to pair with host"; 147 protocol::PairingRequest request; 148 request.set_client_name("Android"); 149 connection_->host_stub()->RequestPairing(request); 150 } 151 152 jni_runtime_->ui_task_runner()->PostTask( 153 FROM_HERE, 154 base::Bind(&ChromotingJniRuntime::ReportConnectionStatus, 155 base::Unretained(jni_runtime_), 156 state, 157 error)); 158 } 159 160 void ChromotingJniInstance::OnConnectionReady(bool ready) { 161 // We ignore this message, since OnConnectoinState tells us the same thing. 162 } 163 164 void ChromotingJniInstance::SetCapabilities(const std::string& capabilities) {} 165 166 void ChromotingJniInstance::SetPairingResponse( 167 const protocol::PairingResponse& response) { 168 LOG(INFO) << "Successfully established pairing with host"; 169 170 jni_runtime_->ui_task_runner()->PostTask( 171 FROM_HERE, 172 base::Bind(&ChromotingJniRuntime::CommitPairingCredentials, 173 base::Unretained(jni_runtime_), 174 host_id_, 175 response.client_id(), 176 response.shared_secret())); 177 } 178 179 void ChromotingJniInstance::DeliverHostMessage( 180 const protocol::ExtensionMessage& message) { 181 NOTIMPLEMENTED(); 182 } 183 184 protocol::ClipboardStub* ChromotingJniInstance::GetClipboardStub() { 185 return this; 186 } 187 188 protocol::CursorShapeStub* ChromotingJniInstance::GetCursorShapeStub() { 189 return this; 190 } 191 192 scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher> 193 ChromotingJniInstance::GetTokenFetcher(const std::string& host_public_key) { 194 // Return null to indicate that third-party authentication is unsupported. 195 return scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>(); 196 } 197 198 void ChromotingJniInstance::InjectClipboardEvent( 199 const protocol::ClipboardEvent& event) { 200 NOTIMPLEMENTED(); 201 } 202 203 void ChromotingJniInstance::SetCursorShape( 204 const protocol::CursorShapeInfo& shape) { 205 NOTIMPLEMENTED(); 206 } 207 208 void ChromotingJniInstance::ConnectToHostOnDisplayThread() { 209 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); 210 211 frame_consumer_ = new FrameConsumerProxy(jni_runtime_->display_task_runner()); 212 view_.reset(new JniFrameConsumer(jni_runtime_)); 213 view_weak_factory_.reset(new base::WeakPtrFactory<JniFrameConsumer>( 214 view_.get())); 215 frame_consumer_->Attach(view_weak_factory_->GetWeakPtr()); 216 217 jni_runtime_->network_task_runner()->PostTask( 218 FROM_HERE, 219 base::Bind(&ChromotingJniInstance::ConnectToHostOnNetworkThread, 220 this)); 221 } 222 223 void ChromotingJniInstance::ConnectToHostOnNetworkThread() { 224 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread()); 225 226 client_config_.reset(new ClientConfig()); 227 client_config_->host_jid = host_jid_; 228 client_config_->host_public_key = host_pubkey_; 229 230 client_config_->fetch_secret_callback = base::Bind( 231 &ChromotingJniInstance::FetchSecret, 232 this); 233 client_config_->authentication_tag = host_id_; 234 235 if (!pairing_id_.empty() && !pairing_secret_.empty()) { 236 client_config_->client_pairing_id = pairing_id_; 237 client_config_->client_paired_secret = pairing_secret_; 238 client_config_->authentication_methods.push_back( 239 protocol::AuthenticationMethod::FromString("spake2_pair")); 240 } 241 242 client_config_->authentication_methods.push_back( 243 protocol::AuthenticationMethod::FromString("spake2_hmac")); 244 client_config_->authentication_methods.push_back( 245 protocol::AuthenticationMethod::FromString("spake2_plain")); 246 247 client_context_.reset(new ClientContext( 248 jni_runtime_->network_task_runner().get())); 249 client_context_->Start(); 250 251 connection_.reset(new protocol::ConnectionToHost(true)); 252 253 client_.reset(new ChromotingClient(*client_config_, 254 client_context_.get(), 255 connection_.get(), 256 this, 257 frame_consumer_, 258 scoped_ptr<AudioPlayer>())); 259 260 view_->set_frame_producer(client_->GetFrameProducer()); 261 262 signaling_config_.reset(new XmppSignalStrategy::XmppServerConfig()); 263 signaling_config_->host = kXmppServer; 264 signaling_config_->port = kXmppPort; 265 signaling_config_->use_tls = kXmppUseTls; 266 267 signaling_.reset(new XmppSignalStrategy(jni_runtime_->url_requester(), 268 username_, 269 auth_token_, 270 "oauth2", 271 *signaling_config_)); 272 273 network_settings_.reset(new NetworkSettings( 274 NetworkSettings::NAT_TRAVERSAL_ENABLED)); 275 scoped_ptr<protocol::TransportFactory> fact( 276 protocol::LibjingleTransportFactory::Create( 277 *network_settings_, 278 jni_runtime_->url_requester())); 279 280 client_->Start(signaling_.get(), fact.Pass()); 281 } 282 283 void ChromotingJniInstance::DisconnectFromHostOnNetworkThread() { 284 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread()); 285 286 username_ = ""; 287 auth_token_ = ""; 288 host_jid_ = ""; 289 host_id_ = ""; 290 host_pubkey_ = ""; 291 292 // |client_| must be torn down before |signaling_|. 293 connection_.reset(); 294 client_.reset(); 295 } 296 297 void ChromotingJniInstance::FetchSecret( 298 bool pairable, 299 const protocol::SecretFetchedCallback& callback) { 300 if (!jni_runtime_->ui_task_runner()->BelongsToCurrentThread()) { 301 jni_runtime_->ui_task_runner()->PostTask( 302 FROM_HERE, 303 base::Bind(&ChromotingJniInstance::FetchSecret, 304 this, 305 pairable, 306 callback)); 307 return; 308 } 309 310 if (!pairing_id_.empty() || !pairing_secret_.empty()) { 311 // We attempted to connect using an existing pairing that was rejected. 312 // Unless we forget about the stale credentials, we'll continue trying them. 313 LOG(INFO) << "Deleting rejected pairing credentials"; 314 jni_runtime_->CommitPairingCredentials(host_id_, "", ""); 315 } 316 317 pin_callback_ = callback; 318 jni_runtime_->DisplayAuthenticationPrompt(); 319 } 320 321 } // namespace remoting 322