• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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