am 465c8f13: am 2e5f8eaa: am ffb49774: Improve ANR diagnostics.

* commit '465c8f1387b231133fb29bdeb0eb685c30e8da00':
  Improve ANR diagnostics.
This commit is contained in:
Jeff Brown 2014-10-12 16:12:42 +00:00 committed by Android Git Automerger
commit 27d0e598fd
2 changed files with 71 additions and 51 deletions

View File

@ -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,14 +1642,38 @@ 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,
ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel()); const char* targetType) {
if (connectionIndex >= 0) { // If the window is paused then keep waiting.
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex); if (windowHandle->getInfo()->paused) {
if (connection->inputPublisherBlocked) { return String8::format("Waiting because the %s window is paused.", targetType);
return false;
} }
// If the window's connection is not registered then keep waiting.
ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
if (connectionIndex < 0) {
return String8::format("Waiting because the %s window's input channel is not "
"registered with the input dispatcher. The window may be in the process "
"of being removed.", targetType);
}
// If the connection is dead then keep waiting.
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
if (connection->status != Connection::STATUS_NORMAL) {
return String8::format("Waiting because the %s window's input connection is %s."
"The window may be in the process of being removed.", targetType,
connection->getStatusLabel());
}
// If the connection is backed up then keep waiting.
if (connection->inputPublisherBlocked) {
return String8::format("Waiting because the %s window's input channel is full. "
"Outbound queue length: %d. Wait queue length: %d.",
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 (eventEntry->type == EventEntry::TYPE_KEY) {
// If the event is a key event, then we must wait for all previous events to // 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 // complete before delivering it because previous events may have the
@ -1677,9 +1686,13 @@ bool InputDispatcher::isWindowReadyForMoreInputLocked(nsecs_t currentTime,
// often anticipate pending UI changes when typing on a keyboard. // often anticipate pending UI changes when typing on a keyboard.
// To obtain this behavior, we must serialize key events with respect to all // To obtain this behavior, we must serialize key events with respect to all
// prior input events. // prior input events.
return connection->outboundQueue.isEmpty() if (!connection->outboundQueue.isEmpty() || !connection->waitQueue.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(

View File

@ -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.