OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/client/jni/chromoting_jni_instance.h" | 5 #include "remoting/client/jni/chromoting_jni_instance.h" |
6 | 6 |
7 #include "base/android/base_jni_registrar.h" | 7 #include "base/android/base_jni_registrar.h" |
8 #include "base/android/jni_android.h" | 8 #include "base/android/jni_android.h" |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/memory/singleton.h" | 12 #include "base/memory/singleton.h" |
13 #include "net/android/net_jni_registrar.h" | 13 #include "net/android/net_jni_registrar.h" |
14 #include "remoting/base/url_request_context.h" | 14 #include "remoting/base/url_request_context.h" |
15 #include "remoting/client/audio_player.h" | 15 #include "remoting/client/audio_player.h" |
16 #include "remoting/protocol/libjingle_transport_factory.h" | 16 #include "remoting/protocol/libjingle_transport_factory.h" |
17 | 17 |
18 namespace remoting { | 18 namespace remoting { |
19 | 19 |
20 // static | 20 // static |
21 ChromotingJNIInstance* ChromotingJNIInstance::GetInstance() { | 21 ChromotingJNIInstance* ChromotingJNIInstance::GetInstance() { |
22 return Singleton<ChromotingJNIInstance>::get(); | 22 return Singleton<ChromotingJNIInstance>::get(); |
23 } | 23 } |
24 | 24 |
25 ChromotingJNIInstance::ChromotingJNIInstance() | 25 ChromotingJNIInstance::ChromotingJNIInstance() |
26 : username_cstr_(NULL), | 26 : connected_(false) { |
27 auth_token_cstr_(NULL), | |
28 host_jid_cstr_(NULL), | |
29 host_id_cstr_(NULL), | |
30 host_pubkey_cstr_(NULL), | |
31 pin_cstr_(NULL) { | |
32 JNIEnv* env = base::android::AttachCurrentThread(); | 27 JNIEnv* env = base::android::AttachCurrentThread(); |
33 | 28 |
29 // The base and networks stacks must be registered with JNI in order to work | |
30 // on Android. An AtExitManager cleans this up at world's end. | |
34 collector_.reset(new base::AtExitManager()); | 31 collector_.reset(new base::AtExitManager()); |
35 base::android::RegisterJni(env); | 32 base::android::RegisterJni(env); |
36 net::android::RegisterJni(env); | 33 net::android::RegisterJni(env); |
37 | 34 |
38 LOG(INFO) << "starting main message loop"; | 35 // On Android, the UI thread is managed by Java, so we need to attach and |
36 // start a special type of message loop to allow Chromium code to run tasks. | |
37 LOG(INFO) << "Starting main message loop"; | |
39 ui_loop_.reset(new base::MessageLoopForUI()); | 38 ui_loop_.reset(new base::MessageLoopForUI()); |
40 ui_loop_->Start(); | 39 ui_loop_->Start(); |
41 | 40 |
42 LOG(INFO) << "spawning additional threads"; | 41 LOG(INFO) << "Spawning additional threads"; |
43 ui_runner_ = new AutoThreadTaskRunner(ui_loop_->message_loop_proxy(), | 42 // TODO(solb) We shouldn't really control the managed UI thread's lifetime. |
Wez
2013/07/12 20:13:41
nit: We _don't_ really control its lifetime, I thi
solb
2013/07/12 21:10:54
Done.
| |
44 base::MessageLoop::QuitClosure()); | 43 ui_task_runner_ = new AutoThreadTaskRunner(ui_loop_->message_loop_proxy(), |
45 net_runner_ = AutoThread::CreateWithType("native_net", | 44 base::MessageLoop::QuitClosure()); |
46 ui_runner_, | 45 network_task_runner_ = AutoThread::CreateWithType("native_net", |
47 base::MessageLoop::TYPE_IO); | 46 ui_task_runner_, |
48 disp_runner_ = AutoThread::CreateWithType("native_disp", | 47 base::MessageLoop::TYPE_IO); |
49 ui_runner_, | 48 display_task_runner_ = AutoThread::Create("native_disp", |
50 base::MessageLoop::TYPE_DEFAULT); | 49 ui_task_runner_); |
51 | 50 |
52 url_requester_ = new URLRequestContextGetter(ui_runner_, net_runner_); | 51 url_requester_ = new URLRequestContextGetter(ui_task_runner_, |
52 network_task_runner_); | |
53 | 53 |
54 class_ = static_cast<jclass>(env->NewGlobalRef(env->FindClass(JAVA_CLASS))); | 54 class_ = static_cast<jclass>(env->NewGlobalRef(env->FindClass(JAVA_CLASS))); |
55 } | 55 } |
56 | 56 |
57 ChromotingJNIInstance::~ChromotingJNIInstance() { | 57 ChromotingJNIInstance::~ChromotingJNIInstance() { |
Wez
2013/07/12 20:13:41
nit: check which thread this is torn down on?
solb
2013/07/12 21:10:54
Done.
| |
58 DCHECK(!connected_); | |
59 | |
58 JNIEnv* env = base::android::AttachCurrentThread(); | 60 JNIEnv* env = base::android::AttachCurrentThread(); |
59 env->DeleteGlobalRef(class_); | 61 env->DeleteGlobalRef(class_); |
60 // TODO(solb) detach all threads from JVM | 62 // TODO(solb): crbug.com/259594 Detach all threads from JVM here. |
61 } | 63 } |
62 | 64 |
63 void ChromotingJNIInstance::ConnectToHost(jstring username, | 65 void ChromotingJNIInstance::ConnectToHost(const char* username, |
64 jstring auth_token, | 66 const char* auth_token, |
65 jstring host_jid, | 67 const char* host_jid, |
66 jstring host_id, | 68 const char* host_id, |
67 jstring host_pubkey) { | 69 const char* host_pubkey) { |
68 JNIEnv* env = base::android::AttachCurrentThread(); | 70 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
71 DCHECK(!connected_); | |
72 connected_ = true; | |
69 | 73 |
70 username_jstr_ = static_cast<jstring>(env->NewGlobalRef(username)); | 74 username_ = username; |
71 auth_token_jstr_ = static_cast<jstring>(env->NewGlobalRef(auth_token)); | 75 auth_token_ = auth_token; |
72 host_jid_jstr_ = static_cast<jstring>(env->NewGlobalRef(host_jid)); | 76 host_jid_ = host_jid; |
73 host_id_jstr_ = static_cast<jstring>(env->NewGlobalRef(host_id)); | 77 host_id_ = host_id; |
74 host_pubkey_jstr_ = static_cast<jstring>(env->NewGlobalRef(host_pubkey)); | 78 host_pubkey_ = host_pubkey; |
75 | 79 |
76 username_cstr_ = env->GetStringUTFChars(username_jstr_, NULL); | 80 display_task_runner_->PostTask(FROM_HERE, base::Bind( |
77 auth_token_cstr_ = env->GetStringUTFChars(auth_token_jstr_, NULL); | |
78 host_jid_cstr_ = env->GetStringUTFChars(host_jid_jstr_, NULL); | |
79 host_id_cstr_ = env->GetStringUTFChars(host_id_jstr_, NULL); | |
80 host_pubkey_cstr_ = env->GetStringUTFChars(host_pubkey_jstr_, NULL); | |
81 | |
82 // We're a singleton, so Unretained is safe here. | |
83 disp_runner_->PostTask(FROM_HERE, base::Bind( | |
84 &ChromotingJNIInstance::ConnectToHostOnDisplayThread, | 81 &ChromotingJNIInstance::ConnectToHostOnDisplayThread, |
85 base::Unretained(this))); | 82 base::Unretained(this))); |
86 } | 83 } |
87 | 84 |
88 void ChromotingJNIInstance::DisconnectFromHost() { | 85 void ChromotingJNIInstance::DisconnectFromHost() { |
89 JNIEnv* env = base::android::AttachCurrentThread(); | 86 DCHECK(ui_task_runner_->BelongsToCurrentThread() && connected_); |
Wez
2013/07/12 20:13:41
Check thread & connected_ state separately, as abo
solb
2013/07/12 21:10:54
Sorry; I'm not sure how that slipped through.
| |
87 connected_ = false; | |
90 | 88 |
91 env->ReleaseStringUTFChars(username_jstr_, username_cstr_); | 89 network_task_runner_->PostTask(FROM_HERE, base::Bind( |
92 env->ReleaseStringUTFChars(auth_token_jstr_, auth_token_cstr_); | |
93 env->ReleaseStringUTFChars(host_jid_jstr_, host_jid_cstr_); | |
94 env->ReleaseStringUTFChars(host_id_jstr_, host_id_cstr_); | |
95 env->ReleaseStringUTFChars(host_pubkey_jstr_, host_pubkey_cstr_); | |
96 | |
97 username_cstr_ = NULL; | |
98 auth_token_cstr_ = NULL; | |
99 host_jid_cstr_ = NULL; | |
100 host_id_cstr_ = NULL; | |
101 host_pubkey_cstr_ = NULL; | |
102 | |
103 env->DeleteGlobalRef(username_jstr_); | |
104 env->DeleteGlobalRef(auth_token_jstr_); | |
105 env->DeleteGlobalRef(host_jid_jstr_); | |
106 env->DeleteGlobalRef(host_id_jstr_); | |
107 env->DeleteGlobalRef(host_pubkey_jstr_); | |
108 | |
109 if (pin_cstr_) { | |
110 // AuthenticatedWithPin() has been called. | |
111 env->ReleaseStringUTFChars(pin_jstr_, pin_cstr_); | |
112 pin_cstr_ = NULL; | |
113 env->DeleteGlobalRef(pin_jstr_); | |
114 } | |
115 | |
116 // We're a singleton, so Unretained is safe here. | |
117 net_runner_->PostTask(FROM_HERE, base::Bind( | |
118 &ChromotingJNIInstance::DisconnectFromHostOnNetworkThread, | 90 &ChromotingJNIInstance::DisconnectFromHostOnNetworkThread, |
119 base::Unretained(this))); | 91 base::Unretained(this))); |
120 } | 92 } |
121 | 93 |
122 void ChromotingJNIInstance::AuthenticateWithPin(jstring pin) { | 94 void ChromotingJNIInstance::AuthenticateWithPin(const char* pin) { |
Wez
2013/07/12 20:13:41
Do you need to check which thread this is called o
solb
2013/07/12 21:10:54
Done.
| |
123 JNIEnv* env = base::android::AttachCurrentThread(); | 95 pin_ = pin; |
Wez
2013/07/12 20:13:41
Why do you store the PIN at all? You never seem to
solb
2013/07/12 21:10:54
Good point. I think this is a bit of a leftover, b
| |
124 | 96 network_task_runner_->PostTask(FROM_HERE, base::Bind(pin_callback_, pin_)); |
125 pin_jstr_ = static_cast<jstring>(env->NewGlobalRef(pin)); | |
126 pin_cstr_ = env->GetStringUTFChars(pin_jstr_, NULL); | |
127 | |
128 net_runner_->PostTask(FROM_HERE, base::Bind(announce_secret_, pin_cstr_)); | |
129 } | 97 } |
130 | 98 |
131 void ChromotingJNIInstance::FetchSecret( | 99 void ChromotingJNIInstance::FetchSecret( |
132 bool pairable, | 100 bool pairable, |
133 const protocol::SecretFetchedCallback& callback_encore) { | 101 const protocol::SecretFetchedCallback& callback) { |
134 // All our work must be done on the UI thread. | 102 if (!ui_task_runner_->BelongsToCurrentThread()) { |
135 if (!ui_runner_->BelongsToCurrentThread()) { | 103 ui_task_runner_->PostTask(FROM_HERE, base::Bind( |
136 // We're a singleton, so Unretained is safe here. | |
137 ui_runner_->PostTask(FROM_HERE, base::Bind( | |
138 &ChromotingJNIInstance::FetchSecret, | 104 &ChromotingJNIInstance::FetchSecret, |
139 base::Unretained(this), | 105 base::Unretained(this), |
140 pairable, | 106 pairable, |
141 callback_encore)); | 107 callback)); |
142 return; | 108 return; |
143 } | 109 } |
144 | 110 |
145 announce_secret_ = callback_encore; | 111 pin_callback_ = callback; |
146 JNIEnv* env = base::android::AttachCurrentThread(); | 112 JNIEnv* env = base::android::AttachCurrentThread(); |
147 env->CallStaticVoidMethod( | 113 env->CallStaticVoidMethod( |
148 class_, | 114 class_, |
149 env->GetStaticMethodID(class_, "displayAuthenticationPrompt", "()V")); | 115 env->GetStaticMethodID(class_, "displayAuthenticationPrompt", "()V")); |
150 } | 116 } |
151 | 117 |
152 void ChromotingJNIInstance::OnConnectionState( | 118 void ChromotingJNIInstance::OnConnectionState( |
153 protocol::ConnectionToHost::State state, | 119 protocol::ConnectionToHost::State state, |
154 protocol::ErrorCode error) { | 120 protocol::ErrorCode error) { |
155 // All our work must be done on the UI thread. | 121 if (!ui_task_runner_->BelongsToCurrentThread()) { |
156 if (!ui_runner_->BelongsToCurrentThread()) { | 122 ui_task_runner_->PostTask(FROM_HERE, base::Bind( |
157 // We're a singleton, so Unretained is safe here. | |
158 ui_runner_->PostTask(FROM_HERE, base::Bind( | |
159 &ChromotingJNIInstance::OnConnectionState, | 123 &ChromotingJNIInstance::OnConnectionState, |
160 base::Unretained(this), | 124 base::Unretained(this), |
161 state, | 125 state, |
162 error)); | 126 error)); |
163 return; | 127 return; |
164 } | 128 } |
165 | 129 |
166 JNIEnv* env = base::android::AttachCurrentThread(); | 130 JNIEnv* env = base::android::AttachCurrentThread(); |
167 env->CallStaticVoidMethod( | 131 env->CallStaticVoidMethod( |
168 class_, | 132 class_, |
169 env->GetStaticMethodID(class_, "reportConnectionStatus", "(II)V"), | 133 env->GetStaticMethodID(class_, "reportConnectionStatus", "(II)V"), |
170 state, | 134 state, |
171 error); | 135 error); |
172 } | 136 } |
173 | 137 |
174 void ChromotingJNIInstance::OnConnectionReady(bool ready) { | 138 void ChromotingJNIInstance::OnConnectionReady(bool ready) { |
139 // We ignore this message, since OnConnectionState() tells us the same thing. | |
175 } | 140 } |
176 | 141 |
177 void ChromotingJNIInstance::SetCapabilities(const std::string& capabilities) {} | 142 void ChromotingJNIInstance::SetCapabilities(const std::string& capabilities) {} |
178 | 143 |
179 void ChromotingJNIInstance::SetPairingResponse( | 144 void ChromotingJNIInstance::SetPairingResponse( |
180 const protocol::PairingResponse& response) {} | 145 const protocol::PairingResponse& response) { |
146 NOTIMPLEMENTED(); | |
147 } | |
181 | 148 |
182 protocol::ClipboardStub* ChromotingJNIInstance::GetClipboardStub() { | 149 protocol::ClipboardStub* ChromotingJNIInstance::GetClipboardStub() { |
183 NOTIMPLEMENTED(); | 150 NOTIMPLEMENTED(); |
184 return NULL; | 151 return NULL; |
185 } | 152 } |
186 | 153 |
187 protocol::CursorShapeStub* ChromotingJNIInstance::GetCursorShapeStub() { | 154 protocol::CursorShapeStub* ChromotingJNIInstance::GetCursorShapeStub() { |
188 NOTIMPLEMENTED(); | 155 NOTIMPLEMENTED(); |
189 return NULL; | 156 return NULL; |
190 } | 157 } |
191 | 158 |
192 // We don't use NOTIMPLEMENTED() here because NegotiatingClientAuthenticator | |
193 // calls this even if it doesn't use the configuration method, and we don't | |
194 // want to print an error on every run. | |
195 scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher> | 159 scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher> |
196 ChromotingJNIInstance::GetTokenFetcher(const std::string& host_public_key) { | 160 ChromotingJNIInstance::GetTokenFetcher(const std::string& host_public_key) { |
197 LOG(INFO) << "ChromotingJNIInstance::GetTokenFetcher(...) [unimplemented]"; | 161 // Return null to indicate that third-party authentication is unsupported. |
198 return scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>(); | 162 return scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>(); |
199 } | 163 } |
200 | 164 |
201 void ChromotingJNIInstance::ConnectToHostOnDisplayThread() { | 165 void ChromotingJNIInstance::ConnectToHostOnDisplayThread() { |
202 DCHECK(disp_runner_->BelongsToCurrentThread()); | 166 DCHECK(display_task_runner_->BelongsToCurrentThread()); |
203 | 167 |
204 if (!frames_.get()) { | 168 if (!frame_consumer_.get()) { |
205 frames_ = new FrameConsumerProxy(disp_runner_); | 169 frame_consumer_ = new FrameConsumerProxy(display_task_runner_); |
206 // TODO(solb) Instantiate some FrameConsumer implementation and attach it. | 170 // TODO(solb) Instantiate some FrameConsumer implementation and attach it. |
207 } | 171 } |
208 | 172 |
209 // We're a singleton, so Unretained is safe here. | 173 network_task_runner_->PostTask(FROM_HERE, base::Bind( |
210 net_runner_->PostTask(FROM_HERE, base::Bind( | |
211 &ChromotingJNIInstance::ConnectToHostOnNetworkThread, | 174 &ChromotingJNIInstance::ConnectToHostOnNetworkThread, |
212 base::Unretained(this))); | 175 base::Unretained(this))); |
213 } | 176 } |
214 | 177 |
215 void ChromotingJNIInstance::ConnectToHostOnNetworkThread() { | 178 void ChromotingJNIInstance::ConnectToHostOnNetworkThread() { |
216 DCHECK(net_runner_->BelongsToCurrentThread()); | 179 DCHECK(network_task_runner_->BelongsToCurrentThread()); |
217 | 180 |
218 client_config_.reset(new ClientConfig()); | 181 client_config_.reset(new ClientConfig()); |
219 client_config_->host_jid = host_jid_cstr_; | 182 client_config_->host_jid = host_jid_; |
220 client_config_->host_public_key = host_pubkey_cstr_; | 183 client_config_->host_public_key = host_pubkey_; |
221 // We're a singleton, so Unretained is safe here. | 184 |
222 client_config_->fetch_secret_callback = base::Bind( | 185 client_config_->fetch_secret_callback = base::Bind( |
223 &ChromotingJNIInstance::FetchSecret, | 186 &ChromotingJNIInstance::FetchSecret, |
224 base::Unretained(this)); | 187 base::Unretained(this)); |
225 client_config_->authentication_tag = host_id_cstr_; | 188 client_config_->authentication_tag = host_id_; |
226 | 189 |
227 // TODO(solb) Move these hardcoded values elsewhere: | 190 // TODO(solb) Move these hardcoded values elsewhere: |
228 client_config_->authentication_methods.push_back( | 191 client_config_->authentication_methods.push_back( |
229 protocol::AuthenticationMethod::FromString("spake2_pair")); | |
230 client_config_->authentication_methods.push_back( | |
231 protocol::AuthenticationMethod::FromString("spake2_hmac")); | 192 protocol::AuthenticationMethod::FromString("spake2_hmac")); |
232 client_config_->authentication_methods.push_back( | 193 client_config_->authentication_methods.push_back( |
233 protocol::AuthenticationMethod::FromString("spake2_plain")); | 194 protocol::AuthenticationMethod::FromString("spake2_plain")); |
234 | 195 |
235 client_context_.reset(new ClientContext(net_runner_.get())); | 196 client_context_.reset(new ClientContext(network_task_runner_.get())); |
236 client_context_->Start(); | 197 client_context_->Start(); |
237 | 198 |
238 connection_.reset(new protocol::ConnectionToHost(true)); | 199 connection_.reset(new protocol::ConnectionToHost(true)); |
239 | 200 |
240 client_.reset(new ChromotingClient(*client_config_, | 201 client_.reset(new ChromotingClient(*client_config_, |
241 client_context_.get(), | 202 client_context_.get(), |
242 connection_.get(), | 203 connection_.get(), |
243 this, | 204 this, |
244 frames_, | 205 frame_consumer_, |
245 scoped_ptr<AudioPlayer>())); | 206 scoped_ptr<AudioPlayer>())); |
246 | 207 |
247 chat_config_.reset(new XmppSignalStrategy::XmppServerConfig()); | 208 signaling_config_.reset(new XmppSignalStrategy::XmppServerConfig()); |
248 chat_config_->host = CHAT_SERVER; | 209 signaling_config_->host = CHAT_SERVER; |
249 chat_config_->port = CHAT_PORT; | 210 signaling_config_->port = CHAT_PORT; |
250 chat_config_->use_tls = CHAT_USE_TLS; | 211 signaling_config_->use_tls = CHAT_USE_TLS; |
251 | 212 |
252 chat_.reset(new XmppSignalStrategy(url_requester_, | 213 signaling_.reset(new XmppSignalStrategy(url_requester_, |
253 username_cstr_, | 214 username_, |
254 auth_token_cstr_, | 215 auth_token_, |
255 CHAT_AUTH_METHOD, | 216 CHAT_AUTH_METHOD, |
256 *chat_config_)); | 217 *signaling_config_)); |
257 | 218 |
258 netset_.reset(new NetworkSettings(NetworkSettings::NAT_TRAVERSAL_OUTGOING)); | 219 network_settings_.reset(new NetworkSettings( |
220 NetworkSettings::NAT_TRAVERSAL_OUTGOING)); | |
259 scoped_ptr<protocol::TransportFactory> fact( | 221 scoped_ptr<protocol::TransportFactory> fact( |
260 protocol::LibjingleTransportFactory::Create(*netset_, url_requester_)); | 222 protocol::LibjingleTransportFactory::Create(*network_settings_, |
223 url_requester_)); | |
261 | 224 |
262 client_->Start(chat_.get(), fact.Pass()); | 225 client_->Start(signaling_.get(), fact.Pass()); |
263 } | 226 } |
264 | 227 |
265 void ChromotingJNIInstance::DisconnectFromHostOnNetworkThread() { | 228 void ChromotingJNIInstance::DisconnectFromHostOnNetworkThread() { |
266 DCHECK(net_runner_->BelongsToCurrentThread()); | 229 DCHECK(network_task_runner_->BelongsToCurrentThread()); |
267 | 230 |
231 username_ = ""; | |
232 auth_token_ = ""; | |
233 host_jid_ = ""; | |
234 host_id_ = ""; | |
235 host_pubkey_ = ""; | |
236 pin_ = ""; | |
237 | |
238 // |client_| must be torn down before |signaling_|. | |
268 client_.reset(); | 239 client_.reset(); |
269 connection_.reset(); | 240 connection_.reset(); |
270 client_context_.reset(); | 241 client_context_.reset(); |
271 client_config_.reset(); | 242 client_config_.reset(); |
272 chat_.reset(); // This object must outlive client_. | 243 signaling_.reset(); |
273 chat_config_.reset(); // TODO(solb) Restructure to reuse between sessions. | 244 signaling_config_.reset(); |
274 netset_.reset(); | 245 network_settings_.reset(); |
275 } | 246 } |
276 | 247 |
277 } // namespace remoting | 248 } // namespace remoting |
OLD | NEW |