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 "base/android/java_handler_thread.h"
6
7 #include <jni.h>
8
9 #include "base/android/jni_android.h"
10 #include "base/android/jni_string.h"
11 #include "base/bind.h"
12 #include "base/run_loop.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/threading/platform_thread_internal_posix.h"
15 #include "base/threading/thread_restrictions.h"
16 #include "jni/JavaHandlerThread_jni.h"
17
18 using base::android::AttachCurrentThread;
19
20 namespace base {
21
22 namespace android {
23
JavaHandlerThread(const char * name,base::ThreadPriority priority)24 JavaHandlerThread::JavaHandlerThread(const char* name,
25 base::ThreadPriority priority)
26 : JavaHandlerThread(Java_JavaHandlerThread_create(
27 AttachCurrentThread(),
28 ConvertUTF8ToJavaString(AttachCurrentThread(), name),
29 base::internal::ThreadPriorityToNiceValue(priority))) {}
30
JavaHandlerThread(const base::android::ScopedJavaLocalRef<jobject> & obj)31 JavaHandlerThread::JavaHandlerThread(
32 const base::android::ScopedJavaLocalRef<jobject>& obj)
33 : java_thread_(obj) {}
34
~JavaHandlerThread()35 JavaHandlerThread::~JavaHandlerThread() {
36 JNIEnv* env = base::android::AttachCurrentThread();
37 DCHECK(!Java_JavaHandlerThread_isAlive(env, java_thread_));
38 DCHECK(!message_loop_ || message_loop_->IsAborted());
39 // TODO(mthiesse): We shouldn't leak the MessageLoop as this could affect
40 // future tests.
41 if (message_loop_ && message_loop_->IsAborted()) {
42 // When the message loop has been aborted due to a crash, we intentionally
43 // leak the message loop because the message loop hasn't been shut down
44 // properly and would trigger DCHECKS. This should only happen in tests,
45 // where we handle the exception instead of letting it take down the
46 // process.
47 message_loop_.release();
48 }
49 }
50
Start()51 void JavaHandlerThread::Start() {
52 // Check the thread has not already been started.
53 DCHECK(!message_loop_);
54
55 JNIEnv* env = base::android::AttachCurrentThread();
56 base::WaitableEvent initialize_event(
57 WaitableEvent::ResetPolicy::AUTOMATIC,
58 WaitableEvent::InitialState::NOT_SIGNALED);
59 Java_JavaHandlerThread_startAndInitialize(
60 env, java_thread_, reinterpret_cast<intptr_t>(this),
61 reinterpret_cast<intptr_t>(&initialize_event));
62 // Wait for thread to be initialized so it is ready to be used when Start
63 // returns.
64 base::ThreadRestrictions::ScopedAllowWait wait_allowed;
65 initialize_event.Wait();
66 }
67
Stop()68 void JavaHandlerThread::Stop() {
69 DCHECK(!task_runner()->BelongsToCurrentThread());
70 task_runner()->PostTask(
71 FROM_HERE,
72 base::BindOnce(&JavaHandlerThread::StopOnThread, base::Unretained(this)));
73 JNIEnv* env = base::android::AttachCurrentThread();
74 Java_JavaHandlerThread_joinThread(env, java_thread_);
75 }
76
InitializeThread(JNIEnv * env,const JavaParamRef<jobject> & obj,jlong event)77 void JavaHandlerThread::InitializeThread(JNIEnv* env,
78 const JavaParamRef<jobject>& obj,
79 jlong event) {
80 // TYPE_JAVA to get the Android java style message loop.
81 message_loop_ =
82 std::make_unique<MessageLoopForUI>(base::MessageLoop::TYPE_JAVA);
83 Init();
84 reinterpret_cast<base::WaitableEvent*>(event)->Signal();
85 }
86
OnLooperStopped(JNIEnv * env,const JavaParamRef<jobject> & obj)87 void JavaHandlerThread::OnLooperStopped(JNIEnv* env,
88 const JavaParamRef<jobject>& obj) {
89 DCHECK(task_runner()->BelongsToCurrentThread());
90 message_loop_.reset();
91 CleanUp();
92 }
93
StopMessageLoopForTesting()94 void JavaHandlerThread::StopMessageLoopForTesting() {
95 DCHECK(task_runner()->BelongsToCurrentThread());
96 StopOnThread();
97 }
98
JoinForTesting()99 void JavaHandlerThread::JoinForTesting() {
100 DCHECK(!task_runner()->BelongsToCurrentThread());
101 JNIEnv* env = base::android::AttachCurrentThread();
102 Java_JavaHandlerThread_joinThread(env, java_thread_);
103 }
104
ListenForUncaughtExceptionsForTesting()105 void JavaHandlerThread::ListenForUncaughtExceptionsForTesting() {
106 DCHECK(!task_runner()->BelongsToCurrentThread());
107 JNIEnv* env = base::android::AttachCurrentThread();
108 Java_JavaHandlerThread_listenForUncaughtExceptionsForTesting(env,
109 java_thread_);
110 }
111
GetUncaughtExceptionIfAny()112 ScopedJavaLocalRef<jthrowable> JavaHandlerThread::GetUncaughtExceptionIfAny() {
113 DCHECK(!task_runner()->BelongsToCurrentThread());
114 JNIEnv* env = base::android::AttachCurrentThread();
115 return Java_JavaHandlerThread_getUncaughtExceptionIfAny(env, java_thread_);
116 }
117
StopOnThread()118 void JavaHandlerThread::StopOnThread() {
119 DCHECK(task_runner()->BelongsToCurrentThread());
120 message_loop_->QuitWhenIdle(base::BindOnce(
121 &JavaHandlerThread::QuitThreadSafely, base::Unretained(this)));
122 }
123
QuitThreadSafely()124 void JavaHandlerThread::QuitThreadSafely() {
125 DCHECK(task_runner()->BelongsToCurrentThread());
126 JNIEnv* env = base::android::AttachCurrentThread();
127 Java_JavaHandlerThread_quitThreadSafely(env, java_thread_,
128 reinterpret_cast<intptr_t>(this));
129 }
130
131 } // namespace android
132 } // namespace base
133