|  | // Copyright 2013 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/android/java_handler_thread.h" | 
|  |  | 
|  | #include <jni.h> | 
|  |  | 
|  | #include "base/android/jni_android.h" | 
|  | #include "base/android/jni_string.h" | 
|  | #include "base/bind.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "base/synchronization/waitable_event.h" | 
|  | #include "base/threading/platform_thread_internal_posix.h" | 
|  | #include "base/threading/thread_id_name_manager.h" | 
|  | #include "base/threading/thread_restrictions.h" | 
|  | #include "jni/JavaHandlerThread_jni.h" | 
|  | #include "starboard/types.h" | 
|  |  | 
|  | using base::android::AttachCurrentThread; | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | namespace android { | 
|  |  | 
|  | JavaHandlerThread::JavaHandlerThread(const char* name, | 
|  | base::ThreadPriority priority) | 
|  | : JavaHandlerThread( | 
|  | name, | 
|  | Java_JavaHandlerThread_create( | 
|  | AttachCurrentThread(), | 
|  | ConvertUTF8ToJavaString(AttachCurrentThread(), name), | 
|  | base::internal::ThreadPriorityToNiceValue(priority))) {} | 
|  |  | 
|  | JavaHandlerThread::JavaHandlerThread( | 
|  | const char* name, | 
|  | const base::android::ScopedJavaLocalRef<jobject>& obj) | 
|  | : name_(name), java_thread_(obj) {} | 
|  |  | 
|  | JavaHandlerThread::~JavaHandlerThread() { | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  | DCHECK(!Java_JavaHandlerThread_isAlive(env, java_thread_)); | 
|  | DCHECK(!message_loop_ || message_loop_->IsAborted()); | 
|  | // TODO(mthiesse): We shouldn't leak the MessageLoop as this could affect | 
|  | // future tests. | 
|  | if (message_loop_ && message_loop_->IsAborted()) { | 
|  | // When the message loop has been aborted due to a crash, we intentionally | 
|  | // leak the message loop because the message loop hasn't been shut down | 
|  | // properly and would trigger DCHECKS. This should only happen in tests, | 
|  | // where we handle the exception instead of letting it take down the | 
|  | // process. | 
|  | message_loop_.release(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void JavaHandlerThread::Start() { | 
|  | // Check the thread has not already been started. | 
|  | DCHECK(!message_loop_); | 
|  |  | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  | base::WaitableEvent initialize_event( | 
|  | WaitableEvent::ResetPolicy::AUTOMATIC, | 
|  | WaitableEvent::InitialState::NOT_SIGNALED); | 
|  | Java_JavaHandlerThread_startAndInitialize( | 
|  | env, java_thread_, reinterpret_cast<intptr_t>(this), | 
|  | reinterpret_cast<intptr_t>(&initialize_event)); | 
|  | // Wait for thread to be initialized so it is ready to be used when Start | 
|  | // returns. | 
|  | base::ThreadRestrictions::ScopedAllowWait wait_allowed; | 
|  | initialize_event.Wait(); | 
|  | } | 
|  |  | 
|  | void JavaHandlerThread::Stop() { | 
|  | DCHECK(!task_runner()->BelongsToCurrentThread()); | 
|  | task_runner()->PostTask( | 
|  | FROM_HERE, | 
|  | base::BindOnce(&JavaHandlerThread::StopOnThread, base::Unretained(this))); | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  | Java_JavaHandlerThread_joinThread(env, java_thread_); | 
|  | } | 
|  |  | 
|  | void JavaHandlerThread::InitializeThread(JNIEnv* env, | 
|  | const JavaParamRef<jobject>& obj, | 
|  | jlong event) { | 
|  | base::ThreadIdNameManager::GetInstance()->RegisterThread( | 
|  | base::PlatformThread::CurrentHandle().platform_handle(), | 
|  | base::PlatformThread::CurrentId()); | 
|  |  | 
|  | if (name_) | 
|  | PlatformThread::SetName(name_); | 
|  |  | 
|  | // TYPE_JAVA to get the Android java style message loop. | 
|  | message_loop_ = | 
|  | std::make_unique<MessageLoopForUI>(base::MessageLoop::TYPE_JAVA); | 
|  | Init(); | 
|  | reinterpret_cast<base::WaitableEvent*>(event)->Signal(); | 
|  | } | 
|  |  | 
|  | void JavaHandlerThread::OnLooperStopped(JNIEnv* env, | 
|  | const JavaParamRef<jobject>& obj) { | 
|  | DCHECK(task_runner()->BelongsToCurrentThread()); | 
|  | message_loop_.reset(); | 
|  |  | 
|  | CleanUp(); | 
|  |  | 
|  | base::ThreadIdNameManager::GetInstance()->RemoveName( | 
|  | base::PlatformThread::CurrentHandle().platform_handle(), | 
|  | base::PlatformThread::CurrentId()); | 
|  | } | 
|  |  | 
|  | void JavaHandlerThread::StopMessageLoopForTesting() { | 
|  | DCHECK(task_runner()->BelongsToCurrentThread()); | 
|  | StopOnThread(); | 
|  | } | 
|  |  | 
|  | void JavaHandlerThread::JoinForTesting() { | 
|  | DCHECK(!task_runner()->BelongsToCurrentThread()); | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  | Java_JavaHandlerThread_joinThread(env, java_thread_); | 
|  | } | 
|  |  | 
|  | void JavaHandlerThread::ListenForUncaughtExceptionsForTesting() { | 
|  | DCHECK(!task_runner()->BelongsToCurrentThread()); | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  | Java_JavaHandlerThread_listenForUncaughtExceptionsForTesting(env, | 
|  | java_thread_); | 
|  | } | 
|  |  | 
|  | ScopedJavaLocalRef<jthrowable> JavaHandlerThread::GetUncaughtExceptionIfAny() { | 
|  | DCHECK(!task_runner()->BelongsToCurrentThread()); | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  | return Java_JavaHandlerThread_getUncaughtExceptionIfAny(env, java_thread_); | 
|  | } | 
|  |  | 
|  | void JavaHandlerThread::StopOnThread() { | 
|  | DCHECK(task_runner()->BelongsToCurrentThread()); | 
|  | message_loop_->QuitWhenIdle(base::BindOnce( | 
|  | &JavaHandlerThread::QuitThreadSafely, base::Unretained(this))); | 
|  | } | 
|  |  | 
|  | void JavaHandlerThread::QuitThreadSafely() { | 
|  | DCHECK(task_runner()->BelongsToCurrentThread()); | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  | Java_JavaHandlerThread_quitThreadSafely(env, java_thread_, | 
|  | reinterpret_cast<intptr_t>(this)); | 
|  | } | 
|  |  | 
|  | } // namespace android | 
|  | } // namespace base |