#include "base/message_loop/message_pump_android.h"
#include <android/looper.h>
#include <errno.h>
#include <fcntl.h>
#include <jni.h>
#include <sys/eventfd.h>
#include <sys/timerfd.h>
#include <sys/types.h>
#include <unistd.h>
#include <atomic>
#include <map>
#include <memory>
#include <utility>
#include "base/android/input_hint_checker.h"
#include "base/android/jni_android.h"
#include "base/android/scoped_java_ref.h"
#include "base/android/yield_to_looper_checker.h"
#include "base/check.h"
#include "base/check_op.h"
#include "base/message_loop/io_watcher.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/run_loop.h"
#include "base/task/task_features.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
using base::android::InputHintChecker;
using base::android::InputHintResult;
using base::android::YieldToLooperChecker;
namespace base {
namespace {
#if defined(ARCH_CPU_X86)
#define NO_INSTRUMENT_STACK_ALIGN \
__attribute__((force_align_arg_pointer, no_instrument_function))
#else
#define NO_INSTRUMENT_STACK_ALIGN __attribute__((no_instrument_function))
#endif
NO_INSTRUMENT_STACK_ALIGN int NonDelayedLooperCallback(int fd,
int events,
void* data) {
if (events & ALOOPER_EVENT_HANGUP) {
return 0;
}
DCHECK(events & ALOOPER_EVENT_INPUT);
MessagePumpAndroid* pump = reinterpret_cast<MessagePumpAndroid*>(data);
pump->OnNonDelayedLooperCallback();
return 1;
}
NO_INSTRUMENT_STACK_ALIGN int DelayedLooperCallback(int fd,
int events,
void* data) {
if (events & ALOOPER_EVENT_HANGUP) {
return 0;
}
DCHECK(events & ALOOPER_EVENT_INPUT);
MessagePumpAndroid* pump = reinterpret_cast<MessagePumpAndroid*>(data);
pump->OnDelayedLooperCallback();
return 1;
}
constexpr uint64_t kTryNativeWorkBeforeIdleBit = uint64_t(1) << 32;
std::atomic_bool g_fast_to_sleep = false;
class IOWatcherImpl : public IOWatcher {
public:
explicit IOWatcherImpl(ALooper* looper) : looper_(looper) {}
~IOWatcherImpl() override {
for (auto& [fd, watches] : watched_fds_) {
ALooper_removeFd(looper_, fd);
if (auto read_watch = std::exchange(watches.read_watch, nullptr)) {
read_watch->Detach();
}
if (auto write_watch = std::exchange(watches.write_watch, nullptr)) {
write_watch->Detach();
}
}
}
std::unique_ptr<IOWatcher::FdWatch> WatchFileDescriptorImpl(
int fd,
FdWatchDuration duration,
FdWatchMode mode,
IOWatcher::FdWatcher& watcher,
const Location& location) override {
const bool is_read =
(mode == FdWatchMode::kRead || mode == FdWatchMode::kReadWrite);
const bool is_write =
(mode == FdWatchMode::kWrite || mode == FdWatchMode::kReadWrite);
TRACE_EVENT("base", "MessagePumpAndroid::IOWatcher::WatchFileDescriptor",
"fd", fd, "persistent",
duration == FdWatchDuration::kPersistent, "write_mode",
is_write, "read_mode", is_read);
auto& watches = watched_fds_[fd];
auto watch = std::make_unique<FdWatchImpl>(*this, fd, duration, watcher);
if (is_write) {
if (watches.write_watch) {
watches.write_watch->Detach();
}
watches.write_watch = watch.get();
}
if (is_read) {
if (watches.read_watch) {
watches.read_watch->Detach();
}
watches.read_watch = watch.get();
}
const int events = (watches.read_watch ? ALOOPER_EVENT_INPUT : 0) |
(watches.write_watch ? ALOOPER_EVENT_OUTPUT : 0);
ALooper_addFd(looper_, fd, 0, events, &OnFdIoEvent, this);
return watch;
}
private:
class FdWatchImpl : public FdWatch {
public:
FdWatchImpl(IOWatcherImpl& io_watcher,
int fd,
FdWatchDuration duration,
FdWatcher& fd_watcher)
: fd_(fd),
duration_(duration),
fd_watcher_(fd_watcher),
io_watcher_(&io_watcher) {}
~FdWatchImpl() override {
Stop();
if (destruction_flag_) {
*destruction_flag_ = true;
}
}
void set_destruction_flag(bool* flag) { destruction_flag_ = flag; }
int fd() const { return fd_; }
FdWatcher& fd_watcher() const { return *fd_watcher_; }
bool is_persistent() const {
return duration_ == FdWatchDuration::kPersistent;
}
void Detach() { io_watcher_ = nullptr; }
void Stop() {
if (io_watcher_) {
std::exchange(io_watcher_, nullptr)->StopWatching(*this);
}
}
private:
const int fd_;
const FdWatchDuration duration_;
raw_ref<FdWatcher> fd_watcher_;
raw_ptr<IOWatcherImpl> io_watcher_;
raw_ptr<bool> destruction_flag_ = nullptr;
};
enum class EventResult {
kStopWatching,
kKeepWatching,
};
static NO_INSTRUMENT_STACK_ALIGN int OnFdIoEvent(int fd,
int events,
void* data) {
switch (static_cast<IOWatcherImpl*>(data)->HandleEvent(fd, events)) {
case EventResult::kStopWatching:
return 0;
case EventResult::kKeepWatching:
return 1;
}
}
EventResult HandleEvent(int fd, int events) {
auto it = watched_fds_.find(fd);
if (it == watched_fds_.end()) {
return EventResult::kStopWatching;
}
auto& watches = it->second;
const bool is_readable =
events & (ALOOPER_EVENT_INPUT | ALOOPER_EVENT_HANGUP);
const bool is_writable =
events & (ALOOPER_EVENT_OUTPUT | ALOOPER_EVENT_HANGUP);
auto* read_watch = watches.read_watch.get();
auto* write_watch = watches.write_watch.get();
bool read_watch_destroyed = false;
bool write_watch_destroyed = false;
bool fd_removed = false;
if (read_watch) {
read_watch->set_destruction_flag(&read_watch_destroyed);
}
if (write_watch && read_watch != write_watch) {
write_watch->set_destruction_flag(&write_watch_destroyed);
}
watches.removed_flag = &fd_removed;
bool did_observe_one_shot_read = false;
if (read_watch && is_readable) {
DCHECK_EQ(read_watch->fd(), fd);
did_observe_one_shot_read = !read_watch->is_persistent();
read_watch->fd_watcher().OnFdReadable(fd);
if (!read_watch_destroyed && did_observe_one_shot_read) {
read_watch->Stop();
}
}
if (read_watch == write_watch &&
(read_watch_destroyed || did_observe_one_shot_read)) {
write_watch = nullptr;
}
if (write_watch && is_writable && !write_watch_destroyed) {
DCHECK_EQ(write_watch->fd(), fd);
const bool is_persistent = write_watch->is_persistent();
write_watch->fd_watcher().OnFdWritable(fd);
if (!write_watch_destroyed && !is_persistent) {
write_watch->Stop();
}
}
if (read_watch && !read_watch_destroyed) {
read_watch->set_destruction_flag(nullptr);
}
if (write_watch && !write_watch_destroyed) {
write_watch->set_destruction_flag(nullptr);
}
if (fd_removed) {
return EventResult::kStopWatching;
}
watches.removed_flag = nullptr;
return EventResult::kKeepWatching;
}
void StopWatching(FdWatchImpl& watch) {
const int fd = watch.fd();
auto it = watched_fds_.find(fd);
if (it == watched_fds_.end()) {
return;
}
WatchPair& watches = it->second;
if (watches.read_watch == &watch) {
watches.read_watch = nullptr;
}
if (watches.write_watch == &watch) {
watches.write_watch = nullptr;
}
const int remaining_events =
(watches.read_watch ? ALOOPER_EVENT_INPUT : 0) |
(watches.write_watch ? ALOOPER_EVENT_OUTPUT : 0);
if (remaining_events) {
ALooper_addFd(looper_, fd, 0, remaining_events, &OnFdIoEvent, this);
return;
}
ALooper_removeFd(looper_, fd);
if (watches.removed_flag) {
*watches.removed_flag = true;
}
watched_fds_.erase(it);
}
private:
const raw_ptr<ALooper> looper_;
struct WatchPair {
raw_ptr<FdWatchImpl> read_watch = nullptr;
raw_ptr<FdWatchImpl> write_watch = nullptr;
raw_ptr<bool> removed_flag = nullptr;
};
std::map<int, WatchPair> watched_fds_;
};
}
MessagePumpAndroid::MessagePumpAndroid()
: env_(base::android::AttachCurrentThread()) {
non_delayed_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
CHECK_NE(non_delayed_fd_, -1);
DCHECK_EQ(TimeTicks::GetClock(), TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
delayed_fd_ = checked_cast<int>(
timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC));
CHECK_NE(delayed_fd_, -1);
looper_ = ALooper_prepare(0);
DCHECK(looper_);
ALooper_acquire(looper_);
ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT,
&NonDelayedLooperCallback, reinterpret_cast<void*>(this));
ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT,
&DelayedLooperCallback, reinterpret_cast<void*>(this));
}
MessagePumpAndroid::~MessagePumpAndroid() {
DCHECK_EQ(ALooper_forThread(), looper_);
io_watcher_.reset();
ALooper_removeFd(looper_, non_delayed_fd_);
ALooper_removeFd(looper_, delayed_fd_);
ALooper_release(looper_);
looper_ = nullptr;
close(non_delayed_fd_);
close(delayed_fd_);
}
void MessagePumpAndroid::InitializeFeatures() {
g_fast_to_sleep = base::FeatureList::IsEnabled(kPumpFastToSleepAndroid);
}
void MessagePumpAndroid::OnDelayedLooperCallback() {
OnReturnFromLooper();
if (base::android::HasException(env_)) {
return;
}
if (ShouldQuit()) {
return;
}
uint64_t value;
long ret = read(delayed_fd_, &value, sizeof(value));
DPCHECK(ret >= 0 || errno == EAGAIN);
DoDelayedLooperWork();
}
void MessagePumpAndroid::DoDelayedLooperWork() {
delayed_scheduled_time_.reset();
Delegate::NextWorkInfo next_work_info = delegate_->DoWork();
if (ShouldQuit()) {
return;
}
if (next_work_info.is_immediate()) {
ScheduleWork();
return;
}
delegate_->DoIdleWork();
if (!next_work_info.delayed_run_time.is_max()) {
ScheduleDelayedWork(next_work_info);
}
}
void MessagePumpAndroid::OnNonDelayedLooperCallback() {
OnReturnFromLooper();
if (base::android::HasException(env_)) {
return;
}
if (ShouldQuit()) {
return;
}
uint64_t value = 0;
long ret = read(non_delayed_fd_, &value, sizeof(value));
DPCHECK(ret >= 0);
DCHECK_GT(value, 0U);
bool do_idle_work = value == kTryNativeWorkBeforeIdleBit;
DoNonDelayedLooperWork(do_idle_work);
}
void MessagePumpAndroid::DoNonDelayedLooperWork(bool do_idle_work) {
Delegate::NextWorkInfo next_work_info;
do {
if (ShouldQuit()) {
return;
}
next_work_info = delegate_->DoWork();
if (is_type_ui_ && next_work_info.is_immediate()) {
if (YieldToLooperChecker::GetInstance().ShouldYield()) {
ScheduleWork();
return;
}
if (InputHintChecker::HasInput()) {
InputHintChecker::GetInstance().set_is_after_input_yield(true);
ScheduleWork();
return;
}
}
} while (next_work_info.is_immediate());
if (ShouldQuit()) {
return;
}
if (!g_fast_to_sleep) {
if (!do_idle_work) {
ScheduleWorkInternal(true);
return;
}
DCHECK(do_idle_work);
}
if (ShouldQuit()) {
return;
}
delegate_->DoIdleWork();
if (!next_work_info.delayed_run_time.is_max()) {
ScheduleDelayedWork(next_work_info);
}
}
void MessagePumpAndroid::Run(Delegate* delegate) {
NOTREACHED() << "Unexpected call to Run()";
}
void MessagePumpAndroid::Attach(Delegate* delegate) {
DCHECK(!quit_);
SetDelegate(delegate);
run_loop_ = std::make_unique<RunLoop>();
CHECK(run_loop_->BeforeRun());
}
void MessagePumpAndroid::Quit() {
if (quit_) {
return;
}
quit_ = true;
int64_t value;
read(delayed_fd_, &value, sizeof(value));
read(non_delayed_fd_, &value, sizeof(value));
if (run_loop_) {
run_loop_->AfterRun();
run_loop_ = nullptr;
}
if (on_quit_callback_) {
std::move(on_quit_callback_).Run();
}
}
void MessagePumpAndroid::ScheduleWork() {
ScheduleWorkInternal(false);
}
void MessagePumpAndroid::ScheduleWorkInternal(bool do_idle_work) {
uint64_t value = do_idle_work ? kTryNativeWorkBeforeIdleBit : 1;
long ret = write(non_delayed_fd_, &value, sizeof(value));
DPCHECK(ret >= 0);
}
void MessagePumpAndroid::OnReturnFromLooper() {
if (!is_type_ui_) {
return;
}
auto& checker = InputHintChecker::GetInstance();
if (checker.is_after_input_yield()) {
InputHintChecker::GetInstance().RecordInputHintResult(
InputHintResult::kBackToNative);
}
checker.set_is_after_input_yield(false);
}
void MessagePumpAndroid::ScheduleDelayedWork(
const Delegate::NextWorkInfo& next_work_info) {
if (ShouldQuit()) {
return;
}
if (delayed_scheduled_time_ &&
*delayed_scheduled_time_ == next_work_info.delayed_run_time) {
return;
}
DCHECK(!next_work_info.is_immediate());
delayed_scheduled_time_ = next_work_info.delayed_run_time;
int64_t nanos =
next_work_info.delayed_run_time.since_origin().InNanoseconds();
struct itimerspec ts;
ts.it_interval.tv_sec = 0;
ts.it_interval.tv_nsec = 0;
ts.it_value.tv_sec =
static_cast<time_t>(nanos / TimeTicks::kNanosecondsPerSecond);
ts.it_value.tv_nsec = nanos % TimeTicks::kNanosecondsPerSecond;
long ret = timerfd_settime(delayed_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
DPCHECK(ret >= 0);
}
IOWatcher* MessagePumpAndroid::GetIOWatcher() {
if (!io_watcher_) {
io_watcher_ = std::make_unique<IOWatcherImpl>(looper_);
}
return io_watcher_.get();
}
void MessagePumpAndroid::QuitWhenIdle(base::OnceClosure callback) {
DCHECK(!on_quit_callback_);
DCHECK(run_loop_);
on_quit_callback_ = std::move(callback);
run_loop_->QuitWhenIdle();
ScheduleWork();
}
MessagePump::Delegate* MessagePumpAndroid::SetDelegate(Delegate* delegate) {
return std::exchange(delegate_, delegate);
}
bool MessagePumpAndroid::SetQuit(bool quit) {
return std::exchange(quit_, quit);
}
}