blob: 7610675ce4c09c6a344ec07f4db3593f4a738102 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2012 The Chromium Authors
[email protected]61c86c62011-08-02 16:11:162// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]59e69e742013-06-18 20:27:525#include "base/message_loop/message_pump_android.h"
[email protected]61c86c62011-08-02 16:11:166
Michael Thiessend7ae7352018-07-10 00:57:137#include <android/looper.h>
8#include <errno.h>
9#include <fcntl.h>
[email protected]61c86c62011-08-02 16:11:1610#include <jni.h>
Michael Thiessend7ae7352018-07-10 00:57:1311#include <sys/eventfd.h>
Egor Pasko6c9baa332023-04-04 18:08:2212#include <sys/timerfd.h>
Michael Thiessend7ae7352018-07-10 00:57:1313#include <sys/types.h>
14#include <unistd.h>
15#include <utility>
[email protected]61c86c62011-08-02 16:11:1616
Egor Pasko16859192024-03-11 12:20:5617#include "base/android/input_hint_checker.h"
[email protected]61c86c62011-08-02 16:11:1618#include "base/android/jni_android.h"
[email protected]449019ca2012-03-14 22:17:0019#include "base/android/scoped_java_ref.h"
Hans Wennborgc3cffa62020-04-27 10:09:1220#include "base/check_op.h"
Hans Wennborgc3cffa62020-04-27 10:09:1221#include "base/notreached.h"
Peter Kasting2f61c8b2022-07-19 23:43:4622#include "base/numerics/safe_conversions.h"
[email protected]8e937c1e2012-06-28 22:57:3023#include "base/run_loop.h"
Egor Pasko16859192024-03-11 12:20:5624#include "base/time/time.h"
Michael Thiessen6fd43902018-08-30 23:14:1525#include "build/build_config.h"
Michael Thiessend7ae7352018-07-10 00:57:1326
Michael Thiessen781ddeb2017-11-15 17:07:2327namespace base {
28
Michael Thiessend7ae7352018-07-10 00:57:1329namespace {
Michael Thiessen781ddeb2017-11-15 17:07:2330
Michael Thiessen6fd43902018-08-30 23:14:1531// https://crbug.com/873588. The stack may not be aligned when the ALooper calls
32// into our code due to the inconsistent ABI on older Android OS versions.
33#if defined(ARCH_CPU_X86)
34#define STACK_ALIGN __attribute__((force_align_arg_pointer))
35#else
36#define STACK_ALIGN
37#endif
38
39STACK_ALIGN int NonDelayedLooperCallback(int fd, int events, void* data) {
Michael Thiessend7ae7352018-07-10 00:57:1340 if (events & ALOOPER_EVENT_HANGUP)
41 return 0;
42
43 DCHECK(events & ALOOPER_EVENT_INPUT);
Egor Pasko6da5a07c2024-03-11 19:56:2944 MessagePumpAndroid* pump = reinterpret_cast<MessagePumpAndroid*>(data);
Michael Thiessend7ae7352018-07-10 00:57:1345 pump->OnNonDelayedLooperCallback();
46 return 1; // continue listening for events
47}
48
Michael Thiessen6fd43902018-08-30 23:14:1549STACK_ALIGN int DelayedLooperCallback(int fd, int events, void* data) {
Michael Thiessend7ae7352018-07-10 00:57:1350 if (events & ALOOPER_EVENT_HANGUP)
51 return 0;
52
53 DCHECK(events & ALOOPER_EVENT_INPUT);
Egor Pasko6da5a07c2024-03-11 19:56:2954 MessagePumpAndroid* pump = reinterpret_cast<MessagePumpAndroid*>(data);
Michael Thiessend7ae7352018-07-10 00:57:1355 pump->OnDelayedLooperCallback();
56 return 1; // continue listening for events
57}
58
Aaron Colwell48c4d5072020-11-13 16:45:0359// A bit added to the |non_delayed_fd_| to keep it signaled when we yield to
Gabriel Charette300f16792022-07-06 20:03:3060// native work below.
61constexpr uint64_t kTryNativeWorkBeforeIdleBit = uint64_t(1) << 32;
Michael Thiessend7ae7352018-07-10 00:57:1362} // namespace
63
Egor Pasko6da5a07c2024-03-11 19:56:2964MessagePumpAndroid::MessagePumpAndroid()
François Doray253a3062022-10-24 16:45:2965 : env_(base::android::AttachCurrentThread()) {
Michael Thiessend7ae7352018-07-10 00:57:1366 // The Android native ALooper uses epoll to poll our file descriptors and wake
67 // us up. We use a simple level-triggered eventfd to signal that non-delayed
68 // work is available, and a timerfd to signal when delayed work is ready to
69 // be run.
70 non_delayed_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
71 CHECK_NE(non_delayed_fd_, -1);
72 DCHECK_EQ(TimeTicks::GetClock(), TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
73
Peter Kasting2f61c8b2022-07-19 23:43:4674 delayed_fd_ = checked_cast<int>(
Egor Pasko47ff68432023-04-05 18:23:5775 timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC));
Michael Thiessend7ae7352018-07-10 00:57:1376 CHECK_NE(delayed_fd_, -1);
77
78 looper_ = ALooper_prepare(0);
79 DCHECK(looper_);
80 // Add a reference to the looper so it isn't deleted on us.
81 ALooper_acquire(looper_);
82 ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT,
83 &NonDelayedLooperCallback, reinterpret_cast<void*>(this));
84 ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT,
85 &DelayedLooperCallback, reinterpret_cast<void*>(this));
86}
87
Egor Pasko6da5a07c2024-03-11 19:56:2988MessagePumpAndroid::~MessagePumpAndroid() {
Michael Thiessend7ae7352018-07-10 00:57:1389 DCHECK_EQ(ALooper_forThread(), looper_);
90 ALooper_removeFd(looper_, non_delayed_fd_);
91 ALooper_removeFd(looper_, delayed_fd_);
92 ALooper_release(looper_);
93 looper_ = nullptr;
94
95 close(non_delayed_fd_);
96 close(delayed_fd_);
97}
98
Egor Pasko6da5a07c2024-03-11 19:56:2999void MessagePumpAndroid::OnDelayedLooperCallback() {
Torne (Richard Coles)c6993a032020-02-19 13:23:47100 // There may be non-Chromium callbacks on the same ALooper which may have left
101 // a pending exception set, and ALooper does not check for this between
102 // callbacks. Check here, and if there's already an exception, just skip this
103 // iteration without clearing the fd. If the exception ends up being non-fatal
104 // then we'll just get called again on the next polling iteration.
105 if (base::android::HasException(env_))
106 return;
107
Gabriel Charette9d44a9b2019-04-29 16:35:56108 // ALooper_pollOnce may call this after Quit() if OnNonDelayedLooperCallback()
109 // resulted in Quit() in the same round.
Michael Thiessend7ae7352018-07-10 00:57:13110 if (ShouldQuit())
Michael Thiessenfc7067fe2017-11-01 22:33:01111 return;
112
François Doray23159042023-03-01 14:14:09113 // Clear the fd.
114 uint64_t value;
115 long ret = read(delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54116
François Doray23159042023-03-01 14:14:09117 // TODO(mthiesse): Figure out how it's possible to hit EAGAIN here.
118 // According to http://man7.org/linux/man-pages/man2/timerfd_create.2.html
119 // EAGAIN only happens if no timer has expired. Also according to the man page
120 // poll only returns readable when a timer has expired. So this function will
121 // only be called when a timer has expired, but reading reveals no timer has
122 // expired...
123 // Quit() and ScheduleDelayedWork() are the only other functions that touch
124 // the timerfd, and they both run on the same thread as this callback, so
125 // there are no obvious timing or multi-threading related issues.
126 DPCHECK(ret >= 0 || errno == EAGAIN);
Aaron Colwell48c4d5072020-11-13 16:45:03127 DoDelayedLooperWork();
128}
Michael Thiessen7c36083d2018-08-10 20:24:54129
Egor Pasko6da5a07c2024-03-11 19:56:29130void MessagePumpAndroid::DoDelayedLooperWork() {
Gabriel Charette9d44a9b2019-04-29 16:35:56131 delayed_scheduled_time_.reset();
[email protected]61c86c62011-08-02 16:11:16132
Etienne Pierre-doray2163f3012020-04-02 21:37:14133 Delegate::NextWorkInfo next_work_info = delegate_->DoWork();
Gabriel Charette9d44a9b2019-04-29 16:35:56134
Michael Thiessend7ae7352018-07-10 00:57:13135 if (ShouldQuit())
136 return;
Gabriel Charette9d44a9b2019-04-29 16:35:56137
138 if (next_work_info.is_immediate()) {
139 ScheduleWork();
140 return;
141 }
142
143 DoIdleWork();
144 if (!next_work_info.delayed_run_time.is_max())
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33145 ScheduleDelayedWork(next_work_info);
Michael Thiessend7ae7352018-07-10 00:57:13146}
147
Egor Pasko6da5a07c2024-03-11 19:56:29148void MessagePumpAndroid::OnNonDelayedLooperCallback() {
Torne (Richard Coles)c6993a032020-02-19 13:23:47149 // There may be non-Chromium callbacks on the same ALooper which may have left
150 // a pending exception set, and ALooper does not check for this between
151 // callbacks. Check here, and if there's already an exception, just skip this
152 // iteration without clearing the fd. If the exception ends up being non-fatal
153 // then we'll just get called again on the next polling iteration.
154 if (base::android::HasException(env_))
155 return;
156
Gabriel Charette9d44a9b2019-04-29 16:35:56157 // ALooper_pollOnce may call this after Quit() if OnDelayedLooperCallback()
158 // resulted in Quit() in the same round.
Michael Thiessend7ae7352018-07-10 00:57:13159 if (ShouldQuit())
160 return;
161
Gabriel Charette9d44a9b2019-04-29 16:35:56162 // We're about to process all the work requested by ScheduleWork().
163 // MessagePump users are expected to do their best not to invoke
Etienne Pierre-doray2163f3012020-04-02 21:37:14164 // ScheduleWork() again before DoWork() returns a non-immediate
Gabriel Charette9d44a9b2019-04-29 16:35:56165 // NextWorkInfo below. Hence, capturing the file descriptor's value now and
166 // resetting its contents to 0 should be okay. The value currently stored
167 // should be greater than 0 since work having been scheduled is the reason
168 // we're here. See http://man7.org/linux/man-pages/man2/eventfd.2.html
Aaron Colwell48c4d5072020-11-13 16:45:03169 uint64_t value = 0;
Peter Kasting2f61c8b2022-07-19 23:43:46170 long ret = read(non_delayed_fd_, &value, sizeof(value));
Gabriel Charette9d44a9b2019-04-29 16:35:56171 DPCHECK(ret >= 0);
Aaron Colwell48c4d5072020-11-13 16:45:03172 DCHECK_GT(value, 0U);
Gabriel Charette300f16792022-07-06 20:03:30173 bool do_idle_work = value == kTryNativeWorkBeforeIdleBit;
Aaron Colwell48c4d5072020-11-13 16:45:03174 DoNonDelayedLooperWork(do_idle_work);
175}
Gabriel Charette9d44a9b2019-04-29 16:35:56176
Egor Pasko6da5a07c2024-03-11 19:56:29177void MessagePumpAndroid::DoNonDelayedLooperWork(bool do_idle_work) {
Aaron Colwell48c4d5072020-11-13 16:45:03178 // Note: We can't skip DoWork() even if |do_idle_work| is true here (i.e. no
179 // additional ScheduleWork() since yielding to native) as delayed tasks might
180 // have come in and we need to re-sample |next_work_info|.
Gabriel Charette9d44a9b2019-04-29 16:35:56181
182 // Runs all application tasks scheduled to run.
183 Delegate::NextWorkInfo next_work_info;
184 do {
185 if (ShouldQuit())
186 return;
187
Etienne Pierre-doray2163f3012020-04-02 21:37:14188 next_work_info = delegate_->DoWork();
Egor Pasko16859192024-03-11 12:20:56189
Stephen Nusko133b4e42021-06-01 22:06:23190 // If we are prioritizing native, and the next work would normally run
Gabriel Charette300f16792022-07-06 20:03:30191 // immediately, skip the next work and let the native work items have a
192 // chance to run. This is useful when user input is waiting for native to
193 // have a chance to run.
Stephen Nusko133b4e42021-06-01 22:06:23194 if (next_work_info.is_immediate() && next_work_info.yield_to_native) {
François Doray23159042023-03-01 14:14:09195 ScheduleWork();
Stephen Nusko133b4e42021-06-01 22:06:23196 return;
197 }
Egor Pasko16859192024-03-11 12:20:56198
199 // As an optimization, yield to the Looper when input events are waiting to
200 // be handled. In some cases input events can remain undetected. Such "input
201 // hint false negatives" happen, for example, during initialization, in
202 // multi-window cases, or when a previous value is cached to throttle
203 // polling the input channel.
204 if (is_type_ui_ && android::InputHintChecker::HasInput()) {
205 ScheduleWork();
206 return;
207 }
Gabriel Charette9d44a9b2019-04-29 16:35:56208 } while (next_work_info.is_immediate());
209
210 // Do not resignal |non_delayed_fd_| if we're quitting (this pump doesn't
211 // allow nesting so needing to resume in an outer loop is not an issue
212 // either).
213 if (ShouldQuit())
214 return;
215
Gabriel Charette300f16792022-07-06 20:03:30216 // Before declaring this loop idle, yield to native work items and arrange to
François Doray23159042023-03-01 14:14:09217 // be called again (unless we're already in that second call).
Aaron Colwell48c4d5072020-11-13 16:45:03218 if (!do_idle_work) {
219 ScheduleWorkInternal(/*do_idle_work=*/true);
Gabriel Charette9d44a9b2019-04-29 16:35:56220 return;
Michael Thiessend7ae7352018-07-10 00:57:13221 }
Gabriel Charette9d44a9b2019-04-29 16:35:56222
Gabriel Charette300f16792022-07-06 20:03:30223 // We yielded to native work items already and they didn't generate a
Gabriel Charette1c6e0a22019-04-30 11:15:14224 // ScheduleWork() request so we can declare idleness. It's possible for a
225 // ScheduleWork() request to come in racily while this method unwinds, this is
226 // fine and will merely result in it being re-invoked shortly after it
227 // returns.
228 // TODO(scheduler-dev): this doesn't account for tasks that don't ever call
229 // SchedulerWork() but still keep the system non-idle (e.g., the Java Handler
230 // API). It would be better to add an API to query the presence of native
Gabriel Charette300f16792022-07-06 20:03:30231 // tasks instead of relying on yielding once + kTryNativeWorkBeforeIdleBit.
Aaron Colwell48c4d5072020-11-13 16:45:03232 DCHECK(do_idle_work);
Gabriel Charette9d44a9b2019-04-29 16:35:56233
234 if (ShouldQuit())
235 return;
236
237 // At this point, the java looper might not be idle - it's impossible to know
238 // pre-Android-M, so we may end up doing Idle work while java tasks are still
239 // queued up. Note that this won't cause us to fail to run java tasks using
240 // QuitWhenIdle, as the JavaHandlerThread will finish running all currently
241 // scheduled tasks before it quits. Also note that we can't just add an idle
242 // callback to the java looper, as that will fire even if application tasks
243 // are still queued up.
244 DoIdleWork();
Stephen Nusko408b9a92022-09-15 10:03:57245 if (!next_work_info.delayed_run_time.is_max()) {
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33246 ScheduleDelayedWork(next_work_info);
Stephen Nusko408b9a92022-09-15 10:03:57247 }
Michael Thiessend7ae7352018-07-10 00:57:13248}
249
Egor Pasko6da5a07c2024-03-11 19:56:29250void MessagePumpAndroid::DoIdleWork() {
Michael Thiessend7ae7352018-07-10 00:57:13251 if (delegate_->DoIdleWork()) {
252 // If DoIdleWork() resulted in any work, we're not idle yet. We need to pump
253 // the loop here because we may in fact be idle after doing idle work
254 // without any new tasks being queued.
255 ScheduleWork();
256 }
[email protected]61c86c62011-08-02 16:11:16257}
258
Egor Pasko6da5a07c2024-03-11 19:56:29259void MessagePumpAndroid::Run(Delegate* delegate) {
Aaron Colwelld34116aaa2020-11-11 02:15:37260 CHECK(false) << "Unexpected call to Run()";
[email protected]61c86c62011-08-02 16:11:16261}
262
Egor Pasko6da5a07c2024-03-11 19:56:29263void MessagePumpAndroid::Attach(Delegate* delegate) {
Michael Thiessendbeca242017-08-28 21:10:08264 DCHECK(!quit_);
Michael Thiessend7ae7352018-07-10 00:57:13265
266 // Since the Looper is controlled by the UI thread or JavaHandlerThread, we
267 // can't use Run() like we do on other platforms or we would prevent Java
268 // tasks from running. Instead we create and initialize a run loop here, then
269 // return control back to the Looper.
270
271 SetDelegate(delegate);
Michael Thiessen781ddeb2017-11-15 17:07:23272 run_loop_ = std::make_unique<RunLoop>();
[email protected]8e937c1e2012-06-28 22:57:30273 // Since the RunLoop was just created above, BeforeRun should be guaranteed to
274 // return true (it only returns false if the RunLoop has been Quit already).
275 if (!run_loop_->BeforeRun())
276 NOTREACHED();
[email protected]61c86c62011-08-02 16:11:16277}
278
Egor Pasko6da5a07c2024-03-11 19:56:29279void MessagePumpAndroid::Quit() {
Michael Thiessend7ae7352018-07-10 00:57:13280 if (quit_)
281 return;
282
Michael Thiessendbeca242017-08-28 21:10:08283 quit_ = true;
Michael Thiessen781ddeb2017-11-15 17:07:23284
Michael Thiessend7ae7352018-07-10 00:57:13285 int64_t value;
286 // Clear any pending timer.
287 read(delayed_fd_, &value, sizeof(value));
288 // Clear the eventfd.
289 read(non_delayed_fd_, &value, sizeof(value));
[email protected]61c86c62011-08-02 16:11:16290
[email protected]8e937c1e2012-06-28 22:57:30291 if (run_loop_) {
292 run_loop_->AfterRun();
Michael Thiessen781ddeb2017-11-15 17:07:23293 run_loop_ = nullptr;
[email protected]61c86c62011-08-02 16:11:16294 }
Michael Thiessend7ae7352018-07-10 00:57:13295 if (on_quit_callback_) {
296 std::move(on_quit_callback_).Run();
297 }
[email protected]61c86c62011-08-02 16:11:16298}
299
Egor Pasko6da5a07c2024-03-11 19:56:29300void MessagePumpAndroid::ScheduleWork() {
Aaron Colwell48c4d5072020-11-13 16:45:03301 ScheduleWorkInternal(/*do_idle_work=*/false);
302}
303
Egor Pasko6da5a07c2024-03-11 19:56:29304void MessagePumpAndroid::ScheduleWorkInternal(bool do_idle_work) {
Aaron Colwell48c4d5072020-11-13 16:45:03305 // Write (add) |value| to the eventfd. This tells the Looper to wake up and
306 // call our callback, allowing us to run tasks. This also allows us to detect,
307 // when we clear the fd, whether additional work was scheduled after we
308 // finished performing work, but before we cleared the fd, as we'll read back
309 // >=2 instead of 1 in that case. See the eventfd man pages
Michael Thiessend7ae7352018-07-10 00:57:13310 // (http://man7.org/linux/man-pages/man2/eventfd.2.html) for details on how
311 // the read and write APIs for this file descriptor work, specifically without
312 // EFD_SEMAPHORE.
Aaron Colwell48c4d5072020-11-13 16:45:03313 // Note: Calls with |do_idle_work| set to true may race with potential calls
François Doray23159042023-03-01 14:14:09314 // where the parameter is false. This is fine as write() is adding |value|,
315 // not overwriting the existing value, and as such racing calls would merely
316 // have their values added together. Since idle work is only executed when the
317 // value read equals kTryNativeWorkBeforeIdleBit, a race would prevent idle
318 // work from being run and trigger another call to this method with
319 // |do_idle_work| set to true.
Gabriel Charette300f16792022-07-06 20:03:30320 uint64_t value = do_idle_work ? kTryNativeWorkBeforeIdleBit : 1;
Peter Kasting2f61c8b2022-07-19 23:43:46321 long ret = write(non_delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54322 DPCHECK(ret >= 0);
[email protected]61c86c62011-08-02 16:11:16323}
324
Egor Pasko6da5a07c2024-03-11 19:56:29325void MessagePumpAndroid::ScheduleDelayedWork(
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33326 const Delegate::NextWorkInfo& next_work_info) {
Michael Thiessend7ae7352018-07-10 00:57:13327 if (ShouldQuit())
Michael Thiessendbeca242017-08-28 21:10:08328 return;
Michael Thiessend7ae7352018-07-10 00:57:13329
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33330 if (delayed_scheduled_time_ &&
331 *delayed_scheduled_time_ == next_work_info.delayed_run_time) {
Michael Thiessen781ddeb2017-11-15 17:07:23332 return;
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33333 }
Michael Thiessend7ae7352018-07-10 00:57:13334
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33335 DCHECK(!next_work_info.is_immediate());
336 delayed_scheduled_time_ = next_work_info.delayed_run_time;
François Doray23159042023-03-01 14:14:09337 int64_t nanos =
338 next_work_info.delayed_run_time.since_origin().InNanoseconds();
339 struct itimerspec ts;
340 ts.it_interval.tv_sec = 0; // Don't repeat.
341 ts.it_interval.tv_nsec = 0;
342 ts.it_value.tv_sec =
343 static_cast<time_t>(nanos / TimeTicks::kNanosecondsPerSecond);
344 ts.it_value.tv_nsec = nanos % TimeTicks::kNanosecondsPerSecond;
345
Peter Kasting2f61c8b2022-07-19 23:43:46346 long ret = timerfd_settime(delayed_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
Michael Thiessen7c36083d2018-08-10 20:24:54347 DPCHECK(ret >= 0);
Michael Thiessend7ae7352018-07-10 00:57:13348}
349
Egor Pasko6da5a07c2024-03-11 19:56:29350void MessagePumpAndroid::QuitWhenIdle(base::OnceClosure callback) {
Michael Thiessend7ae7352018-07-10 00:57:13351 DCHECK(!on_quit_callback_);
352 DCHECK(run_loop_);
353 on_quit_callback_ = std::move(callback);
354 run_loop_->QuitWhenIdle();
355 // Pump the loop in case we're already idle.
356 ScheduleWork();
357}
358
Egor Pasko6da5a07c2024-03-11 19:56:29359MessagePump::Delegate* MessagePumpAndroid::SetDelegate(Delegate* delegate) {
Aaron Colwell48c4d5072020-11-13 16:45:03360 return std::exchange(delegate_, delegate);
361}
362
Egor Pasko6da5a07c2024-03-11 19:56:29363bool MessagePumpAndroid::SetQuit(bool quit) {
Aaron Colwell48c4d5072020-11-13 16:45:03364 return std::exchange(quit_, quit);
365}
366
[email protected]61c86c62011-08-02 16:11:16367} // namespace base