1 // Copyright (c) 2012 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/message_loop/message_pump_android.h"
6
7 #include <jni.h>
8
9 #include "base/android/java_message_handler_factory.h"
10 #include "base/android/jni_android.h"
11 #include "base/android/scoped_java_ref.h"
12 #include "base/lazy_instance.h"
13 #include "base/logging.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/run_loop.h"
16 #include "base/time/time.h"
17 #include "jni/SystemMessageHandler_jni.h"
18
19 using base::android::JavaParamRef;
20 using base::android::ScopedJavaLocalRef;
21
22 // ----------------------------------------------------------------------------
23 // Native JNI methods called by Java.
24 // ----------------------------------------------------------------------------
25 // This method can not move to anonymous namespace as it has been declared as
26 // 'static' in system_message_handler_jni.h.
DoRunLoopOnce(JNIEnv * env,const JavaParamRef<jobject> & obj,jlong native_delegate,jlong native_message_pump,jlong delayed_scheduled_time_ticks)27 static void DoRunLoopOnce(JNIEnv* env,
28 const JavaParamRef<jobject>& obj,
29 jlong native_delegate,
30 jlong native_message_pump,
31 jlong delayed_scheduled_time_ticks) {
32 base::MessagePump::Delegate* delegate =
33 reinterpret_cast<base::MessagePump::Delegate*>(native_delegate);
34 DCHECK(delegate);
35 base::MessagePumpForUI* pump =
36 reinterpret_cast<base::MessagePumpForUI*>(native_message_pump);
37 DCHECK(pump);
38 // This is based on MessagePumpForUI::DoRunLoop() from desktop.
39 // Note however that our system queue is handled in the java side.
40 // In desktop we inspect and process a single system message and then
41 // we call DoWork() / DoDelayedWork().
42 // On Android, the java message queue may contain messages for other handlers
43 // that will be processed before calling here again.
44 bool did_work = delegate->DoWork();
45 if (pump->ShouldAbort()) {
46 // There is a pending JNI exception, return to Java so that the exception is
47 // thrown correctly.
48 return;
49 }
50
51 // In the java side, |SystemMessageHandler| keeps a single "delayed" message.
52 // It's an expensive operation to |removeMessage| there, so this is optimized
53 // to avoid those calls.
54 //
55 // At this stage, |next_delayed_work_time| can be:
56 // 1) The same as previously scheduled: nothing to be done, move along. This
57 // is the typical case, since this method is called for every single message.
58 //
59 // 2) Not previously scheduled: just post a new message in java.
60 //
61 // 3) Shorter than previously scheduled: far less common. In this case,
62 // |removeMessage| and post a new one.
63 //
64 // 4) Longer than previously scheduled (or null): nothing to be done, move
65 // along.
66 //
67 // Side note: base::TimeTicks is a C++ representation and can't be
68 // compared in java. When calling |scheduleDelayedWork|, pass the
69 // |InternalValue()| to java and then back to C++ so the comparisons can be
70 // done here.
71 // This roundtrip allows comparing TimeTicks directly (cheap) and
72 // avoid comparisons with TimeDelta / Now() (expensive).
73 base::TimeTicks next_delayed_work_time;
74 did_work |= delegate->DoDelayedWork(&next_delayed_work_time);
75 if (pump->ShouldAbort()) {
76 // There is a pending JNI exception, return to Java so that the exception is
77 // thrown correctly
78 return;
79 }
80
81 if (!next_delayed_work_time.is_null()) {
82 // Schedule a new message if there's nothing already scheduled or there's a
83 // shorter delay than previously scheduled (see (2) and (3) above).
84 if (delayed_scheduled_time_ticks == 0 ||
85 next_delayed_work_time < base::TimeTicks::FromInternalValue(
86 delayed_scheduled_time_ticks)) {
87 Java_SystemMessageHandler_scheduleDelayedWork(env, obj,
88 next_delayed_work_time.ToInternalValue(),
89 (next_delayed_work_time -
90 base::TimeTicks::Now()).InMillisecondsRoundedUp());
91 }
92 }
93
94 // This is a major difference between android and other platforms: since we
95 // can't inspect it and process just one single message, instead we'll yeld
96 // the callstack.
97 if (did_work)
98 return;
99
100 delegate->DoIdleWork();
101 // Note that we do not check whether we should abort here since we are
102 // returning to the JVM anyway. If, in the future, we add any more code after
103 // the call to DoIdleWork() here, we should add an abort-check and return
104 // immediately if the check passes.
105 }
106
107 namespace base {
108
MessagePumpForUI()109 MessagePumpForUI::MessagePumpForUI()
110 : run_loop_(nullptr), should_abort_(false) {}
111
~MessagePumpForUI()112 MessagePumpForUI::~MessagePumpForUI() {
113 }
114
Run(Delegate * delegate)115 void MessagePumpForUI::Run(Delegate* delegate) {
116 NOTREACHED() << "UnitTests should rely on MessagePumpForUIStub in"
117 " test_stub_android.h";
118 }
119
StartInternal()120 JNIEnv* MessagePumpForUI::StartInternal() {
121 run_loop_ = new RunLoop();
122 // Since the RunLoop was just created above, BeforeRun should be guaranteed to
123 // return true (it only returns false if the RunLoop has been Quit already).
124 if (!run_loop_->BeforeRun())
125 NOTREACHED();
126
127 DCHECK(system_message_handler_obj_.is_null());
128
129 JNIEnv* env = base::android::AttachCurrentThread();
130 DCHECK(env);
131 return env;
132 }
133
Start(Delegate * delegate)134 void MessagePumpForUI::Start(Delegate* delegate) {
135 JNIEnv* env = StartInternal();
136 system_message_handler_obj_.Reset(Java_SystemMessageHandler_create(
137 env, reinterpret_cast<intptr_t>(delegate),
138 reinterpret_cast<intptr_t>(this)));
139 }
140
StartForUnitTest(Delegate * delegate,base::android::JavaMessageHandlerFactory * factory,WaitableEvent * test_done_event)141 void MessagePumpForUI::StartForUnitTest(
142 Delegate* delegate,
143 base::android::JavaMessageHandlerFactory* factory,
144 WaitableEvent* test_done_event) {
145 JNIEnv* env = StartInternal();
146 system_message_handler_obj_.Reset(
147 factory->CreateMessageHandler(env, delegate, this, test_done_event));
148 }
149
Quit()150 void MessagePumpForUI::Quit() {
151 if (!system_message_handler_obj_.is_null()) {
152 JNIEnv* env = base::android::AttachCurrentThread();
153 DCHECK(env);
154
155 Java_SystemMessageHandler_removeAllPendingMessages(
156 env, system_message_handler_obj_);
157 system_message_handler_obj_.Reset();
158 }
159
160 if (run_loop_) {
161 run_loop_->AfterRun();
162 delete run_loop_;
163 run_loop_ = NULL;
164 }
165 }
166
ScheduleWork()167 void MessagePumpForUI::ScheduleWork() {
168 DCHECK(!system_message_handler_obj_.is_null());
169
170 JNIEnv* env = base::android::AttachCurrentThread();
171 DCHECK(env);
172
173 Java_SystemMessageHandler_scheduleWork(env, system_message_handler_obj_);
174 }
175
ScheduleDelayedWork(const TimeTicks & delayed_work_time)176 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
177 DCHECK(!system_message_handler_obj_.is_null());
178
179 JNIEnv* env = base::android::AttachCurrentThread();
180 DCHECK(env);
181
182 jlong millis =
183 (delayed_work_time - TimeTicks::Now()).InMillisecondsRoundedUp();
184 // Note that we're truncating to milliseconds as required by the java side,
185 // even though delayed_work_time is microseconds resolution.
186 Java_SystemMessageHandler_scheduleDelayedWork(
187 env, system_message_handler_obj_, delayed_work_time.ToInternalValue(),
188 millis);
189 }
190
191 // static
RegisterBindings(JNIEnv * env)192 bool MessagePumpForUI::RegisterBindings(JNIEnv* env) {
193 return RegisterNativesImpl(env);
194 }
195
196 } // namespace base
197