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/base_jni_registrar.h" 8 #include "base/android/jni_android.h" 9 #include "base/memory/singleton.h" 10 #include "base/synchronization/waitable_event.h" 11 #include "media/base/yuv_convert.h" 12 #include "net/android/net_jni_registrar.h" 13 #include "remoting/base/url_request_context.h" 14 15 // Class and package name of the Java class supporting the methods we call. 16 const char* const kJavaClass = "org/chromium/chromoting/jni/JniInterface"; 17 18 namespace remoting { 19 20 // static 21 ChromotingJniRuntime* ChromotingJniRuntime::GetInstance() { 22 return Singleton<ChromotingJniRuntime>::get(); 23 } 24 25 ChromotingJniRuntime::ChromotingJniRuntime() { 26 // Obtain a reference to the Java environment. (Future calls to this function 27 // made from the same thread return the same stored reference instead of 28 // repeating the work of attaching to the JVM.) 29 JNIEnv* env = base::android::AttachCurrentThread(); 30 31 // The base and networks stacks must be registered with JNI in order to work 32 // on Android. An AtExitManager cleans this up at process exit. 33 at_exit_manager_.reset(new base::AtExitManager()); 34 base::android::RegisterJni(env); 35 net::android::RegisterJni(env); 36 37 // On Android, the UI thread is managed by Java, so we need to attach and 38 // start a special type of message loop to allow Chromium code to run tasks. 39 LOG(INFO) << "Starting main message loop"; 40 ui_loop_.reset(new base::MessageLoopForUI()); 41 ui_loop_->Start(); 42 43 LOG(INFO) << "Spawning additional threads"; 44 // TODO(solb) Stop pretending to control the managed UI thread's lifetime. 45 ui_task_runner_ = new AutoThreadTaskRunner(ui_loop_->message_loop_proxy(), 46 base::MessageLoop::QuitClosure()); 47 network_task_runner_ = AutoThread::CreateWithType("native_net", 48 ui_task_runner_, 49 base::MessageLoop::TYPE_IO); 50 display_task_runner_ = AutoThread::Create("native_disp", 51 ui_task_runner_); 52 53 url_requester_ = new URLRequestContextGetter(ui_task_runner_, 54 network_task_runner_); 55 56 // Allows later decoding of video frames. 57 media::InitializeCPUSpecificYUVConversions(); 58 59 class_ = static_cast<jclass>(env->NewGlobalRef(env->FindClass(kJavaClass))); 60 } 61 62 ChromotingJniRuntime::~ChromotingJniRuntime() { 63 // The singleton should only ever be destroyed on the main thread. 64 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 65 66 // The session must be shut down first, since it depends on our other 67 // components' still being alive. 68 DisconnectFromHost(); 69 70 JNIEnv* env = base::android::AttachCurrentThread(); 71 env->DeleteGlobalRef(class_); 72 73 base::WaitableEvent done_event(false, false); 74 network_task_runner_->PostTask(FROM_HERE, base::Bind( 75 &ChromotingJniRuntime::DetachFromVmAndSignal, 76 base::Unretained(this), 77 &done_event)); 78 done_event.Wait(); 79 display_task_runner_->PostTask(FROM_HERE, base::Bind( 80 &ChromotingJniRuntime::DetachFromVmAndSignal, 81 base::Unretained(this), 82 &done_event)); 83 done_event.Wait(); 84 base::android::DetachFromVM(); 85 } 86 87 void ChromotingJniRuntime::ConnectToHost(const char* username, 88 const char* auth_token, 89 const char* host_jid, 90 const char* host_id, 91 const char* host_pubkey, 92 const char* pairing_id, 93 const char* pairing_secret) { 94 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 95 DCHECK(!session_); 96 session_ = new ChromotingJniInstance(this, 97 username, 98 auth_token, 99 host_jid, 100 host_id, 101 host_pubkey, 102 pairing_id, 103 pairing_secret); 104 } 105 106 void ChromotingJniRuntime::DisconnectFromHost() { 107 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 108 if (session_) { 109 session_->Cleanup(); 110 session_ = NULL; 111 } 112 } 113 114 void ChromotingJniRuntime::ReportConnectionStatus( 115 protocol::ConnectionToHost::State state, 116 protocol::ErrorCode error) { 117 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 118 119 JNIEnv* env = base::android::AttachCurrentThread(); 120 env->CallStaticVoidMethod( 121 class_, 122 env->GetStaticMethodID(class_, "reportConnectionStatus", "(II)V"), 123 state, 124 error); 125 } 126 127 void ChromotingJniRuntime::DisplayAuthenticationPrompt() { 128 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 129 130 JNIEnv* env = base::android::AttachCurrentThread(); 131 env->CallStaticVoidMethod( 132 class_, 133 env->GetStaticMethodID(class_, "displayAuthenticationPrompt", "()V")); 134 } 135 136 void ChromotingJniRuntime::CommitPairingCredentials(const std::string& host, 137 const std::string& id, 138 const std::string& secret) { 139 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 140 141 JNIEnv* env = base::android::AttachCurrentThread(); 142 jstring host_jstr = env->NewStringUTF(host.c_str()); 143 jbyteArray id_arr = env->NewByteArray(id.size()); 144 env->SetByteArrayRegion(id_arr, 0, id.size(), 145 reinterpret_cast<const jbyte*>(id.c_str())); 146 jbyteArray secret_arr = env->NewByteArray(secret.size()); 147 env->SetByteArrayRegion(secret_arr, 0, secret.size(), 148 reinterpret_cast<const jbyte*>(secret.c_str())); 149 150 env->CallStaticVoidMethod( 151 class_, 152 env->GetStaticMethodID( 153 class_, 154 "commitPairingCredentials", 155 "(Ljava/lang/String;[B[B)V"), 156 host_jstr, 157 id_arr, 158 secret_arr); 159 160 // Because we passed them as arguments, their corresponding Java objects were 161 // GCd as soon as the managed method returned, so we mustn't release it here. 162 } 163 164 void ChromotingJniRuntime::UpdateImageBuffer(int width, 165 int height, 166 jobject buffer) { 167 DCHECK(display_task_runner_->BelongsToCurrentThread()); 168 169 JNIEnv* env = base::android::AttachCurrentThread(); 170 env->SetStaticIntField( 171 class_, 172 env->GetStaticFieldID(class_, "sWidth", "I"), 173 width); 174 env->SetStaticIntField( 175 class_, 176 env->GetStaticFieldID(class_, "sHeight", "I"), 177 height); 178 env->SetStaticObjectField( 179 class_, 180 env->GetStaticFieldID(class_, "sBuffer", "Ljava/nio/ByteBuffer;"), 181 buffer); 182 } 183 184 void ChromotingJniRuntime::RedrawCanvas() { 185 DCHECK(display_task_runner_->BelongsToCurrentThread()); 186 187 JNIEnv* env = base::android::AttachCurrentThread(); 188 env->CallStaticVoidMethod( 189 class_, 190 env->GetStaticMethodID(class_, "redrawGraphicsInternal", "()V")); 191 } 192 193 void ChromotingJniRuntime::DetachFromVmAndSignal(base::WaitableEvent* waiter) { 194 base::android::DetachFromVM(); 195 waiter->Signal(); 196 } 197 198 } // namespace remoting 199