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.
5 #include "remoting/client/jni/chromoting_jni_runtime.h"
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_array.h"
9 #include "base/android/jni_string.h"
10 #include "base/android/library_loader/library_loader_hooks.h"
11 #include "base/android/scoped_java_ref.h"
12 #include "base/basictypes.h"
13 #include "base/command_line.h"
14 #include "base/memory/singleton.h"
15 #include "base/stl_util.h"
16 #include "base/synchronization/waitable_event.h"
17 #include "google_apis/google_api_keys.h"
18 #include "jni/JniInterface_jni.h"
19 #include "remoting/base/url_request_context_getter.h"
21 using base::android::ConvertJavaStringToUTF8
;
22 using base::android::ConvertUTF8ToJavaString
;
23 using base::android::ToJavaByteArray
;
27 const int kBytesPerPixel
= 4;
33 bool RegisterChromotingJniRuntime(JNIEnv
* env
) {
34 return remoting::RegisterNativesImpl(env
);
37 // Implementation of stubs defined in JniInterface_jni.h. These are the entry
38 // points for JNI calls from Java into C++.
40 static void LoadNative(JNIEnv
* env
,
41 const JavaParamRef
<jclass
>& clazz
,
42 const JavaParamRef
<jobject
>& context
) {
43 base::android::InitApplicationContext(env
, context
);
45 // The google_apis functions check the command-line arguments to make sure no
46 // runtime API keys have been specified by the environment. Unfortunately, we
47 // neither launch Chromium nor have a command line, so we need to prevent
48 // them from DCHECKing out when they go looking.
49 base::CommandLine::Init(0, nullptr);
51 // Create the singleton now so that the Chromoting threads will be set up.
52 remoting::ChromotingJniRuntime::GetInstance();
55 static ScopedJavaLocalRef
<jstring
> GetApiKey(
57 const JavaParamRef
<jclass
>& clazz
) {
58 return ConvertUTF8ToJavaString(env
, google_apis::GetAPIKey().c_str());
61 static ScopedJavaLocalRef
<jstring
> GetClientId(
63 const JavaParamRef
<jclass
>& clazz
) {
64 return ConvertUTF8ToJavaString(
66 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING
).c_str());
69 static ScopedJavaLocalRef
<jstring
> GetClientSecret(
71 const JavaParamRef
<jclass
>& clazz
) {
72 return ConvertUTF8ToJavaString(
74 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING
).c_str());
77 static void Connect(JNIEnv
* env
,
78 const JavaParamRef
<jclass
>& clazz
,
79 const JavaParamRef
<jstring
>& username
,
80 const JavaParamRef
<jstring
>& authToken
,
81 const JavaParamRef
<jstring
>& hostJid
,
82 const JavaParamRef
<jstring
>& hostId
,
83 const JavaParamRef
<jstring
>& hostPubkey
,
84 const JavaParamRef
<jstring
>& pairId
,
85 const JavaParamRef
<jstring
>& pairSecret
,
86 const JavaParamRef
<jstring
>& capabilities
) {
87 remoting::ChromotingJniRuntime::GetInstance()->ConnectToHost(
88 ConvertJavaStringToUTF8(env
, username
).c_str(),
89 ConvertJavaStringToUTF8(env
, authToken
).c_str(),
90 ConvertJavaStringToUTF8(env
, hostJid
).c_str(),
91 ConvertJavaStringToUTF8(env
, hostId
).c_str(),
92 ConvertJavaStringToUTF8(env
, hostPubkey
).c_str(),
93 ConvertJavaStringToUTF8(env
, pairId
).c_str(),
94 ConvertJavaStringToUTF8(env
, pairSecret
).c_str(),
95 ConvertJavaStringToUTF8(env
, capabilities
).c_str());
98 static void Disconnect(JNIEnv
* env
, const JavaParamRef
<jclass
>& clazz
) {
99 remoting::ChromotingJniRuntime::GetInstance()->DisconnectFromHost();
102 static void AuthenticationResponse(JNIEnv
* env
,
103 const JavaParamRef
<jclass
>& clazz
,
104 const JavaParamRef
<jstring
>& pin
,
106 const JavaParamRef
<jstring
>& deviceName
) {
107 remoting::ChromotingJniRuntime::GetInstance()->session()->ProvideSecret(
108 ConvertJavaStringToUTF8(env
, pin
).c_str(), createPair
,
109 ConvertJavaStringToUTF8(env
, deviceName
));
112 static void ScheduleRedraw(JNIEnv
* env
, const JavaParamRef
<jclass
>& clazz
) {
113 remoting::ChromotingJniRuntime::GetInstance()->session()->RedrawDesktop();
116 static void SendMouseEvent(JNIEnv
* env
,
117 const JavaParamRef
<jclass
>& clazz
,
121 jboolean buttonDown
) {
122 // Button must be within the bounds of the MouseEvent_MouseButton enum.
123 DCHECK(whichButton
>= 0 && whichButton
< 5);
125 remoting::ChromotingJniRuntime::GetInstance()->session()->SendMouseEvent(
127 static_cast<remoting::protocol::MouseEvent_MouseButton
>(whichButton
),
131 static void SendMouseWheelEvent(JNIEnv
* env
,
132 const JavaParamRef
<jclass
>& clazz
,
135 remoting::ChromotingJniRuntime::GetInstance()->session()->SendMouseWheelEvent(
139 static jboolean
SendKeyEvent(JNIEnv
* env
,
140 const JavaParamRef
<jclass
>& clazz
,
144 return remoting::ChromotingJniRuntime::GetInstance()->session()->SendKeyEvent(
145 scanCode
, keyCode
, keyDown
);
148 static void SendTextEvent(JNIEnv
* env
,
149 const JavaParamRef
<jclass
>& clazz
,
150 const JavaParamRef
<jstring
>& text
) {
151 remoting::ChromotingJniRuntime::GetInstance()->session()->SendTextEvent(
152 ConvertJavaStringToUTF8(env
, text
));
155 static void EnableVideoChannel(JNIEnv
* env
,
156 const JavaParamRef
<jclass
>& clazz
,
158 remoting::ChromotingJniRuntime::GetInstance()->session()->EnableVideoChannel(
162 static void OnThirdPartyTokenFetched(
164 const JavaParamRef
<jclass
>& clazz
,
165 const JavaParamRef
<jstring
>& token
,
166 const JavaParamRef
<jstring
>& shared_secret
) {
167 ChromotingJniRuntime
* runtime
= remoting::ChromotingJniRuntime::GetInstance();
168 runtime
->network_task_runner()->PostTask(FROM_HERE
, base::Bind(
169 &ChromotingJniInstance::HandleOnThirdPartyTokenFetched
,
171 ConvertJavaStringToUTF8(env
, token
),
172 ConvertJavaStringToUTF8(env
, shared_secret
)));
175 static void SendExtensionMessage(JNIEnv
* env
,
176 const JavaParamRef
<jclass
>& clazz
,
177 const JavaParamRef
<jstring
>& type
,
178 const JavaParamRef
<jstring
>& data
) {
179 remoting::ChromotingJniRuntime::GetInstance()->session()->SendClientMessage(
180 ConvertJavaStringToUTF8(env
, type
),
181 ConvertJavaStringToUTF8(env
, data
));
184 // ChromotingJniRuntime implementation.
187 ChromotingJniRuntime
* ChromotingJniRuntime::GetInstance() {
188 return base::Singleton
<ChromotingJniRuntime
>::get();
191 ChromotingJniRuntime::ChromotingJniRuntime() {
192 // On Android, the UI thread is managed by Java, so we need to attach and
193 // start a special type of message loop to allow Chromium code to run tasks.
194 ui_loop_
.reset(new base::MessageLoopForUI());
197 // TODO(solb) Stop pretending to control the managed UI thread's lifetime.
198 ui_task_runner_
= new AutoThreadTaskRunner(ui_loop_
->task_runner(),
199 base::MessageLoop::QuitClosure());
200 network_task_runner_
= AutoThread::CreateWithType("native_net",
202 base::MessageLoop::TYPE_IO
);
203 display_task_runner_
= AutoThread::Create("native_disp",
207 new URLRequestContextGetter(network_task_runner_
, network_task_runner_
);
210 ChromotingJniRuntime::~ChromotingJniRuntime() {
211 // The singleton should only ever be destroyed on the main thread.
212 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
214 // The session must be shut down first, since it depends on our other
215 // components' still being alive.
216 DisconnectFromHost();
218 base::WaitableEvent
done_event(false, false);
219 network_task_runner_
->PostTask(FROM_HERE
, base::Bind(
220 &ChromotingJniRuntime::DetachFromVmAndSignal
,
221 base::Unretained(this),
224 display_task_runner_
->PostTask(FROM_HERE
, base::Bind(
225 &ChromotingJniRuntime::DetachFromVmAndSignal
,
226 base::Unretained(this),
229 base::android::LibraryLoaderExitHook();
230 base::android::DetachFromVM();
233 void ChromotingJniRuntime::ConnectToHost(const char* username
,
234 const char* auth_token
,
235 const char* host_jid
,
237 const char* host_pubkey
,
238 const char* pairing_id
,
239 const char* pairing_secret
,
240 const char* capabilities
) {
241 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
242 DCHECK(!session_
.get());
243 session_
= new ChromotingJniInstance(this,
254 void ChromotingJniRuntime::DisconnectFromHost() {
255 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
256 if (session_
.get()) {
257 session_
->Disconnect();
262 void ChromotingJniRuntime::OnConnectionState(
263 protocol::ConnectionToHost::State state
,
264 protocol::ErrorCode error
) {
265 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
267 JNIEnv
* env
= base::android::AttachCurrentThread();
268 Java_JniInterface_onConnectionState(env
, state
, error
);
271 void ChromotingJniRuntime::DisplayAuthenticationPrompt(bool pairing_supported
) {
272 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
274 JNIEnv
* env
= base::android::AttachCurrentThread();
275 Java_JniInterface_displayAuthenticationPrompt(env
, pairing_supported
);
278 void ChromotingJniRuntime::CommitPairingCredentials(const std::string
& host
,
279 const std::string
& id
,
280 const std::string
& secret
) {
281 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
283 JNIEnv
* env
= base::android::AttachCurrentThread();
284 ScopedJavaLocalRef
<jstring
> j_host
= ConvertUTF8ToJavaString(env
, host
);
285 ScopedJavaLocalRef
<jstring
> j_id
= ConvertUTF8ToJavaString(env
, id
);
286 ScopedJavaLocalRef
<jstring
> j_secret
= ConvertUTF8ToJavaString(env
,secret
);
288 Java_JniInterface_commitPairingCredentials(
289 env
, j_host
.obj(), j_id
.obj(), j_secret
.obj());
292 void ChromotingJniRuntime::FetchThirdPartyToken(const GURL
& token_url
,
293 const std::string
& client_id
,
294 const std::string
& scope
) {
295 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
296 JNIEnv
* env
= base::android::AttachCurrentThread();
298 ScopedJavaLocalRef
<jstring
> j_url
=
299 ConvertUTF8ToJavaString(env
, token_url
.spec());
300 ScopedJavaLocalRef
<jstring
> j_client_id
=
301 ConvertUTF8ToJavaString(env
, client_id
);
302 ScopedJavaLocalRef
<jstring
> j_scope
= ConvertUTF8ToJavaString(env
, scope
);
304 Java_JniInterface_fetchThirdPartyToken(
305 env
, j_url
.obj(), j_client_id
.obj(), j_scope
.obj());
308 void ChromotingJniRuntime::SetCapabilities(const std::string
& capabilities
) {
309 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
310 JNIEnv
* env
= base::android::AttachCurrentThread();
312 ScopedJavaLocalRef
<jstring
> j_cap
=
313 ConvertUTF8ToJavaString(env
, capabilities
);
315 Java_JniInterface_setCapabilities(env
, j_cap
.obj());
318 void ChromotingJniRuntime::HandleExtensionMessage(const std::string
& type
,
319 const std::string
& message
) {
320 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
321 JNIEnv
* env
= base::android::AttachCurrentThread();
323 ScopedJavaLocalRef
<jstring
> j_type
= ConvertUTF8ToJavaString(env
, type
);
324 ScopedJavaLocalRef
<jstring
> j_message
= ConvertUTF8ToJavaString(env
, message
);
326 Java_JniInterface_handleExtensionMessage(env
, j_type
.obj(), j_message
.obj());
329 base::android::ScopedJavaLocalRef
<jobject
> ChromotingJniRuntime::NewBitmap(
330 int width
, int height
) {
331 JNIEnv
* env
= base::android::AttachCurrentThread();
332 return Java_JniInterface_newBitmap(env
, width
, height
);
335 void ChromotingJniRuntime::UpdateFrameBitmap(jobject bitmap
) {
336 DCHECK(display_task_runner_
->BelongsToCurrentThread());
338 JNIEnv
* env
= base::android::AttachCurrentThread();
339 Java_JniInterface_setVideoFrame(env
, bitmap
);
342 void ChromotingJniRuntime::UpdateCursorShape(
343 const protocol::CursorShapeInfo
& cursor_shape
) {
344 DCHECK(display_task_runner_
->BelongsToCurrentThread());
346 // const_cast<> is safe as long as the Java updateCursorShape() method copies
347 // the data out of the buffer without mutating it, and doesn't keep any
348 // reference to the buffer afterwards. Unfortunately, there seems to be no way
349 // to create a read-only ByteBuffer from a pointer-to-const.
350 char* data
= string_as_array(const_cast<std::string
*>(&cursor_shape
.data()));
351 int cursor_total_bytes
=
352 cursor_shape
.width() * cursor_shape
.height() * kBytesPerPixel
;
354 JNIEnv
* env
= base::android::AttachCurrentThread();
355 base::android::ScopedJavaLocalRef
<jobject
> buffer(env
,
356 env
->NewDirectByteBuffer(data
, cursor_total_bytes
));
357 Java_JniInterface_updateCursorShape(env
,
358 cursor_shape
.width(),
359 cursor_shape
.height(),
360 cursor_shape
.hotspot_x(),
361 cursor_shape
.hotspot_y(),
365 void ChromotingJniRuntime::RedrawCanvas() {
366 DCHECK(display_task_runner_
->BelongsToCurrentThread());
368 JNIEnv
* env
= base::android::AttachCurrentThread();
369 Java_JniInterface_redrawGraphicsInternal(env
);
372 void ChromotingJniRuntime::DetachFromVmAndSignal(base::WaitableEvent
* waiter
) {
373 base::android::DetachFromVM();
376 } // namespace remoting