blob: e82460373a5fbe308b98aa784eab514ad14650af [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>
Sean Maheracb46d72024-05-16 17:22:1515
16#include <atomic>
Michael Thiessend7ae7352018-07-10 00:57:1317#include <utility>
[email protected]61c86c62011-08-02 16:11:1618
Egor Pasko16859192024-03-11 12:20:5619#include "base/android/input_hint_checker.h"
[email protected]61c86c62011-08-02 16:11:1620#include "base/android/jni_android.h"
[email protected]449019ca2012-03-14 22:17:0021#include "base/android/scoped_java_ref.h"
Hans Wennborgc3cffa62020-04-27 10:09:1222#include "base/check_op.h"
Hans Wennborgc3cffa62020-04-27 10:09:1223#include "base/notreached.h"
Peter Kasting2f61c8b2022-07-19 23:43:4624#include "base/numerics/safe_conversions.h"
[email protected]8e937c1e2012-06-28 22:57:3025#include "base/run_loop.h"
Sean Maheracb46d72024-05-16 17:22:1526#include "base/task/task_features.h"
Egor Pasko16859192024-03-11 12:20:5627#include "base/time/time.h"
Michael Thiessen6fd43902018-08-30 23:14:1528#include "build/build_config.h"
Michael Thiessend7ae7352018-07-10 00:57:1329
Michael Thiessen781ddeb2017-11-15 17:07:2330namespace base {
31
Michael Thiessend7ae7352018-07-10 00:57:1332namespace {
Michael Thiessen781ddeb2017-11-15 17:07:2333
Michael Thiessen6fd43902018-08-30 23:14:1534// https://crbug.com/873588. The stack may not be aligned when the ALooper calls
35// into our code due to the inconsistent ABI on older Android OS versions.
Egor Pasko30c67b62024-03-22 17:55:5736//
37// https://crbug.com/330761384#comment3. Calls from libutils.so into
38// NonDelayedLooperCallback() and DelayedLooperCallback() confuse aarch64 builds
39// with orderfile instrumentation causing incorrect value in
40// __builtin_return_address(0). Disable instrumentation for them. TODO(pasko):
41// Add these symbols to the orderfile manually or fix the builtin.
Michael Thiessen6fd43902018-08-30 23:14:1542#if defined(ARCH_CPU_X86)
Egor Pasko30c67b62024-03-22 17:55:5743#define NO_INSTRUMENT_STACK_ALIGN \
44 __attribute__((force_align_arg_pointer, no_instrument_function))
Michael Thiessen6fd43902018-08-30 23:14:1545#else
Egor Pasko30c67b62024-03-22 17:55:5746#define NO_INSTRUMENT_STACK_ALIGN __attribute__((no_instrument_function))
Michael Thiessen6fd43902018-08-30 23:14:1547#endif
48
Egor Pasko30c67b62024-03-22 17:55:5749NO_INSTRUMENT_STACK_ALIGN int NonDelayedLooperCallback(int fd,
50 int events,
51 void* data) {
Michael Thiessend7ae7352018-07-10 00:57:1352 if (events & ALOOPER_EVENT_HANGUP)
53 return 0;
54
55 DCHECK(events & ALOOPER_EVENT_INPUT);
Egor Pasko6da5a07c2024-03-11 19:56:2956 MessagePumpAndroid* pump = reinterpret_cast<MessagePumpAndroid*>(data);
Michael Thiessend7ae7352018-07-10 00:57:1357 pump->OnNonDelayedLooperCallback();
58 return 1; // continue listening for events
59}
60
Egor Pasko30c67b62024-03-22 17:55:5761NO_INSTRUMENT_STACK_ALIGN int DelayedLooperCallback(int fd,
62 int events,
63 void* data) {
Michael Thiessend7ae7352018-07-10 00:57:1364 if (events & ALOOPER_EVENT_HANGUP)
65 return 0;
66
67 DCHECK(events & ALOOPER_EVENT_INPUT);
Egor Pasko6da5a07c2024-03-11 19:56:2968 MessagePumpAndroid* pump = reinterpret_cast<MessagePumpAndroid*>(data);
Michael Thiessend7ae7352018-07-10 00:57:1369 pump->OnDelayedLooperCallback();
70 return 1; // continue listening for events
71}
72
Aaron Colwell48c4d5072020-11-13 16:45:0373// A bit added to the |non_delayed_fd_| to keep it signaled when we yield to
Gabriel Charette300f16792022-07-06 20:03:3074// native work below.
75constexpr uint64_t kTryNativeWorkBeforeIdleBit = uint64_t(1) << 32;
Sean Maheracb46d72024-05-16 17:22:1576
77std::atomic_bool g_fast_to_sleep = false;
Michael Thiessend7ae7352018-07-10 00:57:1378} // namespace
79
Egor Pasko6da5a07c2024-03-11 19:56:2980MessagePumpAndroid::MessagePumpAndroid()
François Doray253a3062022-10-24 16:45:2981 : env_(base::android::AttachCurrentThread()) {
Michael Thiessend7ae7352018-07-10 00:57:1382 // The Android native ALooper uses epoll to poll our file descriptors and wake
83 // us up. We use a simple level-triggered eventfd to signal that non-delayed
84 // work is available, and a timerfd to signal when delayed work is ready to
85 // be run.
86 non_delayed_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
87 CHECK_NE(non_delayed_fd_, -1);
88 DCHECK_EQ(TimeTicks::GetClock(), TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
89
Peter Kasting2f61c8b2022-07-19 23:43:4690 delayed_fd_ = checked_cast<int>(
Egor Pasko47ff68432023-04-05 18:23:5791 timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC));
Michael Thiessend7ae7352018-07-10 00:57:1392 CHECK_NE(delayed_fd_, -1);
93
94 looper_ = ALooper_prepare(0);
95 DCHECK(looper_);
96 // Add a reference to the looper so it isn't deleted on us.
97 ALooper_acquire(looper_);
98 ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT,
99 &NonDelayedLooperCallback, reinterpret_cast<void*>(this));
100 ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT,
101 &DelayedLooperCallback, reinterpret_cast<void*>(this));
102}
103
Egor Pasko6da5a07c2024-03-11 19:56:29104MessagePumpAndroid::~MessagePumpAndroid() {
Michael Thiessend7ae7352018-07-10 00:57:13105 DCHECK_EQ(ALooper_forThread(), looper_);
106 ALooper_removeFd(looper_, non_delayed_fd_);
107 ALooper_removeFd(looper_, delayed_fd_);
108 ALooper_release(looper_);
109 looper_ = nullptr;
110
111 close(non_delayed_fd_);
112 close(delayed_fd_);
113}
114
Sean Maheracb46d72024-05-16 17:22:15115void MessagePumpAndroid::InitializeFeatures() {
116 g_fast_to_sleep = base::FeatureList::IsEnabled(kPumpFastToSleepAndroid);
117}
118
Egor Pasko6da5a07c2024-03-11 19:56:29119void MessagePumpAndroid::OnDelayedLooperCallback() {
Torne (Richard Coles)c6993a032020-02-19 13:23:47120 // There may be non-Chromium callbacks on the same ALooper which may have left
121 // a pending exception set, and ALooper does not check for this between
122 // callbacks. Check here, and if there's already an exception, just skip this
123 // iteration without clearing the fd. If the exception ends up being non-fatal
124 // then we'll just get called again on the next polling iteration.
125 if (base::android::HasException(env_))
126 return;
127
Gabriel Charette9d44a9b2019-04-29 16:35:56128 // ALooper_pollOnce may call this after Quit() if OnNonDelayedLooperCallback()
129 // resulted in Quit() in the same round.
Michael Thiessend7ae7352018-07-10 00:57:13130 if (ShouldQuit())
Michael Thiessenfc7067fe2017-11-01 22:33:01131 return;
132
François Doray23159042023-03-01 14:14:09133 // Clear the fd.
134 uint64_t value;
135 long ret = read(delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54136
François Doray23159042023-03-01 14:14:09137 // TODO(mthiesse): Figure out how it's possible to hit EAGAIN here.
138 // According to http://man7.org/linux/man-pages/man2/timerfd_create.2.html
139 // EAGAIN only happens if no timer has expired. Also according to the man page
140 // poll only returns readable when a timer has expired. So this function will
141 // only be called when a timer has expired, but reading reveals no timer has
142 // expired...
143 // Quit() and ScheduleDelayedWork() are the only other functions that touch
144 // the timerfd, and they both run on the same thread as this callback, so
145 // there are no obvious timing or multi-threading related issues.
146 DPCHECK(ret >= 0 || errno == EAGAIN);
Aaron Colwell48c4d5072020-11-13 16:45:03147 DoDelayedLooperWork();
148}
Michael Thiessen7c36083d2018-08-10 20:24:54149
Egor Pasko6da5a07c2024-03-11 19:56:29150void MessagePumpAndroid::DoDelayedLooperWork() {
Gabriel Charette9d44a9b2019-04-29 16:35:56151 delayed_scheduled_time_.reset();
[email protected]61c86c62011-08-02 16:11:16152
Etienne Pierre-doray2163f3012020-04-02 21:37:14153 Delegate::NextWorkInfo next_work_info = delegate_->DoWork();
Gabriel Charette9d44a9b2019-04-29 16:35:56154
Michael Thiessend7ae7352018-07-10 00:57:13155 if (ShouldQuit())
156 return;
Gabriel Charette9d44a9b2019-04-29 16:35:56157
158 if (next_work_info.is_immediate()) {
159 ScheduleWork();
160 return;
161 }
162
163 DoIdleWork();
164 if (!next_work_info.delayed_run_time.is_max())
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33165 ScheduleDelayedWork(next_work_info);
Michael Thiessend7ae7352018-07-10 00:57:13166}
167
Egor Pasko6da5a07c2024-03-11 19:56:29168void MessagePumpAndroid::OnNonDelayedLooperCallback() {
Torne (Richard Coles)c6993a032020-02-19 13:23:47169 // There may be non-Chromium callbacks on the same ALooper which may have left
170 // a pending exception set, and ALooper does not check for this between
171 // callbacks. Check here, and if there's already an exception, just skip this
172 // iteration without clearing the fd. If the exception ends up being non-fatal
173 // then we'll just get called again on the next polling iteration.
174 if (base::android::HasException(env_))
175 return;
176
Gabriel Charette9d44a9b2019-04-29 16:35:56177 // ALooper_pollOnce may call this after Quit() if OnDelayedLooperCallback()
178 // resulted in Quit() in the same round.
Michael Thiessend7ae7352018-07-10 00:57:13179 if (ShouldQuit())
180 return;
181
Gabriel Charette9d44a9b2019-04-29 16:35:56182 // We're about to process all the work requested by ScheduleWork().
183 // MessagePump users are expected to do their best not to invoke
Etienne Pierre-doray2163f3012020-04-02 21:37:14184 // ScheduleWork() again before DoWork() returns a non-immediate
Gabriel Charette9d44a9b2019-04-29 16:35:56185 // NextWorkInfo below. Hence, capturing the file descriptor's value now and
186 // resetting its contents to 0 should be okay. The value currently stored
187 // should be greater than 0 since work having been scheduled is the reason
188 // we're here. See http://man7.org/linux/man-pages/man2/eventfd.2.html
Aaron Colwell48c4d5072020-11-13 16:45:03189 uint64_t value = 0;
Peter Kasting2f61c8b2022-07-19 23:43:46190 long ret = read(non_delayed_fd_, &value, sizeof(value));
Gabriel Charette9d44a9b2019-04-29 16:35:56191 DPCHECK(ret >= 0);
Aaron Colwell48c4d5072020-11-13 16:45:03192 DCHECK_GT(value, 0U);
Gabriel Charette300f16792022-07-06 20:03:30193 bool do_idle_work = value == kTryNativeWorkBeforeIdleBit;
Aaron Colwell48c4d5072020-11-13 16:45:03194 DoNonDelayedLooperWork(do_idle_work);
195}
Gabriel Charette9d44a9b2019-04-29 16:35:56196
Egor Pasko6da5a07c2024-03-11 19:56:29197void MessagePumpAndroid::DoNonDelayedLooperWork(bool do_idle_work) {
Aaron Colwell48c4d5072020-11-13 16:45:03198 // Note: We can't skip DoWork() even if |do_idle_work| is true here (i.e. no
199 // additional ScheduleWork() since yielding to native) as delayed tasks might
200 // have come in and we need to re-sample |next_work_info|.
Gabriel Charette9d44a9b2019-04-29 16:35:56201
202 // Runs all application tasks scheduled to run.
203 Delegate::NextWorkInfo next_work_info;
204 do {
205 if (ShouldQuit())
206 return;
207
Etienne Pierre-doray2163f3012020-04-02 21:37:14208 next_work_info = delegate_->DoWork();
Egor Pasko16859192024-03-11 12:20:56209
Stephen Nusko133b4e42021-06-01 22:06:23210 // If we are prioritizing native, and the next work would normally run
Gabriel Charette300f16792022-07-06 20:03:30211 // immediately, skip the next work and let the native work items have a
212 // chance to run. This is useful when user input is waiting for native to
213 // have a chance to run.
Stephen Nusko133b4e42021-06-01 22:06:23214 if (next_work_info.is_immediate() && next_work_info.yield_to_native) {
François Doray23159042023-03-01 14:14:09215 ScheduleWork();
Stephen Nusko133b4e42021-06-01 22:06:23216 return;
217 }
Egor Pasko16859192024-03-11 12:20:56218
219 // As an optimization, yield to the Looper when input events are waiting to
220 // be handled. In some cases input events can remain undetected. Such "input
221 // hint false negatives" happen, for example, during initialization, in
222 // multi-window cases, or when a previous value is cached to throttle
223 // polling the input channel.
224 if (is_type_ui_ && android::InputHintChecker::HasInput()) {
225 ScheduleWork();
226 return;
227 }
Gabriel Charette9d44a9b2019-04-29 16:35:56228 } while (next_work_info.is_immediate());
229
230 // Do not resignal |non_delayed_fd_| if we're quitting (this pump doesn't
231 // allow nesting so needing to resume in an outer loop is not an issue
232 // either).
233 if (ShouldQuit())
234 return;
235
Sean Maheracb46d72024-05-16 17:22:15236 // Under the fast to sleep feature, `do_idle_work` is ignored, and the pump
237 // will always "sleep" after finishing all its work items.
238 if (!g_fast_to_sleep) {
239 // Before declaring this loop idle, yield to native work items and arrange
240 // to be called again (unless we're already in that second call).
241 if (!do_idle_work) {
242 ScheduleWorkInternal(/*do_idle_work=*/true);
243 return;
244 }
245
246 // We yielded to native work items already and they didn't generate a
247 // ScheduleWork() request so we can declare idleness. It's possible for a
248 // ScheduleWork() request to come in racily while this method unwinds, this
249 // is fine and will merely result in it being re-invoked shortly after it
250 // returns.
251 // TODO(scheduler-dev): this doesn't account for tasks that don't ever call
252 // SchedulerWork() but still keep the system non-idle (e.g., the Java
253 // Handler API). It would be better to add an API to query the presence of
254 // native tasks instead of relying on yielding once +
255 // kTryNativeWorkBeforeIdleBit.
256 DCHECK(do_idle_work);
Michael Thiessend7ae7352018-07-10 00:57:13257 }
Gabriel Charette9d44a9b2019-04-29 16:35:56258
Sean Maheracb46d72024-05-16 17:22:15259 if (ShouldQuit()) {
Gabriel Charette9d44a9b2019-04-29 16:35:56260 return;
Sean Maheracb46d72024-05-16 17:22:15261 }
Gabriel Charette9d44a9b2019-04-29 16:35:56262
263 // At this point, the java looper might not be idle - it's impossible to know
264 // pre-Android-M, so we may end up doing Idle work while java tasks are still
265 // queued up. Note that this won't cause us to fail to run java tasks using
266 // QuitWhenIdle, as the JavaHandlerThread will finish running all currently
267 // scheduled tasks before it quits. Also note that we can't just add an idle
268 // callback to the java looper, as that will fire even if application tasks
269 // are still queued up.
270 DoIdleWork();
Stephen Nusko408b9a92022-09-15 10:03:57271 if (!next_work_info.delayed_run_time.is_max()) {
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33272 ScheduleDelayedWork(next_work_info);
Stephen Nusko408b9a92022-09-15 10:03:57273 }
Michael Thiessend7ae7352018-07-10 00:57:13274}
275
Egor Pasko6da5a07c2024-03-11 19:56:29276void MessagePumpAndroid::DoIdleWork() {
Michael Thiessend7ae7352018-07-10 00:57:13277 if (delegate_->DoIdleWork()) {
278 // If DoIdleWork() resulted in any work, we're not idle yet. We need to pump
279 // the loop here because we may in fact be idle after doing idle work
280 // without any new tasks being queued.
281 ScheduleWork();
282 }
[email protected]61c86c62011-08-02 16:11:16283}
284
Egor Pasko6da5a07c2024-03-11 19:56:29285void MessagePumpAndroid::Run(Delegate* delegate) {
Aaron Colwelld34116aaa2020-11-11 02:15:37286 CHECK(false) << "Unexpected call to Run()";
[email protected]61c86c62011-08-02 16:11:16287}
288
Egor Pasko6da5a07c2024-03-11 19:56:29289void MessagePumpAndroid::Attach(Delegate* delegate) {
Michael Thiessendbeca242017-08-28 21:10:08290 DCHECK(!quit_);
Michael Thiessend7ae7352018-07-10 00:57:13291
292 // Since the Looper is controlled by the UI thread or JavaHandlerThread, we
293 // can't use Run() like we do on other platforms or we would prevent Java
294 // tasks from running. Instead we create and initialize a run loop here, then
295 // return control back to the Looper.
296
297 SetDelegate(delegate);
Michael Thiessen781ddeb2017-11-15 17:07:23298 run_loop_ = std::make_unique<RunLoop>();
[email protected]8e937c1e2012-06-28 22:57:30299 // Since the RunLoop was just created above, BeforeRun should be guaranteed to
300 // return true (it only returns false if the RunLoop has been Quit already).
301 if (!run_loop_->BeforeRun())
Peter Boström8c29f4a2024-05-08 01:12:11302 NOTREACHED_IN_MIGRATION();
[email protected]61c86c62011-08-02 16:11:16303}
304
Egor Pasko6da5a07c2024-03-11 19:56:29305void MessagePumpAndroid::Quit() {
Michael Thiessend7ae7352018-07-10 00:57:13306 if (quit_)
307 return;
308
Michael Thiessendbeca242017-08-28 21:10:08309 quit_ = true;
Michael Thiessen781ddeb2017-11-15 17:07:23310
Michael Thiessend7ae7352018-07-10 00:57:13311 int64_t value;
312 // Clear any pending timer.
313 read(delayed_fd_, &value, sizeof(value));
314 // Clear the eventfd.
315 read(non_delayed_fd_, &value, sizeof(value));
[email protected]61c86c62011-08-02 16:11:16316
[email protected]8e937c1e2012-06-28 22:57:30317 if (run_loop_) {
318 run_loop_->AfterRun();
Michael Thiessen781ddeb2017-11-15 17:07:23319 run_loop_ = nullptr;
[email protected]61c86c62011-08-02 16:11:16320 }
Michael Thiessend7ae7352018-07-10 00:57:13321 if (on_quit_callback_) {
322 std::move(on_quit_callback_).Run();
323 }
[email protected]61c86c62011-08-02 16:11:16324}
325
Egor Pasko6da5a07c2024-03-11 19:56:29326void MessagePumpAndroid::ScheduleWork() {
Aaron Colwell48c4d5072020-11-13 16:45:03327 ScheduleWorkInternal(/*do_idle_work=*/false);
328}
329
Egor Pasko6da5a07c2024-03-11 19:56:29330void MessagePumpAndroid::ScheduleWorkInternal(bool do_idle_work) {
Aaron Colwell48c4d5072020-11-13 16:45:03331 // Write (add) |value| to the eventfd. This tells the Looper to wake up and
332 // call our callback, allowing us to run tasks. This also allows us to detect,
333 // when we clear the fd, whether additional work was scheduled after we
334 // finished performing work, but before we cleared the fd, as we'll read back
335 // >=2 instead of 1 in that case. See the eventfd man pages
Michael Thiessend7ae7352018-07-10 00:57:13336 // (http://man7.org/linux/man-pages/man2/eventfd.2.html) for details on how
337 // the read and write APIs for this file descriptor work, specifically without
338 // EFD_SEMAPHORE.
Aaron Colwell48c4d5072020-11-13 16:45:03339 // Note: Calls with |do_idle_work| set to true may race with potential calls
François Doray23159042023-03-01 14:14:09340 // where the parameter is false. This is fine as write() is adding |value|,
341 // not overwriting the existing value, and as such racing calls would merely
342 // have their values added together. Since idle work is only executed when the
343 // value read equals kTryNativeWorkBeforeIdleBit, a race would prevent idle
344 // work from being run and trigger another call to this method with
345 // |do_idle_work| set to true.
Gabriel Charette300f16792022-07-06 20:03:30346 uint64_t value = do_idle_work ? kTryNativeWorkBeforeIdleBit : 1;
Peter Kasting2f61c8b2022-07-19 23:43:46347 long ret = write(non_delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54348 DPCHECK(ret >= 0);
[email protected]61c86c62011-08-02 16:11:16349}
350
Egor Pasko6da5a07c2024-03-11 19:56:29351void MessagePumpAndroid::ScheduleDelayedWork(
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33352 const Delegate::NextWorkInfo& next_work_info) {
Michael Thiessend7ae7352018-07-10 00:57:13353 if (ShouldQuit())
Michael Thiessendbeca242017-08-28 21:10:08354 return;
Michael Thiessend7ae7352018-07-10 00:57:13355
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33356 if (delayed_scheduled_time_ &&
357 *delayed_scheduled_time_ == next_work_info.delayed_run_time) {
Michael Thiessen781ddeb2017-11-15 17:07:23358 return;
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33359 }
Michael Thiessend7ae7352018-07-10 00:57:13360
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33361 DCHECK(!next_work_info.is_immediate());
362 delayed_scheduled_time_ = next_work_info.delayed_run_time;
François Doray23159042023-03-01 14:14:09363 int64_t nanos =
364 next_work_info.delayed_run_time.since_origin().InNanoseconds();
365 struct itimerspec ts;
366 ts.it_interval.tv_sec = 0; // Don't repeat.
367 ts.it_interval.tv_nsec = 0;
368 ts.it_value.tv_sec =
369 static_cast<time_t>(nanos / TimeTicks::kNanosecondsPerSecond);
370 ts.it_value.tv_nsec = nanos % TimeTicks::kNanosecondsPerSecond;
371
Peter Kasting2f61c8b2022-07-19 23:43:46372 long ret = timerfd_settime(delayed_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
Michael Thiessen7c36083d2018-08-10 20:24:54373 DPCHECK(ret >= 0);
Michael Thiessend7ae7352018-07-10 00:57:13374}
375
Egor Pasko6da5a07c2024-03-11 19:56:29376void MessagePumpAndroid::QuitWhenIdle(base::OnceClosure callback) {
Michael Thiessend7ae7352018-07-10 00:57:13377 DCHECK(!on_quit_callback_);
378 DCHECK(run_loop_);
379 on_quit_callback_ = std::move(callback);
380 run_loop_->QuitWhenIdle();
381 // Pump the loop in case we're already idle.
382 ScheduleWork();
383}
384
Egor Pasko6da5a07c2024-03-11 19:56:29385MessagePump::Delegate* MessagePumpAndroid::SetDelegate(Delegate* delegate) {
Aaron Colwell48c4d5072020-11-13 16:45:03386 return std::exchange(delegate_, delegate);
387}
388
Egor Pasko6da5a07c2024-03-11 19:56:29389bool MessagePumpAndroid::SetQuit(bool quit) {
Aaron Colwell48c4d5072020-11-13 16:45:03390 return std::exchange(quit_, quit);
391}
392
[email protected]61c86c62011-08-02 16:11:16393} // namespace base