1、时间限制常量
DEFAULT_INPUT_DISPATCHING_TIMEOUT
一般与UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS
相等,是5s
HwTimeoutMultiplier()
: 调整硬件通信超时时间的乘数"ro.hw_timeout_multiplier"
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
// Default input dispatching timeout if there is no focused application or paused window
// from which to determine an appropriate dispatching timeout.
const std::chrono::duration DEFAULT_INPUT_DISPATCHING_TIMEOUT = std::chrono::milliseconds(
android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
HwTimeoutMultiplier());
frameworks/native/libs/input/android/os/IInputConstants.aidl
// This should be multiplied by the value of the system property ro.hw_timeout_multiplier before
// use. A pre-multiplied constant is available in Java in
// android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS.
const int UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS = 5000; // 5 seconds
system/libbase/include/android-base/properties.h
static inline int HwTimeoutMultiplier() {
return android::base::GetIntProperty("ro.hw_timeout_multiplier", 1);
}
2、ANR输出信息
InputDispatcher::onAnrLocked
检测到anr
,通过JNI通知到InputManagerService
:
- notifyNoFocusedWindowAnr
- notifyWindowUnresponsive
通过InputManagerCallback
处理timeoutRecord
信息,最后AnrController
输出日志信息
frameworks/base/services/core/java/com/android/server/wm/InputManagerCallback.java
/**
* Notifies the window manager about an application that is not responding because it has
* no focused window.
*
* Called by the InputManager.
*/
@Override
public void notifyNoFocusedWindowAnr(@NonNull InputApplicationHandle applicationHandle) {
TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchNoFocusedWindow(
timeoutMessage(OptionalInt.empty(), "Application does not have a focused window"));
mService.mAnrController.notifyAppUnresponsive(applicationHandle, timeoutRecord);
}
@Override
public void notifyWindowUnresponsive(@NonNull IBinder token, @NonNull OptionalInt pid,
String reason) {
TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchWindowUnresponsive(
timeoutMessage(pid, reason));
mService.mAnrController.notifyWindowUnresponsive(token, pid, timeoutRecord);
}
private String timeoutMessage(OptionalInt pid, String reason) {
String message = (reason == null) ? "Input dispatching timed out."
: String.format("Input dispatching timed out (%s).", reason);
if (pid.isEmpty()) {
return message;
}
StalledTransactionInfo stalledTransactionInfo =
SurfaceControl.getStalledTransactionInfo(pid.getAsInt());
if (stalledTransactionInfo == null) {
return message;
}
return String.format("%s Buffer processing for the associated surface is stuck due to an "
+ "unsignaled fence (window=%s, bufferId=0x%016X, frameNumber=%s). This "
+ "potentially indicates a GPU hang.", message, stalledTransactionInfo.layerName,
stalledTransactionInfo.bufferId, stalledTransactionInfo.frameNumber);
}
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
// Native callback
@SuppressWarnings("unused")
private void notifyWindowUnresponsive(IBinder token, int pid, boolean isPidValid,
String reason) {
mWindowManagerCallbacks.notifyWindowUnresponsive(token,
isPidValid ? OptionalInt.of(pid) : OptionalInt.empty(), reason);
}
// Native callback
@SuppressWarnings("unused")
private void notifyWindowResponsive(IBinder token, int pid, boolean isPidValid) {
mWindowManagerCallbacks.notifyWindowResponsive(token,
isPidValid ? OptionalInt.of(pid) : OptionalInt.empty());
}
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
// ANR-related callbacks -- start
void notifyNoFocusedWindowAnr(const std::shared_ptr<InputApplicationHandle>& handle) override;
void notifyWindowUnresponsive(const sp<IBinder>& token, std::optional<gui::Pid> pid,
const std::string& reason) override;
void notifyWindowResponsive(const sp<IBinder>& token, std::optional<gui::Pid> pid) override;
// ANR-related callbacks -- end
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::onAnrLocked(const std::shared_ptr<Connection>& connection) {
if (connection == nullptr) {
LOG_ALWAYS_FATAL("Caller must check for nullness");
}
// Since we are allowing the policy to extend the timeout, maybe the waitQueue
// is already healthy again. Don't raise ANR in this situation
if (connection->waitQueue.empty()) {
ALOGI("Not raising ANR because the connection %s has recovered",
connection->getInputChannelName().c_str());
return;
}
/**
* The "oldestEntry" is the entry that was first sent to the application. That entry, however,
* may not be the one that caused the timeout to occur. One possibility is that window timeout
* has changed. This could cause newer entries to time out before the already dispatched
* entries. In that situation, the newest entries caused ANR. But in all likelihood, the app
* processes the events linearly. So providing information about the oldest entry seems to be
* most useful.
*/
DispatchEntry& oldestEntry = *connection->waitQueue.front();
const nsecs_t currentWait = now() - oldestEntry.deliveryTime;
std::string reason =
android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",
connection->getInputChannelName().c_str(),
ns2ms(currentWait),
oldestEntry.eventEntry->getDescription().c_str());
sp<IBinder> connectionToken = connection->getToken();
updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);
processConnectionUnresponsiveLocked(*connection, std::move(reason));
// Stop waking up for events on this connection, it is already unresponsive
cancelEventsForAnrLocked(connection);
}
void InputDispatcher::onAnrLocked(std::shared_ptr<InputApplicationHandle> application) {
std::string reason =
StringPrintf("%s does not have a focused window", application->getName().c_str());
updateLastAnrStateLocked(*application, reason);
auto command = [this, app = std::move(application)]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
mPolicy.notifyNoFocusedWindowAnr(app);
};
postCommandLocked(std::move(command));
}
3 、超时逻辑
正常情况,
ViewRootImpl
的InputStage
责任链处理完InputEvent
事件,都会finishInputEvent
通知InputDispatcher
执行doDispatchCycleFinishedCommand
进行发送下一个InputEvet
事件,此时会擦除Timeout
时间mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
若InputEvent
事件没有消费反馈处理,当dispatchOnce()
轮询时,检测processAnrsLocked()
,超时就会执行onAnrLocked
ANR输出信息。
frameworks/native/services/inputflinger/dispatcher/AnrTracker.h
frameworks/native/services/inputflinger/dispatcher/AnrTracker.cpp
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
frameworks/base/core/jni/android_view_InputEventSender.cpp
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LLONG_MAX;
{ // acquire lock
std::scoped_lock _l(mLock);
mDispatcherIsAlive.notify_all();
// Run a dispatch loop if there are no pending commands.
// The dispatch loop might enqueue commands to run afterwards.
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(/*byref*/ nextWakeupTime);
}
// Run all pending commands if there are any.
// If any commands were run then force the next poll to wake up immediately.
if (runCommandsLockedInterruptable()) {
nextWakeupTime = LLONG_MIN;
}
// If we are still waiting for ack on some events,
// we might have to wake up earlier to check if an app is anr'ing.
const nsecs_t nextAnrCheck = processAnrsLocked();
nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
if (mPerDeviceInputLatencyMetricsFlag) {
processLatencyStatisticsLocked();
}
// We are about to enter an infinitely long sleep, because we have no commands or
// pending or queued events
if (nextWakeupTime == LLONG_MAX) {
mDispatcherEnteredIdle.notify_all();
}
} // release lock
// Wait for callback or timeout or wake. (make sure we round up, not down)
nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
mLooper->pollOnce(timeoutMillis);
}
/**
* Check if any of the connections' wait queues have events that are too old.
* If we waited for events to be ack'ed for more than the window timeout, raise an ANR.
* Return the time at which we should wake up next.
*/
nsecs_t InputDispatcher::processAnrsLocked() {
const nsecs_t currentTime = now();
nsecs_t nextAnrCheck = LLONG_MAX;
// Check if we are waiting for a focused window to appear. Raise ANR if waited too long
if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
if (currentTime >= *mNoFocusedWindowTimeoutTime) {
processNoFocusedWindowAnrLocked();
mAwaitedFocusedApplication.reset();
mNoFocusedWindowTimeoutTime = std::nullopt;
return LLONG_MIN;
} else {
// Keep waiting. We will drop the event when mNoFocusedWindowTimeoutTime comes.
nextAnrCheck = *mNoFocusedWindowTimeoutTime;
}
}
// Check if any connection ANRs are due
nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());
if (currentTime < nextAnrCheck) { // most likely scenario
return nextAnrCheck; // everything is normal. Let's check again at nextAnrCheck
}
// If we reached here, we have an unresponsive connection.
std::shared_ptr<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
if (connection == nullptr) {
ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout());
return nextAnrCheck;
}
connection->responsive = false;
// Stop waking up for this unresponsive connection
mAnrTracker.eraseToken(connection->getToken());
onAnrLocked(connection);
return LLONG_MIN;
}
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
const std::shared_ptr<Connection>& connection,
uint32_t seq, bool handled, nsecs_t consumeTime) {
if (DEBUG_DISPATCH_CYCLE) {
ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
connection->getInputChannelName().c_str(), seq, toString(handled));
}
if (connection->status != Connection::Status::NORMAL) {
return;
}
// Notify other system components and prepare to start the next dispatch cycle.
auto command = [this, currentTime, connection, seq, handled, consumeTime]() REQUIRES(mLock) {
doDispatchCycleFinishedCommand(currentTime, connection, seq, handled, consumeTime);
};
postCommandLocked(std::move(command));
}
int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionToken) {
std::scoped_lock _l(mLock);
std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken);
if (connection == nullptr) {
ALOGW("Received looper callback for unknown input channel token %p. events=0x%x",
connectionToken.get(), events);
return 0; // remove the callback
}
bool notify;
if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
if (!(events & ALOOPER_EVENT_INPUT)) {
ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
"events=0x%x",
connection->getInputChannelName().c_str(), events);
return 1;
}
nsecs_t currentTime = now();
bool gotOne = false;
status_t status = OK;
for (;;) {
Result<InputPublisher::ConsumerResponse> result =
connection->inputPublisher.receiveConsumerResponse();
if (!result.ok()) {
status = result.error().code();
break;
}
if (std::holds_alternative<InputPublisher::Finished>(*result)) {
const InputPublisher::Finished& finish =
std::get<InputPublisher::Finished>(*result);
finishDispatchCycleLocked(currentTime, connection, finish.seq, finish.handled,
finish.consumeTime);
} else if (std::holds_alternative<InputPublisher::Timeline>(*result)) {
if (shouldReportMetricsForConnection(*connection)) {
const InputPublisher::Timeline& timeline =
std::get<InputPublisher::Timeline>(*result);
mLatencyTracker.trackGraphicsLatency(timeline.inputEventId,
connection->getToken(),
std::move(timeline.graphicsTimeline));
}
}
gotOne = true;
}
if (gotOne) {
runCommandsLockedInterruptable();
if (status == WOULD_BLOCK) {
return 1;
}
}
notify = status != DEAD_OBJECT || !connection->monitor;
if (notify) {
ALOGE("channel '%s' ~ Failed to receive finished signal. status=%s(%d)",
connection->getInputChannelName().c_str(), statusToString(status).c_str(),
status);
}
} else {
// Monitor channels are never explicitly unregistered.
// We do it automatically when the remote endpoint is closed so don't warn about them.
const bool stillHaveWindowHandle = getWindowHandleLocked(connection->getToken()) != nullptr;
notify = !connection->monitor && stillHaveWindowHandle;
if (notify) {
ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. events=0x%x",
connection->getInputChannelName().c_str(), events);
}
}
// Remove the channel.
removeInputChannelLocked(connection->getToken(), notify);
return 0; // remove the callback
}
void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime,
const std::shared_ptr<Connection>& connection,
uint32_t seq, bool handled,
nsecs_t consumeTime) {
// Handle post-event policy actions.
std::unique_ptr<const KeyEntry> fallbackKeyEntry;
{ // Start critical section
auto dispatchEntryIt =
std::find_if(connection->waitQueue.begin(), connection->waitQueue.end(),
[seq](auto& e) { return e->seq == seq; });
if (dispatchEntryIt == connection->waitQueue.end()) {
return;
}
DispatchEntry& dispatchEntry = **dispatchEntryIt;
const nsecs_t eventDuration = finishTime - dispatchEntry.deliveryTime;
if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
ALOGI("%s spent %" PRId64 "ms processing %s", connection->getInputChannelName().c_str(),
ns2ms(eventDuration), dispatchEntry.eventEntry->getDescription().c_str());
}
if (shouldReportFinishedEvent(dispatchEntry, *connection)) {
mLatencyTracker.trackFinishedEvent(dispatchEntry.eventEntry->id, connection->getToken(),
dispatchEntry.deliveryTime, consumeTime, finishTime);
}
if (dispatchEntry.eventEntry->type == EventEntry::Type::KEY) {
fallbackKeyEntry =
afterKeyEventLockedInterruptable(connection, &dispatchEntry, handled);
}
} // End critical section: The -LockedInterruptable methods may have released the lock.
// Dequeue the event and start the next cycle.
// Because the lock might have been released, it is possible that the
// contents of the wait queue to have been drained, so we need to double-check
// a few things.
auto entryIt = std::find_if(connection->waitQueue.begin(), connection->waitQueue.end(),
[seq](auto& e) { return e->seq == seq; });
if (entryIt != connection->waitQueue.end()) {
std::unique_ptr<DispatchEntry> dispatchEntry = std::move(*entryIt);
connection->waitQueue.erase(entryIt);
const sp<IBinder>& connectionToken = connection->getToken();
mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
if (!connection->responsive) {
connection->responsive = isConnectionResponsive(*connection);
if (connection->responsive) {
// The connection was unresponsive, and now it's responsive.
processConnectionResponsiveLocked(*connection);
}
}
traceWaitQueueLength(*connection);
if (fallbackKeyEntry && connection->status == Connection::Status::NORMAL) {
const auto windowHandle = getWindowHandleLocked(connection->getToken());
// Only dispatch fallbacks if there is a window for the connection.
if (windowHandle != nullptr) {
const auto inputTarget =
createInputTargetLocked(windowHandle, InputTarget::DispatchMode::AS_IS,
dispatchEntry->targetFlags,
fallbackKeyEntry->downTime);
if (inputTarget.has_value()) {
enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry),
*inputTarget);
}
}
}
releaseDispatchEntry(std::move(dispatchEntry));
}
// Start the next dispatch cycle for this connection.
startDispatchCycleLocked(now(), connection);
}