Home | History | Annotate | Download | only in jni
      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_runtime.h"
      6 
      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"
     21 
     22 using base::android::ConvertJavaStringToUTF8;
     23 using base::android::ConvertUTF8ToJavaString;
     24 using base::android::ToJavaByteArray;
     25 
     26 namespace {
     27 
     28 const int kBytesPerPixel = 4;
     29 
     30 }  // namespace
     31 
     32 namespace remoting {
     33 
     34 bool RegisterJni(JNIEnv* env) {
     35   return remoting::RegisterNativesImpl(env);
     36 }
     37 
     38 // Implementation of stubs defined in JniInterface_jni.h. These are the entry
     39 // points for JNI calls from Java into C++.
     40 
     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);
     44 
     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);
     50 
     51   // Create the singleton now so that the Chromoting threads will be set up.
     52   remoting::ChromotingJniRuntime::GetInstance();
     53 }
     54 
     55 static jstring GetApiKey(JNIEnv* env, jclass clazz) {
     56   return env->NewStringUTF(google_apis::GetAPIKey().c_str());
     57 }
     58 
     59 static jstring GetClientId(JNIEnv* env, jclass clazz) {
     60   return env->NewStringUTF(
     61       google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING).c_str());
     62 }
     63 
     64 static jstring GetClientSecret(JNIEnv* env, jclass clazz) {
     65   return env->NewStringUTF(
     66       google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING).c_str());
     67 }
     68 
     69 static void Connect(JNIEnv* env,
     70                     jclass clazz,
     71                     jstring username,
     72                     jstring authToken,
     73                     jstring hostJid,
     74                     jstring hostId,
     75                     jstring hostPubkey,
     76                     jstring pairId,
     77                     jstring pairSecret) {
     78   remoting::ChromotingJniRuntime::GetInstance()->ConnectToHost(
     79       ConvertJavaStringToUTF8(env, username).c_str(),
     80       ConvertJavaStringToUTF8(env, authToken).c_str(),
     81       ConvertJavaStringToUTF8(env, hostJid).c_str(),
     82       ConvertJavaStringToUTF8(env, hostId).c_str(),
     83       ConvertJavaStringToUTF8(env, hostPubkey).c_str(),
     84       ConvertJavaStringToUTF8(env, pairId).c_str(),
     85       ConvertJavaStringToUTF8(env, pairSecret).c_str());
     86 }
     87 
     88 static void Disconnect(JNIEnv* env, jclass clazz) {
     89   remoting::ChromotingJniRuntime::GetInstance()->DisconnectFromHost();
     90 }
     91 
     92 static void AuthenticationResponse(JNIEnv* env,
     93                                    jclass clazz,
     94                                    jstring pin,
     95                                    jboolean createPair) {
     96   remoting::ChromotingJniRuntime::GetInstance()->session()->ProvideSecret(
     97       ConvertJavaStringToUTF8(env, pin).c_str(), createPair);
     98 }
     99 
    100 static void ScheduleRedraw(JNIEnv* env, jclass clazz) {
    101   remoting::ChromotingJniRuntime::GetInstance()->session()->RedrawDesktop();
    102 }
    103 
    104 static void MouseAction(JNIEnv* env,
    105                         jclass clazz,
    106                         jint x,
    107                         jint y,
    108                         jint whichButton,
    109                         jboolean buttonDown) {
    110   // Button must be within the bounds of the MouseEvent_MouseButton enum.
    111   DCHECK(whichButton >= 0 && whichButton < 5);
    112 
    113   remoting::ChromotingJniRuntime::GetInstance()->session()->PerformMouseAction(
    114       x,
    115       y,
    116       static_cast<remoting::protocol::MouseEvent_MouseButton>(whichButton),
    117       buttonDown);
    118 }
    119 
    120 static void MouseWheelDeltaAction(JNIEnv* env,
    121                                   jclass clazz,
    122                                   jint delta_x,
    123                                   jint delta_y) {
    124   remoting::ChromotingJniRuntime::GetInstance()
    125       ->session()
    126       ->PerformMouseWheelDeltaAction(delta_x, delta_y);
    127 }
    128 
    129 static void KeyboardAction(JNIEnv* env,
    130                            jclass clazz,
    131                            jint keyCode,
    132                            jboolean keyDown) {
    133   remoting::ChromotingJniRuntime::GetInstance()
    134       ->session()
    135       ->PerformKeyboardAction(keyCode, keyDown);
    136 }
    137 
    138 // ChromotingJniRuntime implementation.
    139 
    140 // static
    141 ChromotingJniRuntime* ChromotingJniRuntime::GetInstance() {
    142   return Singleton<ChromotingJniRuntime>::get();
    143 }
    144 
    145 ChromotingJniRuntime::ChromotingJniRuntime() {
    146   at_exit_manager_.reset(new base::AtExitManager());
    147 
    148   // On Android, the UI thread is managed by Java, so we need to attach and
    149   // start a special type of message loop to allow Chromium code to run tasks.
    150   ui_loop_.reset(new base::MessageLoopForUI());
    151   ui_loop_->Start();
    152 
    153   // TODO(solb) Stop pretending to control the managed UI thread's lifetime.
    154   ui_task_runner_ = new AutoThreadTaskRunner(ui_loop_->message_loop_proxy(),
    155                                              base::MessageLoop::QuitClosure());
    156   network_task_runner_ = AutoThread::CreateWithType("native_net",
    157                                                     ui_task_runner_,
    158                                                     base::MessageLoop::TYPE_IO);
    159   display_task_runner_ = AutoThread::Create("native_disp",
    160                                             ui_task_runner_);
    161 
    162   url_requester_ = new URLRequestContextGetter(network_task_runner_);
    163 
    164   // Allows later decoding of video frames.
    165   media::InitializeCPUSpecificYUVConversions();
    166 }
    167 
    168 ChromotingJniRuntime::~ChromotingJniRuntime() {
    169   // The singleton should only ever be destroyed on the main thread.
    170   DCHECK(ui_task_runner_->BelongsToCurrentThread());
    171 
    172   // The session must be shut down first, since it depends on our other
    173   // components' still being alive.
    174   DisconnectFromHost();
    175 
    176   base::WaitableEvent done_event(false, false);
    177   network_task_runner_->PostTask(FROM_HERE, base::Bind(
    178       &ChromotingJniRuntime::DetachFromVmAndSignal,
    179       base::Unretained(this),
    180       &done_event));
    181   done_event.Wait();
    182   display_task_runner_->PostTask(FROM_HERE, base::Bind(
    183       &ChromotingJniRuntime::DetachFromVmAndSignal,
    184       base::Unretained(this),
    185       &done_event));
    186   done_event.Wait();
    187   base::android::DetachFromVM();
    188 }
    189 
    190 void ChromotingJniRuntime::ConnectToHost(const char* username,
    191                                   const char* auth_token,
    192                                   const char* host_jid,
    193                                   const char* host_id,
    194                                   const char* host_pubkey,
    195                                   const char* pairing_id,
    196                                   const char* pairing_secret) {
    197   DCHECK(ui_task_runner_->BelongsToCurrentThread());
    198   DCHECK(!session_);
    199   session_ = new ChromotingJniInstance(this,
    200                                        username,
    201                                        auth_token,
    202                                        host_jid,
    203                                        host_id,
    204                                        host_pubkey,
    205                                        pairing_id,
    206                                        pairing_secret);
    207 }
    208 
    209 void ChromotingJniRuntime::DisconnectFromHost() {
    210   DCHECK(ui_task_runner_->BelongsToCurrentThread());
    211   if (session_) {
    212     session_->Cleanup();
    213     session_ = NULL;
    214   }
    215 }
    216 
    217 void ChromotingJniRuntime::ReportConnectionStatus(
    218     protocol::ConnectionToHost::State state,
    219     protocol::ErrorCode error) {
    220   DCHECK(ui_task_runner_->BelongsToCurrentThread());
    221 
    222   JNIEnv* env = base::android::AttachCurrentThread();
    223   Java_JniInterface_reportConnectionStatus(env, state, error);
    224 }
    225 
    226 void ChromotingJniRuntime::DisplayAuthenticationPrompt(bool pairing_supported) {
    227   DCHECK(ui_task_runner_->BelongsToCurrentThread());
    228 
    229   JNIEnv* env = base::android::AttachCurrentThread();
    230   Java_JniInterface_displayAuthenticationPrompt(env, pairing_supported);
    231 }
    232 
    233 void ChromotingJniRuntime::CommitPairingCredentials(const std::string& host,
    234                                                     const std::string& id,
    235                                                     const std::string& secret) {
    236   DCHECK(ui_task_runner_->BelongsToCurrentThread());
    237 
    238   JNIEnv* env = base::android::AttachCurrentThread();
    239   ScopedJavaLocalRef<jstring> j_host = ConvertUTF8ToJavaString(env, host);
    240   ScopedJavaLocalRef<jbyteArray> j_id = ToJavaByteArray(
    241       env, reinterpret_cast<const uint8*>(id.data()), id.size());
    242   ScopedJavaLocalRef<jbyteArray> j_secret = ToJavaByteArray(
    243       env, reinterpret_cast<const uint8*>(secret.data()), secret.size());
    244 
    245   Java_JniInterface_commitPairingCredentials(
    246       env, j_host.obj(), j_id.obj(), j_secret.obj());
    247 }
    248 
    249 base::android::ScopedJavaLocalRef<jobject> ChromotingJniRuntime::NewBitmap(
    250     webrtc::DesktopSize size) {
    251   JNIEnv* env = base::android::AttachCurrentThread();
    252   return Java_JniInterface_newBitmap(env, size.width(), size.height());
    253 }
    254 
    255 void ChromotingJniRuntime::UpdateFrameBitmap(jobject bitmap) {
    256   DCHECK(display_task_runner_->BelongsToCurrentThread());
    257 
    258   JNIEnv* env = base::android::AttachCurrentThread();
    259   Java_JniInterface_setVideoFrame(env, bitmap);
    260 }
    261 
    262 void ChromotingJniRuntime::UpdateCursorShape(
    263     const protocol::CursorShapeInfo& cursor_shape) {
    264   DCHECK(display_task_runner_->BelongsToCurrentThread());
    265 
    266   // const_cast<> is safe as long as the Java updateCursorShape() method copies
    267   // the data out of the buffer without mutating it, and doesn't keep any
    268   // reference to the buffer afterwards. Unfortunately, there seems to be no way
    269   // to create a read-only ByteBuffer from a pointer-to-const.
    270   char* data = string_as_array(const_cast<std::string*>(&cursor_shape.data()));
    271   int cursor_total_bytes =
    272       cursor_shape.width() * cursor_shape.height() * kBytesPerPixel;
    273 
    274   JNIEnv* env = base::android::AttachCurrentThread();
    275   base::android::ScopedJavaLocalRef<jobject> buffer(env,
    276       env->NewDirectByteBuffer(data, cursor_total_bytes));
    277   Java_JniInterface_updateCursorShape(env,
    278                                       cursor_shape.width(),
    279                                       cursor_shape.height(),
    280                                       cursor_shape.hotspot_x(),
    281                                       cursor_shape.hotspot_y(),
    282                                       buffer.obj());
    283 }
    284 
    285 void ChromotingJniRuntime::RedrawCanvas() {
    286   DCHECK(display_task_runner_->BelongsToCurrentThread());
    287 
    288   JNIEnv* env = base::android::AttachCurrentThread();
    289   Java_JniInterface_redrawGraphicsInternal(env);
    290 }
    291 
    292 void ChromotingJniRuntime::DetachFromVmAndSignal(base::WaitableEvent* waiter) {
    293   base::android::DetachFromVM();
    294   waiter->Signal();
    295 }
    296 
    297 }  // namespace remoting
    298