am ffb49774
: Improve ANR diagnostics.
* commit 'ffb497743831ae4857b674629b58ea3c46d01431': Improve ANR diagnostics.
This commit is contained in:
commit
1222c8944a
@ -1064,6 +1064,7 @@ void InputDispatcher::resetANRTimeoutsLocked() {
|
|||||||
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
|
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
|
||||||
const EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
|
const EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
|
||||||
int32_t injectionResult;
|
int32_t injectionResult;
|
||||||
|
String8 reason;
|
||||||
|
|
||||||
// If there is no currently focused window and no focused application
|
// If there is no currently focused window and no focused application
|
||||||
// then drop the event.
|
// then drop the event.
|
||||||
@ -1088,20 +1089,12 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
|
|||||||
goto Failed;
|
goto Failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the currently focused window is paused then keep waiting.
|
// Check whether the window is ready for more input.
|
||||||
if (mFocusedWindowHandle->getInfo()->paused) {
|
reason = checkWindowReadyForMoreInputLocked(currentTime,
|
||||||
|
mFocusedWindowHandle, entry, "focused");
|
||||||
|
if (!reason.isEmpty()) {
|
||||||
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
|
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
|
||||||
mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime,
|
mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime, reason.string());
|
||||||
"Waiting because the focused window is paused.");
|
|
||||||
goto Unresponsive;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the currently focused window is still working on previous events then keep waiting.
|
|
||||||
if (!isWindowReadyForMoreInputLocked(currentTime, mFocusedWindowHandle, entry)) {
|
|
||||||
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
|
|
||||||
mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime,
|
|
||||||
"Waiting because the focused window has not finished "
|
|
||||||
"processing the input events that were previously delivered to it.");
|
|
||||||
goto Unresponsive;
|
goto Unresponsive;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1426,20 +1419,12 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
|
|||||||
for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
|
for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
|
||||||
const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
|
const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
|
||||||
if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
|
if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
|
||||||
// If the touched window is paused then keep waiting.
|
// Check whether the window is ready for more input.
|
||||||
if (touchedWindow.windowHandle->getInfo()->paused) {
|
String8 reason = checkWindowReadyForMoreInputLocked(currentTime,
|
||||||
|
touchedWindow.windowHandle, entry, "touched");
|
||||||
|
if (!reason.isEmpty()) {
|
||||||
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
|
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
|
||||||
NULL, touchedWindow.windowHandle, nextWakeupTime,
|
NULL, touchedWindow.windowHandle, nextWakeupTime, reason.string());
|
||||||
"Waiting because the touched window is paused.");
|
|
||||||
goto Unresponsive;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the touched window is still working on previous events then keep waiting.
|
|
||||||
if (!isWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle, entry)) {
|
|
||||||
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
|
|
||||||
NULL, touchedWindow.windowHandle, nextWakeupTime,
|
|
||||||
"Waiting because the touched window has not finished "
|
|
||||||
"processing the input events that were previously delivered to it.");
|
|
||||||
goto Unresponsive;
|
goto Unresponsive;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1657,29 +1642,57 @@ bool InputDispatcher::isWindowObscuredAtPointLocked(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputDispatcher::isWindowReadyForMoreInputLocked(nsecs_t currentTime,
|
String8 InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
|
||||||
const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry) {
|
const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
|
||||||
|
const char* targetType) {
|
||||||
|
// If the window is paused then keep waiting.
|
||||||
|
if (windowHandle->getInfo()->paused) {
|
||||||
|
return String8::format("Waiting because the %s window is paused.", targetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the window's connection is not registered then keep waiting.
|
||||||
ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
|
ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
|
||||||
if (connectionIndex >= 0) {
|
if (connectionIndex < 0) {
|
||||||
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
|
return String8::format("Waiting because the %s window's input channel is not "
|
||||||
if (connection->inputPublisherBlocked) {
|
"registered with the input dispatcher. The window may be in the process "
|
||||||
return false;
|
"of being removed.", targetType);
|
||||||
}
|
}
|
||||||
if (eventEntry->type == EventEntry::TYPE_KEY) {
|
|
||||||
// If the event is a key event, then we must wait for all previous events to
|
// If the connection is dead then keep waiting.
|
||||||
// complete before delivering it because previous events may have the
|
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
|
||||||
// side-effect of transferring focus to a different window and we want to
|
if (connection->status != Connection::STATUS_NORMAL) {
|
||||||
// ensure that the following keys are sent to the new window.
|
return String8::format("Waiting because the %s window's input connection is %s."
|
||||||
//
|
"The window may be in the process of being removed.", targetType,
|
||||||
// Suppose the user touches a button in a window then immediately presses "A".
|
connection->getStatusLabel());
|
||||||
// If the button causes a pop-up window to appear then we want to ensure that
|
}
|
||||||
// the "A" key is delivered to the new pop-up window. This is because users
|
|
||||||
// often anticipate pending UI changes when typing on a keyboard.
|
// If the connection is backed up then keep waiting.
|
||||||
// To obtain this behavior, we must serialize key events with respect to all
|
if (connection->inputPublisherBlocked) {
|
||||||
// prior input events.
|
return String8::format("Waiting because the %s window's input channel is full. "
|
||||||
return connection->outboundQueue.isEmpty()
|
"Outbound queue length: %d. Wait queue length: %d.",
|
||||||
&& connection->waitQueue.isEmpty();
|
targetType, connection->outboundQueue.count(), connection->waitQueue.count());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the dispatch queues aren't too far backed up for this event.
|
||||||
|
if (eventEntry->type == EventEntry::TYPE_KEY) {
|
||||||
|
// If the event is a key event, then we must wait for all previous events to
|
||||||
|
// complete before delivering it because previous events may have the
|
||||||
|
// side-effect of transferring focus to a different window and we want to
|
||||||
|
// ensure that the following keys are sent to the new window.
|
||||||
|
//
|
||||||
|
// Suppose the user touches a button in a window then immediately presses "A".
|
||||||
|
// If the button causes a pop-up window to appear then we want to ensure that
|
||||||
|
// the "A" key is delivered to the new pop-up window. This is because users
|
||||||
|
// often anticipate pending UI changes when typing on a keyboard.
|
||||||
|
// To obtain this behavior, we must serialize key events with respect to all
|
||||||
|
// prior input events.
|
||||||
|
if (!connection->outboundQueue.isEmpty() || !connection->waitQueue.isEmpty()) {
|
||||||
|
return String8::format("Waiting to send key event because the %s window has not "
|
||||||
|
"finished processing all of the input events that were previously "
|
||||||
|
"delivered to it. Outbound queue length: %d. Wait queue length: %d.",
|
||||||
|
targetType, connection->outboundQueue.count(), connection->waitQueue.count());
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
// Touch events can always be sent to a window immediately because the user intended
|
// Touch events can always be sent to a window immediately because the user intended
|
||||||
// to touch whatever was visible at the time. Even if focus changes or a new
|
// to touch whatever was visible at the time. Even if focus changes or a new
|
||||||
// window appears moments later, the touch event was meant to be delivered to
|
// window appears moments later, the touch event was meant to be delivered to
|
||||||
@ -1698,10 +1711,15 @@ bool InputDispatcher::isWindowReadyForMoreInputLocked(nsecs_t currentTime,
|
|||||||
if (!connection->waitQueue.isEmpty()
|
if (!connection->waitQueue.isEmpty()
|
||||||
&& currentTime >= connection->waitQueue.head->deliveryTime
|
&& currentTime >= connection->waitQueue.head->deliveryTime
|
||||||
+ STREAM_AHEAD_EVENT_TIMEOUT) {
|
+ STREAM_AHEAD_EVENT_TIMEOUT) {
|
||||||
return false;
|
return String8::format("Waiting to send non-key event because the %s window has not "
|
||||||
|
"finished processing certain input events that were delivered to it over "
|
||||||
|
"%0.1fms ago. Wait queue length: %d. Wait queue head age: %0.1fms.",
|
||||||
|
targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f,
|
||||||
|
connection->waitQueue.count(),
|
||||||
|
(currentTime - connection->waitQueue.head->deliveryTime) * 0.000001f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return String8::empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
String8 InputDispatcher::getApplicationWindowLabelLocked(
|
String8 InputDispatcher::getApplicationWindowLabelLocked(
|
||||||
|
@ -1038,11 +1038,13 @@ private:
|
|||||||
const InjectionState* injectionState);
|
const InjectionState* injectionState);
|
||||||
bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
|
bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
|
||||||
int32_t x, int32_t y) const;
|
int32_t x, int32_t y) const;
|
||||||
bool isWindowReadyForMoreInputLocked(nsecs_t currentTime,
|
|
||||||
const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry);
|
|
||||||
String8 getApplicationWindowLabelLocked(const sp<InputApplicationHandle>& applicationHandle,
|
String8 getApplicationWindowLabelLocked(const sp<InputApplicationHandle>& applicationHandle,
|
||||||
const sp<InputWindowHandle>& windowHandle);
|
const sp<InputWindowHandle>& windowHandle);
|
||||||
|
|
||||||
|
String8 checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
|
||||||
|
const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
|
||||||
|
const char* targetType);
|
||||||
|
|
||||||
// Manage the dispatch cycle for a single connection.
|
// Manage the dispatch cycle for a single connection.
|
||||||
// These methods are deliberately not Interruptible because doing all of the work
|
// These methods are deliberately not Interruptible because doing all of the work
|
||||||
// with the mutex held makes it easier to ensure that connection invariants are maintained.
|
// with the mutex held makes it easier to ensure that connection invariants are maintained.
|
||||||
|
Loading…
Reference in New Issue
Block a user