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/scoped_java_ref.h"
11 #include "base/basictypes.h"
12 #include "base/command_line.h"
13 #include "base/memory/singleton.h"
14 #include "base/stl_util.h"
15 #include "base/synchronization/waitable_event.h"
16 #include "google_apis/google_api_keys.h"
17 #include "jni/JniInterface_jni.h"
18 #include "media/base/yuv_convert.h"
19 #include "remoting/base/url_request_context.h"
20 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
22 using base::android::ConvertJavaStringToUTF8;
23 using base::android::ConvertUTF8ToJavaString;
24 using base::android::ToJavaByteArray;
28 const int kBytesPerPixel = 4;
34 bool RegisterJni(JNIEnv* env) {
35 return remoting::RegisterNativesImpl(env);
38 // Implementation of stubs defined in JniInterface_jni.h. These are the entry
39 // points for JNI calls from Java into C++.
41 static void LoadNative(JNIEnv* env, jclass clazz, jobject context) {
42 base::android::ScopedJavaLocalRef<jobject> context_activity(env, context);
43 base::android::InitApplicationContext(env, context_activity);
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 CommandLine::Init(0, NULL);
51 // Create the singleton now so that the Chromoting threads will be set up.
52 remoting::ChromotingJniRuntime::GetInstance();
55 static jstring GetApiKey(JNIEnv* env, jclass clazz) {
56 return ConvertUTF8ToJavaString(
57 env, google_apis::GetAPIKey().c_str()).Release();
60 static jstring GetClientId(JNIEnv* env, jclass clazz) {
61 return ConvertUTF8ToJavaString(
62 env, google_apis::GetOAuth2ClientID(
63 google_apis::CLIENT_REMOTING).c_str()).Release();
66 static jstring GetClientSecret(JNIEnv* env, jclass clazz) {
67 return ConvertUTF8ToJavaString(
68 env, google_apis::GetOAuth2ClientSecret(
69 google_apis::CLIENT_REMOTING).c_str()).Release();
72 static void Connect(JNIEnv* env,
81 remoting::ChromotingJniRuntime::GetInstance()->ConnectToHost(
82 ConvertJavaStringToUTF8(env, username).c_str(),
83 ConvertJavaStringToUTF8(env, authToken).c_str(),
84 ConvertJavaStringToUTF8(env, hostJid).c_str(),
85 ConvertJavaStringToUTF8(env, hostId).c_str(),
86 ConvertJavaStringToUTF8(env, hostPubkey).c_str(),
87 ConvertJavaStringToUTF8(env, pairId).c_str(),
88 ConvertJavaStringToUTF8(env, pairSecret).c_str());
91 static void Disconnect(JNIEnv* env, jclass clazz) {
92 remoting::ChromotingJniRuntime::GetInstance()->DisconnectFromHost();
95 static void AuthenticationResponse(JNIEnv* env,
100 remoting::ChromotingJniRuntime::GetInstance()->session()->ProvideSecret(
101 ConvertJavaStringToUTF8(env, pin).c_str(), createPair,
102 ConvertJavaStringToUTF8(env, deviceName));
105 static void ScheduleRedraw(JNIEnv* env, jclass clazz) {
106 remoting::ChromotingJniRuntime::GetInstance()->session()->RedrawDesktop();
109 static void SendMouseEvent(JNIEnv* env,
114 jboolean buttonDown) {
115 // Button must be within the bounds of the MouseEvent_MouseButton enum.
116 DCHECK(whichButton >= 0 && whichButton < 5);
118 remoting::ChromotingJniRuntime::GetInstance()->session()->SendMouseEvent(
120 static_cast<remoting::protocol::MouseEvent_MouseButton>(whichButton),
124 static void SendMouseWheelEvent(JNIEnv* env,
128 remoting::ChromotingJniRuntime::GetInstance()->session()->SendMouseWheelEvent(
132 static void SendKeyEvent(JNIEnv* env,
136 remoting::ChromotingJniRuntime::GetInstance()->session()->SendKeyEvent(
140 static void SendTextEvent(JNIEnv* env,
143 remoting::ChromotingJniRuntime::GetInstance()->session()->SendTextEvent(
144 ConvertJavaStringToUTF8(env, text));
147 // ChromotingJniRuntime implementation.
150 ChromotingJniRuntime* ChromotingJniRuntime::GetInstance() {
151 return Singleton<ChromotingJniRuntime>::get();
154 ChromotingJniRuntime::ChromotingJniRuntime() {
155 at_exit_manager_.reset(new base::AtExitManager());
157 // On Android, the UI thread is managed by Java, so we need to attach and
158 // start a special type of message loop to allow Chromium code to run tasks.
159 ui_loop_.reset(new base::MessageLoopForUI());
162 // TODO(solb) Stop pretending to control the managed UI thread's lifetime.
163 ui_task_runner_ = new AutoThreadTaskRunner(ui_loop_->message_loop_proxy(),
164 base::MessageLoop::QuitClosure());
165 network_task_runner_ = AutoThread::CreateWithType("native_net",
167 base::MessageLoop::TYPE_IO);
168 display_task_runner_ = AutoThread::Create("native_disp",
171 url_requester_ = new URLRequestContextGetter(network_task_runner_);
173 // Allows later decoding of video frames.
174 media::InitializeCPUSpecificYUVConversions();
177 ChromotingJniRuntime::~ChromotingJniRuntime() {
178 // The singleton should only ever be destroyed on the main thread.
179 DCHECK(ui_task_runner_->BelongsToCurrentThread());
181 // The session must be shut down first, since it depends on our other
182 // components' still being alive.
183 DisconnectFromHost();
185 base::WaitableEvent done_event(false, false);
186 network_task_runner_->PostTask(FROM_HERE, base::Bind(
187 &ChromotingJniRuntime::DetachFromVmAndSignal,
188 base::Unretained(this),
191 display_task_runner_->PostTask(FROM_HERE, base::Bind(
192 &ChromotingJniRuntime::DetachFromVmAndSignal,
193 base::Unretained(this),
196 base::android::DetachFromVM();
199 void ChromotingJniRuntime::ConnectToHost(const char* username,
200 const char* auth_token,
201 const char* host_jid,
203 const char* host_pubkey,
204 const char* pairing_id,
205 const char* pairing_secret) {
206 DCHECK(ui_task_runner_->BelongsToCurrentThread());
208 session_ = new ChromotingJniInstance(this,
218 void ChromotingJniRuntime::DisconnectFromHost() {
219 DCHECK(ui_task_runner_->BelongsToCurrentThread());
226 void ChromotingJniRuntime::ReportConnectionStatus(
227 protocol::ConnectionToHost::State state,
228 protocol::ErrorCode error) {
229 DCHECK(ui_task_runner_->BelongsToCurrentThread());
231 JNIEnv* env = base::android::AttachCurrentThread();
232 Java_JniInterface_reportConnectionStatus(env, state, error);
235 void ChromotingJniRuntime::DisplayAuthenticationPrompt(bool pairing_supported) {
236 DCHECK(ui_task_runner_->BelongsToCurrentThread());
238 JNIEnv* env = base::android::AttachCurrentThread();
239 Java_JniInterface_displayAuthenticationPrompt(env, pairing_supported);
242 void ChromotingJniRuntime::CommitPairingCredentials(const std::string& host,
243 const std::string& id,
244 const std::string& secret) {
245 DCHECK(ui_task_runner_->BelongsToCurrentThread());
247 JNIEnv* env = base::android::AttachCurrentThread();
248 ScopedJavaLocalRef<jstring> j_host = ConvertUTF8ToJavaString(env, host);
249 ScopedJavaLocalRef<jbyteArray> j_id = ToJavaByteArray(
250 env, reinterpret_cast<const uint8*>(id.data()), id.size());
251 ScopedJavaLocalRef<jbyteArray> j_secret = ToJavaByteArray(
252 env, reinterpret_cast<const uint8*>(secret.data()), secret.size());
254 Java_JniInterface_commitPairingCredentials(
255 env, j_host.obj(), j_id.obj(), j_secret.obj());
258 base::android::ScopedJavaLocalRef<jobject> ChromotingJniRuntime::NewBitmap(
259 webrtc::DesktopSize size) {
260 JNIEnv* env = base::android::AttachCurrentThread();
261 return Java_JniInterface_newBitmap(env, size.width(), size.height());
264 void ChromotingJniRuntime::UpdateFrameBitmap(jobject bitmap) {
265 DCHECK(display_task_runner_->BelongsToCurrentThread());
267 JNIEnv* env = base::android::AttachCurrentThread();
268 Java_JniInterface_setVideoFrame(env, bitmap);
271 void ChromotingJniRuntime::UpdateCursorShape(
272 const protocol::CursorShapeInfo& cursor_shape) {
273 DCHECK(display_task_runner_->BelongsToCurrentThread());
275 // const_cast<> is safe as long as the Java updateCursorShape() method copies
276 // the data out of the buffer without mutating it, and doesn't keep any
277 // reference to the buffer afterwards. Unfortunately, there seems to be no way
278 // to create a read-only ByteBuffer from a pointer-to-const.
279 char* data = string_as_array(const_cast<std::string*>(&cursor_shape.data()));
280 int cursor_total_bytes =
281 cursor_shape.width() * cursor_shape.height() * kBytesPerPixel;
283 JNIEnv* env = base::android::AttachCurrentThread();
284 base::android::ScopedJavaLocalRef<jobject> buffer(env,
285 env->NewDirectByteBuffer(data, cursor_total_bytes));
286 Java_JniInterface_updateCursorShape(env,
287 cursor_shape.width(),
288 cursor_shape.height(),
289 cursor_shape.hotspot_x(),
290 cursor_shape.hotspot_y(),
294 void ChromotingJniRuntime::RedrawCanvas() {
295 DCHECK(display_task_runner_->BelongsToCurrentThread());
297 JNIEnv* env = base::android::AttachCurrentThread();
298 Java_JniInterface_redrawGraphicsInternal(env);
301 void ChromotingJniRuntime::DetachFromVmAndSignal(base::WaitableEvent* waiter) {
302 base::android::DetachFromVM();
306 } // namespace remoting