Make input dispatcher only ANR for foreground windows.
Redesigned the input dispatcher's ANR timeout mechanism so it is much
closer to Froyo's policy. ANR is only ever signalled if the dispatcher
is waiting on a window to finish processing its previous event(s) and
there is new pending input.
In the old code, we tracked the dispatch timeout separately for each
input channel. This was somewhat complicated and also resulted in the
situation where applications could ANR long after the user had pushed
them into the background.
Change-Id: I666ecada0952d4b95f1d67b9f733842b745c7f4b
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index d7e6254..e466ddd 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -81,9 +81,8 @@
*/
struct InputTarget {
enum {
- /* This flag indicates that subsequent event delivery should be held until the
- * current event is delivered to this target or a timeout occurs. */
- FLAG_SYNC = 0x01,
+ /* This flag indicates that the event is being delivered to a foreground application. */
+ FLAG_FOREGROUND = 0x01,
/* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside
* of the area of this target and so should instead be delivered as an
@@ -109,12 +108,6 @@
// Flags for the input target.
int32_t flags;
- // The timeout for event delivery to this target in nanoseconds, or -1 to wait indefinitely.
- nsecs_t timeout;
-
- // The time already spent waiting for this target in nanoseconds, or 0 if none.
- nsecs_t timeSpentWaitingForApplication;
-
// The x and y offset to add to a MotionEvent as it is delivered.
// (ignored for KeyEvents)
float xOffset, yOffset;
@@ -190,6 +183,7 @@
};
sp<InputChannel> inputChannel;
+ String8 name;
int32_t layoutParamsFlags;
int32_t layoutParamsType;
nsecs_t dispatchingTimeout;
@@ -206,9 +200,11 @@
int32_t touchableAreaRight;
int32_t touchableAreaBottom;
bool visible;
+ bool canReceiveKeys;
bool hasFocus;
bool hasWallpaper;
bool paused;
+ int32_t layer;
int32_t ownerPid;
int32_t ownerUid;
@@ -257,18 +253,12 @@
/* Notifies the system that an application is not responding.
* Returns a new timeout to continue waiting, or 0 to abort dispatch. */
- virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
+ virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<InputChannel>& inputChannel) = 0;
/* Notifies the system that an input channel is unrecoverably broken. */
virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel) = 0;
- /* Notifies the system that an input channel is not responding.
- * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
- virtual nsecs_t notifyInputChannelANR(const sp<InputChannel>& inputChannel) = 0;
-
- /* Notifies the system that an input channel recovered from ANR. */
- virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) = 0;
-
/* Gets the key repeat initial timeout or -1 if automatic key repeating is disabled. */
virtual nsecs_t getKeyRepeatTimeout() = 0;
@@ -361,16 +351,6 @@
*/
virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;
- /* Preempts input dispatch in progress by making pending synchronous
- * dispatches asynchronous instead. This method is generally called during a focus
- * transition from one application to the next so as to enable the new application
- * to start receiving input as soon as possible without having to wait for the
- * old application to finish up.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual void preemptInputDispatch() = 0;
-
/* Registers or unregister input channels that may be used as targets for input events.
* If monitor is true, the channel will receive a copy of all input events.
*
@@ -424,7 +404,6 @@
virtual void setInputWindows(const Vector<InputWindow>& inputWindows);
virtual void setFocusedApplication(const InputApplication* inputApplication);
virtual void setInputDispatchMode(bool enabled, bool frozen);
- virtual void preemptInputDispatch();
virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor);
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
@@ -454,7 +433,7 @@
int32_t injectorUid; // -1 if not injected
bool dispatchInProgress; // initially false, set to true while dispatching
- int32_t pendingSyncDispatches; // the number of synchronous dispatches in progress
+ int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
inline bool isInjected() { return injectorPid >= 0; }
@@ -522,7 +501,6 @@
int32_t targetFlags;
float xOffset;
float yOffset;
- nsecs_t timeout;
// True if dispatch has started.
bool inProgress;
@@ -540,12 +518,8 @@
// will be set to NULL.
MotionSample* tailMotionSample;
- inline bool isSyncTarget() const {
- return targetFlags & InputTarget::FLAG_SYNC;
- }
-
- inline void preemptSyncTarget() {
- targetFlags &= ~ InputTarget::FLAG_SYNC;
+ inline bool hasForegroundTarget() const {
+ return targetFlags & InputTarget::FLAG_FOREGROUND;
}
};
@@ -628,6 +602,8 @@
dequeue(first);
return first;
}
+
+ uint32_t count() const;
};
/* Allocates queue entries and performs reference counting as needed. */
@@ -647,7 +623,7 @@
nsecs_t downTime, uint32_t pointerCount,
const int32_t* pointerIds, const PointerCoords* pointerCoords);
DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry,
- int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout);
+ int32_t targetFlags, float xOffset, float yOffset);
CommandEntry* obtainCommandEntry(Command command);
void releaseEventEntry(EventEntry* entry);
@@ -761,8 +737,6 @@
STATUS_NORMAL,
// An unrecoverable communication error has occurred.
STATUS_BROKEN,
- // The client is not responding.
- STATUS_NOT_RESPONDING,
// The input channel has been unregistered.
STATUS_ZOMBIE
};
@@ -772,11 +746,9 @@
InputPublisher inputPublisher;
InputState inputState;
Queue<DispatchEntry> outboundQueue;
- nsecs_t nextTimeoutTime; // next timeout time (LONG_LONG_MAX if none)
nsecs_t lastEventTime; // the time when the event was originally captured
nsecs_t lastDispatchTime; // the time when the last event was dispatched
- nsecs_t lastANRTime; // the time when the last ANR was recorded
explicit Connection(const sp<InputChannel>& inputChannel);
@@ -788,18 +760,6 @@
// Returns NULL if not found.
DispatchEntry* findQueuedDispatchEntryForEvent(const EventEntry* eventEntry) const;
- // Determine whether this connection has a pending synchronous dispatch target.
- // Since there can only ever be at most one such target at a time, if there is one,
- // it must be at the tail because nothing else can be enqueued after it.
- inline bool hasPendingSyncTarget() const {
- return ! outboundQueue.isEmpty() && outboundQueue.tailSentinel.prev->isSyncTarget();
- }
-
- // Assuming there is a pending sync target, make it async.
- inline void preemptSyncTarget() {
- outboundQueue.tailSentinel.prev->preemptSyncTarget();
- }
-
// Gets the time since the current event was originally obtained from the input driver.
inline double getEventLatencyMillis(nsecs_t currentTime) const {
return (currentTime - lastEventTime) / 1000000.0;
@@ -810,15 +770,7 @@
return (currentTime - lastDispatchTime) / 1000000.0;
}
- // Gets the time since the current event ANR was declared, if applicable.
- inline double getANRLatencyMillis(nsecs_t currentTime) const {
- return (currentTime - lastANRTime) / 1000000.0;
- }
-
status_t initialize();
-
- void setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout);
- void resetTimeout(nsecs_t currentTime);
};
sp<InputDispatcherPolicyInterface> mPolicy;
@@ -851,7 +803,7 @@
// All registered connections mapped by receive pipe file descriptor.
KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
- ssize_t getConnectionIndex(const sp<InputChannel>& inputChannel);
+ ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel);
// Active connections are connections that have a non-empty outbound queue.
// We don't use a ref-counted pointer here because we explicitly abort connections
@@ -859,12 +811,6 @@
// and the connection itself to be deactivated.
Vector<Connection*> mActiveConnections;
- // List of connections that have timed out. Only used by dispatchOnce()
- // We don't use a ref-counted pointer here because it is not possible for a connection
- // to be unregistered while processing timed out connections since we hold the lock for
- // the duration.
- Vector<Connection*> mTimedOutConnections;
-
// Input channels that will receive a copy of all input events.
Vector<sp<InputChannel> > mMonitoringChannels;
@@ -877,7 +823,7 @@
void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
Condition mInjectionSyncFinishedCondition;
- void decrementPendingSyncDispatchesLocked(EventEntry* entry);
+ void decrementPendingForegroundDispatchesLocked(EventEntry* entry);
// Throttling state.
struct ThrottleState {
@@ -951,8 +897,6 @@
void logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry);
// The input targets that were most recently identified for dispatch.
- // If there is a synchronous event dispatch in progress, the current input targets will
- // remain unchanged until the dispatch has completed or been aborted.
bool mCurrentInputTargetsValid; // false while targets are being recomputed
Vector<InputTarget> mCurrentInputTargets;
int32_t mCurrentInputWindowType;
@@ -975,8 +919,9 @@
int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
const InputApplication* application, const InputWindow* window,
nsecs_t* nextWakeupTime);
- void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout);
- nsecs_t getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(nsecs_t currentTime);
+ void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
+ const sp<InputChannel>& inputChannel);
+ nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime);
void resetANRTimeoutsLocked();
int32_t findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
@@ -984,14 +929,16 @@
int32_t findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry,
nsecs_t* nextWakeupTime, InputWindow** outWindow);
- void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
- nsecs_t timeSpentWaitingForApplication);
+ void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags);
void addMonitoringTargetsLocked();
void pokeUserActivityLocked(nsecs_t eventTime, int32_t windowType, int32_t eventType);
bool checkInjectionPermission(const InputWindow* window,
int32_t injectorPid, int32_t injectorUid);
bool isWindowObscuredLocked(const InputWindow* window);
+ bool isWindowFinishedWithPreviousInputLocked(const InputWindow* window);
void releaseTouchedWindowLocked();
+ String8 getApplicationWindowLabelLocked(const InputApplication* application,
+ const InputWindow* window);
// Manage the dispatch cycle for a single connection.
// These methods are deliberately not Interruptible because doing all of the work
@@ -1000,21 +947,14 @@
void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
EventEntry* eventEntry, const InputTarget* inputTarget,
bool resumeWithAppendedMotionSample);
- void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- nsecs_t timeSpentWaitingForApplication);
+ void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
void startNextDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
- void timeoutDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
- void resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection, nsecs_t newTimeout);
void abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
bool broken);
- void drainOutboundQueueLocked(Connection* connection, DispatchEntry* firstDispatchEntryToDrain);
+ void drainOutboundQueueLocked(Connection* connection);
static int handleReceiveCallback(int receiveFd, int events, void* data);
- // Preempting input dispatch.
- bool preemptInputDispatchInnerLocked();
-
// Dump state.
void dumpDispatchStateLocked(String8& dump);
void logDispatchStateLocked();
@@ -1027,20 +967,23 @@
void onDispatchCycleStartedLocked(
nsecs_t currentTime, const sp<Connection>& connection);
void onDispatchCycleFinishedLocked(
- nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR);
- void onDispatchCycleANRLocked(
nsecs_t currentTime, const sp<Connection>& connection);
void onDispatchCycleBrokenLocked(
nsecs_t currentTime, const sp<Connection>& connection);
+ void onANRLocked(
+ nsecs_t currentTime, const InputApplication* application, const InputWindow* window,
+ nsecs_t eventTime, nsecs_t waitStartTime);
// Outbound policy interactions.
void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry);
void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
- void doNotifyInputChannelANRLockedInterruptible(CommandEntry* commandEntry);
- void doNotifyInputChannelRecoveredFromANRLockedInterruptible(CommandEntry* commandEntry);
+ void doNotifyANRLockedInterruptible(CommandEntry* commandEntry);
void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry);
void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry);
- void doTargetsNotReadyTimeoutLockedInterruptible(CommandEntry* commandEntry);
+
+ // Statistics gathering.
+ void updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
+ int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
};
/* Enqueues and dispatches input events, endlessly. */
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 48dea57..3b3c483 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -196,42 +196,6 @@
*nextWakeupTime = mAppSwitchDueTime;
}
- // Detect and process timeouts for all connections and determine if there are any
- // synchronous event dispatches pending. This step is entirely non-interruptible.
- bool havePendingSyncTarget = false;
- size_t activeConnectionCount = mActiveConnections.size();
- for (size_t i = 0; i < activeConnectionCount; i++) {
- Connection* connection = mActiveConnections.itemAt(i);
-
- if (connection->hasPendingSyncTarget()) {
- if (isAppSwitchDue) {
- connection->preemptSyncTarget();
- } else {
- havePendingSyncTarget = true;
- }
- }
-
- nsecs_t connectionTimeoutTime = connection->nextTimeoutTime;
- if (connectionTimeoutTime <= currentTime) {
- mTimedOutConnections.add(connection);
- } else if (connectionTimeoutTime < *nextWakeupTime) {
- *nextWakeupTime = connectionTimeoutTime;
- }
- }
-
- size_t timedOutConnectionCount = mTimedOutConnections.size();
- for (size_t i = 0; i < timedOutConnectionCount; i++) {
- Connection* connection = mTimedOutConnections.itemAt(i);
- timeoutDispatchCycleLocked(currentTime, connection);
- *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
- }
- mTimedOutConnections.clear();
-
- // If we have a pending synchronous target, skip dispatch.
- if (havePendingSyncTarget) {
- return;
- }
-
// Ready to start a new event.
// If we don't already have a pending event, go grab one.
if (! mPendingEvent) {
@@ -557,7 +521,7 @@
}
entry->dispatchInProgress = true;
- startFindingTargetsLocked();
+ startFindingTargetsLocked(); // resets mCurrentInputTargetsValid
}
// Identify targets.
@@ -618,7 +582,7 @@
logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
entry->dispatchInProgress = true;
- startFindingTargetsLocked();
+ startFindingTargetsLocked(); // resets mCurrentInputTargetsValid
}
bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
@@ -728,7 +692,7 @@
for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
- ssize_t connectionIndex = getConnectionIndex(inputTarget.inputChannel);
+ ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
@@ -770,9 +734,8 @@
} else {
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
#if DEBUG_FOCUS
- LOGD("Waiting for application to become ready for input: name=%s, window=%s",
- application ? application->name.string() : "<unknown>",
- window ? window->inputChannel->getName().string() : "<unknown>");
+ LOGD("Waiting for application to become ready for input: %s",
+ getApplicationWindowLabelLocked(application, window).string());
#endif
nsecs_t timeout = window ? window->dispatchingTimeout :
application ? application->dispatchingTimeout : DEFAULT_INPUT_DISPATCHING_TIMEOUT;
@@ -789,21 +752,7 @@
}
if (currentTime >= mInputTargetWaitTimeoutTime) {
- LOGI("Application is not ready for input: name=%s, window=%s,"
- "%01.1fms since event, %01.1fms since wait started",
- application ? application->name.string() : "<unknown>",
- window ? window->inputChannel->getName().string() : "<unknown>",
- (currentTime - entry->eventTime) / 1000000.0,
- (currentTime - mInputTargetWaitStartTime) / 1000000.0);
-
- CommandEntry* commandEntry = postCommandLocked(
- & InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible);
- if (application) {
- commandEntry->inputApplicationHandle = application->handle;
- }
- if (window) {
- commandEntry->inputChannel = window->inputChannel;
- }
+ onANRLocked(currentTime, application, window, entry->eventTime, mInputTargetWaitStartTime);
// Force poll loop to wake up immediately on next iteration once we get the
// ANR response back from the policy.
@@ -818,17 +767,25 @@
}
}
-void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout) {
+void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
+ const sp<InputChannel>& inputChannel) {
if (newTimeout > 0) {
// Extend the timeout.
mInputTargetWaitTimeoutTime = now() + newTimeout;
} else {
// Give up.
mInputTargetWaitTimeoutExpired = true;
+
+ // Input state will not be realistic. Mark it out of sync.
+ ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
+ if (connectionIndex >= 0) {
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ connection->inputState.setOutOfSync();
+ }
}
}
-nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(
+nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(
nsecs_t currentTime) {
if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
return currentTime - mInputTargetWaitStartTime;
@@ -841,13 +798,6 @@
LOGD("Resetting ANR timeouts.");
#endif
- // Reset timeouts for all active connections.
- nsecs_t currentTime = now();
- for (size_t i = 0; i < mActiveConnections.size(); i++) {
- Connection* connection = mActiveConnections[i];
- connection->resetTimeout(currentTime);
- }
-
// Reset input target wait timeout.
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
}
@@ -865,8 +815,8 @@
if (mFocusedApplication) {
#if DEBUG_FOCUS
LOGD("Waiting because there is no focused window but there is a "
- "focused application that may eventually add a window: '%s'.",
- mFocusedApplication->name.string());
+ "focused application that may eventually add a window: %s.",
+ getApplicationWindowLabelLocked(mFocusedApplication, NULL).string());
#endif
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplication, NULL, nextWakeupTime);
@@ -894,19 +844,31 @@
goto Unresponsive;
}
+ // If the currently focused window is still working on previous events then keep waiting.
+ if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindow)) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because focused window still processing previous input.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ mFocusedApplication, mFocusedWindow, nextWakeupTime);
+ goto Unresponsive;
+ }
+
// Success! Output targets.
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
*outWindow = mFocusedWindow;
- addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_SYNC,
- getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime));
+ addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND);
// Done.
Failed:
Unresponsive:
+ nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
+ updateDispatchStatisticsLocked(currentTime, entry,
+ injectionResult, timeSpentWaitingForApplication);
#if DEBUG_FOCUS
- LOGD("findFocusedWindow finished: injectionResult=%d",
- injectionResult);
- logDispatchStateLocked();
+ LOGD("findFocusedWindow finished: injectionResult=%d, "
+ "timeSpendWaitingForApplication=%0.1fms",
+ injectionResult, timeSpentWaitingForApplication / 1000000.0);
#endif
return injectionResult;
}
@@ -1018,8 +980,8 @@
if (mFocusedApplication) {
#if DEBUG_FOCUS
LOGD("Waiting because there is no touched window but there is a "
- "focused application that may eventually add a new window: '%s'.",
- mFocusedApplication->name.string());
+ "focused application that may eventually add a new window: %s.",
+ getApplicationWindowLabelLocked(mFocusedApplication, NULL).string());
#endif
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplication, NULL, nextWakeupTime);
@@ -1051,6 +1013,17 @@
goto Unresponsive;
}
+ // If the touched window is still working on previous events then keep waiting.
+ if (! isWindowFinishedWithPreviousInputLocked(newTouchedWindow)) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because touched window still processing previous input.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ NULL, newTouchedWindow, nextWakeupTime);
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ goto Unresponsive;
+ }
+
// Success! Update the touch dispatch state for real.
releaseTouchedWindowLocked();
@@ -1098,6 +1071,17 @@
injectionPermission = INJECTION_PERMISSION_GRANTED;
goto Unresponsive;
}
+
+ // If the touched window is still working on previous events then keep waiting.
+ if (! isWindowFinishedWithPreviousInputLocked(mTouchedWindow)) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because touched window still processing previous input.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ NULL, mTouchedWindow, nextWakeupTime);
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ goto Unresponsive;
+ }
}
// Success! Output targets.
@@ -1108,7 +1092,7 @@
size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
for (size_t i = 0; i < numWallpaperWindows; i++) {
addWindowTargetLocked(mTouchedWallpaperWindows[i],
- InputTarget::FLAG_WINDOW_IS_OBSCURED, 0);
+ InputTarget::FLAG_WINDOW_IS_OBSCURED);
}
size_t numOutsideTargets = mTempTouchedOutsideTargets.size();
@@ -1118,16 +1102,15 @@
if (outsideTarget.obscured) {
outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
}
- addWindowTargetLocked(outsideTarget.window, outsideTargetFlags, 0);
+ addWindowTargetLocked(outsideTarget.window, outsideTargetFlags);
}
mTempTouchedOutsideTargets.clear();
- int32_t targetFlags = InputTarget::FLAG_SYNC;
+ int32_t targetFlags = InputTarget::FLAG_FOREGROUND;
if (mTouchedWindowIsObscured) {
targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
}
- addWindowTargetLocked(mTouchedWindow, targetFlags,
- getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime));
+ addWindowTargetLocked(mTouchedWindow, targetFlags);
*outWindow = mTouchedWindow;
}
@@ -1166,10 +1149,13 @@
}
Unresponsive:
+ nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
+ updateDispatchStatisticsLocked(currentTime, entry,
+ injectionResult, timeSpentWaitingForApplication);
#if DEBUG_FOCUS
- LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d",
- injectionResult, injectionPermission);
- logDispatchStateLocked();
+ LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d,"
+ "timeSpendWaitingForApplication=%0.1fms",
+ injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
#endif
return injectionResult;
}
@@ -1180,15 +1166,12 @@
mTouchedWallpaperWindows.clear();
}
-void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
- nsecs_t timeSpentWaitingForApplication) {
+void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags) {
mCurrentInputTargets.push();
InputTarget& target = mCurrentInputTargets.editTop();
target.inputChannel = window->inputChannel;
target.flags = targetFlags;
- target.timeout = window->dispatchingTimeout;
- target.timeSpentWaitingForApplication = timeSpentWaitingForApplication;
target.xOffset = - window->frameLeft;
target.yOffset = - window->frameTop;
}
@@ -1200,8 +1183,6 @@
InputTarget& target = mCurrentInputTargets.editTop();
target.inputChannel = mMonitoringChannels[i];
target.flags = 0;
- target.timeout = -1;
- target.timeSpentWaitingForApplication = 0;
target.xOffset = 0;
target.yOffset = 0;
}
@@ -1241,6 +1222,34 @@
return false;
}
+bool InputDispatcher::isWindowFinishedWithPreviousInputLocked(const InputWindow* window) {
+ ssize_t connectionIndex = getConnectionIndexLocked(window->inputChannel);
+ if (connectionIndex >= 0) {
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ return connection->outboundQueue.isEmpty();
+ } else {
+ return true;
+ }
+}
+
+String8 InputDispatcher::getApplicationWindowLabelLocked(const InputApplication* application,
+ const InputWindow* window) {
+ if (application) {
+ if (window) {
+ String8 label(application->name);
+ label.append(" - ");
+ label.append(window->name);
+ return label;
+ } else {
+ return application->name;
+ }
+ } else if (window) {
+ return window->name;
+ } else {
+ return String8("<unknown application or window>");
+ }
+}
+
void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime,
int32_t windowType, int32_t eventType) {
CommandEntry* commandEntry = postCommandLocked(
@@ -1254,25 +1263,18 @@
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
bool resumeWithAppendedMotionSample) {
#if DEBUG_DISPATCH_CYCLE
- LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, timeout=%lldns, "
+ LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, "
"xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s",
- connection->getInputChannelName(), inputTarget->flags, inputTarget->timeout,
+ connection->getInputChannelName(), inputTarget->flags,
inputTarget->xOffset, inputTarget->yOffset,
toString(resumeWithAppendedMotionSample));
#endif
// Skip this event if the connection status is not normal.
- // We don't want to enqueue additional outbound events if the connection is broken or
- // not responding.
+ // We don't want to enqueue additional outbound events if the connection is broken.
if (connection->status != Connection::STATUS_NORMAL) {
LOGW("channel '%s' ~ Dropping event because the channel status is %s",
connection->getInputChannelName(), connection->getStatusLabel());
-
- // If the connection is not responding but the user is poking the application anyways,
- // retrigger the original timeout.
- if (connection->status == Connection::STATUS_NOT_RESPONDING) {
- timeoutDispatchCycleLocked(currentTime, connection);
- }
return;
}
@@ -1379,7 +1381,7 @@
DispatchEntry* cancelationDispatchEntry =
mAllocator.obtainDispatchEntry(cancelationEventEntry,
- 0, inputTarget->xOffset, inputTarget->yOffset, inputTarget->timeout);
+ 0, inputTarget->xOffset, inputTarget->yOffset); // increments ref
connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry);
mAllocator.releaseEventEntry(cancelationEventEntry);
@@ -1390,10 +1392,9 @@
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref
- inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset,
- inputTarget->timeout);
- if (dispatchEntry->isSyncTarget()) {
- eventEntry->pendingSyncDispatches += 1;
+ inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset);
+ if (dispatchEntry->hasForegroundTarget()) {
+ eventEntry->pendingForegroundDispatches += 1;
}
// Handle the case where we could not stream a new motion sample because the consumer has
@@ -1416,13 +1417,12 @@
// If the outbound queue was previously empty, start the dispatch cycle going.
if (wasEmpty) {
activateConnectionLocked(connection.get());
- startDispatchCycleLocked(currentTime, connection,
- inputTarget->timeSpentWaitingForApplication);
+ startDispatchCycleLocked(currentTime, connection);
}
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection, nsecs_t timeSpentWaitingForApplication) {
+ const sp<Connection>& connection) {
#if DEBUG_DISPATCH_CYCLE
LOGD("channel '%s' ~ startDispatchCycle",
connection->getInputChannelName());
@@ -1588,9 +1588,6 @@
connection->lastEventTime = dispatchEntry->eventEntry->eventTime;
connection->lastDispatchTime = currentTime;
- nsecs_t timeout = dispatchEntry->timeout - timeSpentWaitingForApplication;
- connection->setNextTimeoutTime(currentTime, timeout);
-
// Notify other system components.
onDispatchCycleStartedLocked(currentTime, connection);
}
@@ -1610,21 +1607,8 @@
return;
}
- // Clear the pending timeout.
- connection->nextTimeoutTime = LONG_LONG_MAX;
-
- if (connection->status == Connection::STATUS_NOT_RESPONDING) {
- // Recovering from an ANR.
- connection->status = Connection::STATUS_NORMAL;
-
- // Notify other system components.
- onDispatchCycleFinishedLocked(currentTime, connection, true /*recoveredFromANR*/);
- } else {
- // Normal finish. Not much to do here.
-
- // Notify other system components.
- onDispatchCycleFinishedLocked(currentTime, connection, false /*recoveredFromANR*/);
- }
+ // Notify other system components.
+ onDispatchCycleFinishedLocked(currentTime, connection);
// Reset the publisher since the event has been consumed.
// We do this now so that the publisher can release some of its internal resources
@@ -1653,20 +1637,20 @@
dispatchEntry->inProgress = false;
dispatchEntry->headMotionSample = dispatchEntry->tailMotionSample;
dispatchEntry->tailMotionSample = NULL;
- startDispatchCycleLocked(currentTime, connection, 0);
+ startDispatchCycleLocked(currentTime, connection);
return;
}
// Finished.
connection->outboundQueue.dequeueAtHead();
- if (dispatchEntry->isSyncTarget()) {
- decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
+ if (dispatchEntry->hasForegroundTarget()) {
+ decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry);
}
mAllocator.releaseDispatchEntry(dispatchEntry);
} else {
// If the head is not in progress, then we must have already dequeued the in
- // progress event, which means we actually aborted it (due to ANR).
+ // progress event, which means we actually aborted it.
// So just start the next event for this connection.
- startDispatchCycleLocked(currentTime, connection, 0);
+ startDispatchCycleLocked(currentTime, connection);
return;
}
}
@@ -1675,66 +1659,6 @@
deactivateConnectionLocked(connection.get());
}
-void InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection) {
-#if DEBUG_DISPATCH_CYCLE
- LOGD("channel '%s' ~ timeoutDispatchCycle",
- connection->getInputChannelName());
-#endif
-
- if (connection->status == Connection::STATUS_NORMAL) {
- // Enter the not responding state.
- connection->status = Connection::STATUS_NOT_RESPONDING;
- connection->lastANRTime = currentTime;
- } else if (connection->status != Connection::STATUS_NOT_RESPONDING) {
- // Connection is broken or dead.
- return;
- }
-
- // Notify other system components.
- // This enqueues a command which will eventually call resumeAfterTimeoutDispatchCycleLocked.
- onDispatchCycleANRLocked(currentTime, connection);
-}
-
-void InputDispatcher::resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection, nsecs_t newTimeout) {
-#if DEBUG_DISPATCH_CYCLE
- LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked - newTimeout=%lld",
- connection->getInputChannelName(), newTimeout);
-#endif
-
- if (connection->status != Connection::STATUS_NOT_RESPONDING) {
- return;
- }
-
- if (newTimeout > 0) {
- // The system has decided to give the application some more time.
- // Keep waiting synchronously and resume normal dispatch.
- connection->status = Connection::STATUS_NORMAL;
- connection->setNextTimeoutTime(currentTime, newTimeout);
- } else {
- // The system is about to throw up an ANR dialog and has requested that we abort dispatch.
- // Reset the timeout.
- connection->nextTimeoutTime = LONG_LONG_MAX;
-
- // Input state will no longer be realistic.
- connection->inputState.setOutOfSync();
-
- if (! connection->outboundQueue.isEmpty()) {
- // Make the current pending dispatch asynchronous (if it isn't already) so that
- // subsequent events can be delivered to the ANR dialog or to another application.
- DispatchEntry* currentDispatchEntry = connection->outboundQueue.headSentinel.next;
- currentDispatchEntry->preemptSyncTarget();
-
- // Drain all but the first entry in the outbound queue. We keep the first entry
- // since that is the one that dispatch is stuck on. We throw away the others
- // so that we don't spam the application with stale messages if it eventually
- // wakes up and recovers from the ANR.
- drainOutboundQueueLocked(connection.get(), currentDispatchEntry->next);
- }
- }
-}
-
void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, bool broken) {
#if DEBUG_DISPATCH_CYCLE
@@ -1742,20 +1666,16 @@
connection->getInputChannelName(), toString(broken));
#endif
- // Clear the pending timeout.
- connection->nextTimeoutTime = LONG_LONG_MAX;
-
// Input state will no longer be realistic.
connection->inputState.setOutOfSync();
// Clear the outbound queue.
- drainOutboundQueueLocked(connection.get(), connection->outboundQueue.headSentinel.next);
+ drainOutboundQueueLocked(connection.get());
// Handle the case where the connection appears to be unrecoverably broken.
// Ignore already broken or zombie connections.
if (broken) {
- if (connection->status == Connection::STATUS_NORMAL
- || connection->status == Connection::STATUS_NOT_RESPONDING) {
+ if (connection->status == Connection::STATUS_NORMAL) {
connection->status = Connection::STATUS_BROKEN;
// Notify other system components.
@@ -1764,24 +1684,16 @@
}
}
-void InputDispatcher::drainOutboundQueueLocked(Connection* connection,
- DispatchEntry* firstDispatchEntryToDrain) {
- for (DispatchEntry* dispatchEntry = firstDispatchEntryToDrain;
- dispatchEntry != & connection->outboundQueue.tailSentinel;) {
- DispatchEntry* next = dispatchEntry->next;
- connection->outboundQueue.dequeue(dispatchEntry);
-
- if (dispatchEntry->isSyncTarget()) {
- decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
+void InputDispatcher::drainOutboundQueueLocked(Connection* connection) {
+ while (! connection->outboundQueue.isEmpty()) {
+ DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
+ if (dispatchEntry->hasForegroundTarget()) {
+ decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry);
}
mAllocator.releaseDispatchEntry(dispatchEntry);
-
- dispatchEntry = next;
}
- if (connection->outboundQueue.isEmpty()) {
- deactivateConnectionLocked(connection);
- }
+ deactivateConnectionLocked(connection);
}
int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) {
@@ -1941,56 +1853,63 @@
// STREAMING CASE
//
// There is no pending motion event (of any kind) for this device in the inbound queue.
- // Search the outbound queues for a synchronously dispatched motion event for this
- // device. If found, then we append the new sample to that event and then try to
- // push it out to all current targets. It is possible that some targets will already
- // have consumed the motion event. This case is automatically handled by the
- // logic in prepareDispatchCycleLocked by tracking where resumption takes place.
- //
- // The reason we look for a synchronously dispatched motion event is because we
- // want to be sure that no other motion events have been dispatched since the move.
- // It's also convenient because it means that the input targets are still valid.
- // This code could be improved to support streaming of asynchronously dispatched
- // motion events (which might be significantly more efficient) but it may become
- // a little more complicated as a result.
- //
- // Note: This code crucially depends on the invariant that an outbound queue always
- // contains at most one synchronous event and it is always last (but it might
- // not be first!).
+ // Search the outbound queue for the current foreground targets to find a dispatched
+ // motion event that is still in progress. If found, then, appen the new sample to
+ // that event and push it out to all current targets. The logic in
+ // prepareDispatchCycleLocked takes care of the case where some targets may
+ // already have consumed the motion event by starting a new dispatch cycle if needed.
if (mCurrentInputTargetsValid) {
- for (size_t i = 0; i < mActiveConnections.size(); i++) {
- Connection* connection = mActiveConnections.itemAt(i);
- if (! connection->outboundQueue.isEmpty()) {
- DispatchEntry* dispatchEntry = connection->outboundQueue.tailSentinel.prev;
- if (dispatchEntry->isSyncTarget()) {
- if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
- goto NoBatchingOrStreaming;
- }
-
- MotionEntry* syncedMotionEntry = static_cast<MotionEntry*>(
- dispatchEntry->eventEntry);
- if (syncedMotionEntry->action != AMOTION_EVENT_ACTION_MOVE
- || syncedMotionEntry->deviceId != deviceId
- || syncedMotionEntry->pointerCount != pointerCount
- || syncedMotionEntry->isInjected()) {
- goto NoBatchingOrStreaming;
- }
-
- // Found synced move entry. Append sample and resume dispatch.
- mAllocator.appendMotionSample(syncedMotionEntry, eventTime,
- pointerCoords);
- #if DEBUG_BATCHING
- LOGD("Appended motion sample onto batch for most recent synchronously "
- "dispatched motion event for this device in the outbound queues.");
- #endif
- nsecs_t currentTime = now();
- dispatchEventToCurrentInputTargetsLocked(currentTime, syncedMotionEntry,
- true /*resumeWithAppendedMotionSample*/);
-
- runCommandsLockedInterruptible();
- return; // done!
- }
+ for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
+ const InputTarget& inputTarget = mCurrentInputTargets[i];
+ if ((inputTarget.flags & InputTarget::FLAG_FOREGROUND) == 0) {
+ // Skip non-foreground targets. We only want to stream if there is at
+ // least one foreground target whose dispatch is still in progress.
+ continue;
}
+
+ ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
+ if (connectionIndex < 0) {
+ // Connection must no longer be valid.
+ continue;
+ }
+
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ if (connection->outboundQueue.isEmpty()) {
+ // This foreground target has an empty outbound queue.
+ continue;
+ }
+
+ DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
+ if (! dispatchEntry->inProgress
+ || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
+ // No motion event is being dispatched.
+ continue;
+ }
+
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(
+ dispatchEntry->eventEntry);
+ if (motionEntry->action != AMOTION_EVENT_ACTION_MOVE
+ || motionEntry->deviceId != deviceId
+ || motionEntry->pointerCount != pointerCount
+ || motionEntry->isInjected()) {
+ // The motion event is not compatible with this move.
+ continue;
+ }
+
+ // Hurray! This foreground target is currently dispatching a move event
+ // that we can stream onto. Append the motion sample and resume dispatch.
+ mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords);
+#if DEBUG_BATCHING
+ LOGD("Appended motion sample onto batch for most recently dispatched "
+ "motion event for this device in the outbound queues. "
+ "Attempting to stream the motion sample.");
+#endif
+ nsecs_t currentTime = now();
+ dispatchEventToCurrentInputTargetsLocked(currentTime, motionEntry,
+ true /*resumeWithAppendedMotionSample*/);
+
+ runCommandsLockedInterruptible();
+ return; // done!
}
}
@@ -2074,15 +1993,15 @@
if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
&& syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
- while (injectedEntry->pendingSyncDispatches != 0) {
+ while (injectedEntry->pendingForegroundDispatches != 0) {
#if DEBUG_INJECTION
- LOGD("injectInputEvent - Waiting for %d pending synchronous dispatches.",
- injectedEntry->pendingSyncDispatches);
+ LOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
+ injectedEntry->pendingForegroundDispatches);
#endif
nsecs_t remainingTimeout = endTime - now();
if (remainingTimeout <= 0) {
#if DEBUG_INJECTION
- LOGD("injectInputEvent - Timed out waiting for pending synchronous "
+ LOGD("injectInputEvent - Timed out waiting for pending foreground "
"dispatches to finish.");
#endif
injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
@@ -2137,10 +2056,10 @@
}
}
-void InputDispatcher::decrementPendingSyncDispatchesLocked(EventEntry* entry) {
- entry->pendingSyncDispatches -= 1;
+void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* entry) {
+ entry->pendingForegroundDispatches -= 1;
- if (entry->isInjected() && entry->pendingSyncDispatches == 0) {
+ if (entry->isInjected() && entry->pendingForegroundDispatches == 0) {
mInjectionSyncFinishedCondition.broadcast();
}
}
@@ -2238,6 +2157,10 @@
{ // acquire lock
AutoMutex _l(mLock);
+ sp<InputChannel> oldFocusedWindowChannel = mFocusedWindow
+ ? mFocusedWindow->inputChannel : NULL;
+ int32_t oldFocusedWindowLayer = mFocusedWindow ? mFocusedWindow->layer : -1;
+
sp<InputChannel> touchedWindowChannel;
if (mTouchedWindow) {
touchedWindowChannel = mTouchedWindow->inputChannel;
@@ -2251,8 +2174,6 @@
mTouchedWallpaperWindows.clear();
}
- bool hadFocusedWindow = mFocusedWindow != NULL;
-
mFocusedWindow = NULL;
mWallpaperWindows.clear();
@@ -2283,9 +2204,36 @@
mTempTouchedWallpaperChannels.clear();
- if ((hadFocusedWindow && ! mFocusedWindow)
- || (mFocusedWindow && ! mFocusedWindow->visible)) {
- preemptInputDispatchInnerLocked();
+ bool preempt = false;
+ if (mFocusedWindow
+ && mFocusedWindow->inputChannel != oldFocusedWindowChannel
+ && mFocusedWindow->canReceiveKeys) {
+ // If the new input focus is an error window or appears above the current
+ // input focus, drop the current touched window so that we can start
+ // delivering events to the new input focus as soon as possible.
+ if (mFocusedWindow->layoutParamsFlags & InputWindow::FLAG_SYSTEM_ERROR) {
+#if DEBUG_FOCUS
+ LOGD("Preempting: New SYSTEM_ERROR window; resetting state");
+#endif
+ preempt = true;
+ } else if (oldFocusedWindowChannel.get() != NULL
+ && mFocusedWindow->layer > oldFocusedWindowLayer) {
+#if DEBUG_FOCUS
+ LOGD("Preempting: Transferring focus to new window at higher layer: "
+ "old win layer=%d, new win layer=%d",
+ oldFocusedWindowLayer, mFocusedWindow->layer);
+#endif
+ preempt = true;
+ }
+ }
+ if (mTouchedWindow && ! mTouchedWindow->visible) {
+#if DEBUG_FOCUS
+ LOGD("Preempting: Touched window became invisible.");
+#endif
+ preempt = true;
+ }
+ if (preempt) {
+ releaseTouchedWindowLocked();
}
#if DEBUG_FOCUS
@@ -2359,39 +2307,6 @@
}
}
-void InputDispatcher::preemptInputDispatch() {
-#if DEBUG_FOCUS
- LOGD("preemptInputDispatch");
-#endif
-
- bool preemptedOne;
- { // acquire lock
- AutoMutex _l(mLock);
- preemptedOne = preemptInputDispatchInnerLocked();
- } // release lock
-
- if (preemptedOne) {
- // Wake up the poll loop so it can get a head start dispatching the next event.
- mLooper->wake();
- }
-}
-
-bool InputDispatcher::preemptInputDispatchInnerLocked() {
- bool preemptedOne = false;
- for (size_t i = 0; i < mActiveConnections.size(); i++) {
- Connection* connection = mActiveConnections[i];
- if (connection->hasPendingSyncTarget()) {
-#if DEBUG_DISPATCH_CYCLE
- LOGD("channel '%s' ~ Preempted pending synchronous dispatch",
- connection->getInputChannelName());
-#endif
- connection->preemptSyncTarget();
- preemptedOne = true;
- }
- }
- return preemptedOne;
-}
-
void InputDispatcher::logDispatchStateLocked() {
String8 dump;
dumpDispatchStateLocked(dump);
@@ -2447,12 +2362,14 @@
i, channel->getName().string());
}
+ dump.appendFormat(" inboundQueue: length=%u", mInboundQueue.count());
+
for (size_t i = 0; i < mActiveConnections.size(); i++) {
const Connection* connection = mActiveConnections[i];
- dump.appendFormat(" activeConnection[%d]: '%s', status=%s, hasPendingSyncTarget=%s, "
+ dump.appendFormat(" activeConnection[%d]: '%s', status=%s, outboundQueueLength=%u"
"inputState.isNeutral=%s, inputState.isOutOfSync=%s\n",
i, connection->getInputChannelName(), connection->getStatusLabel(),
- toString(connection->hasPendingSyncTarget()),
+ connection->outboundQueue.count(),
toString(connection->inputState.isNeutral()),
toString(connection->inputState.isOutOfSync()));
}
@@ -2474,7 +2391,7 @@
{ // acquire lock
AutoMutex _l(mLock);
- if (getConnectionIndex(inputChannel) >= 0) {
+ if (getConnectionIndexLocked(inputChannel) >= 0) {
LOGW("Attempted to register already registered input channel '%s'",
inputChannel->getName().string());
return BAD_VALUE;
@@ -2510,7 +2427,7 @@
{ // acquire lock
AutoMutex _l(mLock);
- ssize_t connectionIndex = getConnectionIndex(inputChannel);
+ ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
if (connectionIndex < 0) {
LOGW("Attempted to unregister already unregistered input channel '%s'",
inputChannel->getName().string());
@@ -2543,7 +2460,7 @@
return OK;
}
-ssize_t InputDispatcher::getConnectionIndex(const sp<InputChannel>& inputChannel) {
+ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
@@ -2578,31 +2495,7 @@
}
void InputDispatcher::onDispatchCycleFinishedLocked(
- nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR) {
- if (recoveredFromANR) {
- LOGI("channel '%s' ~ Recovered from ANR. %01.1fms since event, "
- "%01.1fms since dispatch, %01.1fms since ANR",
- connection->getInputChannelName(),
- connection->getEventLatencyMillis(currentTime),
- connection->getDispatchLatencyMillis(currentTime),
- connection->getANRLatencyMillis(currentTime));
-
- CommandEntry* commandEntry = postCommandLocked(
- & InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible);
- commandEntry->connection = connection;
- }
-}
-
-void InputDispatcher::onDispatchCycleANRLocked(
nsecs_t currentTime, const sp<Connection>& connection) {
- LOGI("channel '%s' ~ Not responding! %01.1fms since event, %01.1fms since dispatch",
- connection->getInputChannelName(),
- connection->getEventLatencyMillis(currentTime),
- connection->getDispatchLatencyMillis(currentTime));
-
- CommandEntry* commandEntry = postCommandLocked(
- & InputDispatcher::doNotifyInputChannelANRLockedInterruptible);
- commandEntry->connection = connection;
}
void InputDispatcher::onDispatchCycleBrokenLocked(
@@ -2615,6 +2508,25 @@
commandEntry->connection = connection;
}
+void InputDispatcher::onANRLocked(
+ nsecs_t currentTime, const InputApplication* application, const InputWindow* window,
+ nsecs_t eventTime, nsecs_t waitStartTime) {
+ LOGI("Application is not responding: %s. "
+ "%01.1fms since event, %01.1fms since wait started",
+ getApplicationWindowLabelLocked(application, window).string(),
+ (currentTime - eventTime) / 1000000.0,
+ (currentTime - waitStartTime) / 1000000.0);
+
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doNotifyANRLockedInterruptible);
+ if (application) {
+ commandEntry->inputApplicationHandle = application->handle;
+ }
+ if (window) {
+ commandEntry->inputChannel = window->inputChannel;
+ }
+}
+
void InputDispatcher::doNotifyConfigurationChangedInterruptible(
CommandEntry* commandEntry) {
mLock.unlock();
@@ -2637,33 +2549,16 @@
}
}
-void InputDispatcher::doNotifyInputChannelANRLockedInterruptible(
+void InputDispatcher::doNotifyANRLockedInterruptible(
CommandEntry* commandEntry) {
- sp<Connection> connection = commandEntry->connection;
+ mLock.unlock();
- if (connection->status != Connection::STATUS_ZOMBIE) {
- mLock.unlock();
+ nsecs_t newTimeout = mPolicy->notifyANR(
+ commandEntry->inputApplicationHandle, commandEntry->inputChannel);
- nsecs_t newTimeout = mPolicy->notifyInputChannelANR(connection->inputChannel);
+ mLock.lock();
- mLock.lock();
-
- nsecs_t currentTime = now();
- resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout);
- }
-}
-
-void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible(
- CommandEntry* commandEntry) {
- sp<Connection> connection = commandEntry->connection;
-
- if (connection->status != Connection::STATUS_ZOMBIE) {
- mLock.unlock();
-
- mPolicy->notifyInputChannelRecoveredFromANR(connection->inputChannel);
-
- mLock.lock();
- }
+ resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel);
}
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
@@ -2695,22 +2590,9 @@
mLock.lock();
}
-void InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible(
- CommandEntry* commandEntry) {
- mLock.unlock();
-
- nsecs_t newTimeout;
- if (commandEntry->inputChannel.get()) {
- newTimeout = mPolicy->notifyInputChannelANR(commandEntry->inputChannel);
- } else if (commandEntry->inputApplicationHandle.get()) {
- newTimeout = mPolicy->notifyANR(commandEntry->inputApplicationHandle);
- } else {
- newTimeout = 0;
- }
-
- mLock.lock();
-
- resumeAfterTargetsNotReadyTimeoutLocked(newTimeout);
+void InputDispatcher::updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
+ int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) {
+ // TODO Write some statistics about how long we spend waiting.
}
void InputDispatcher::dump(String8& dump) {
@@ -2718,6 +2600,18 @@
}
+// --- InputDispatcher::Queue ---
+
+template <typename T>
+uint32_t InputDispatcher::Queue<T>::count() const {
+ uint32_t result = 0;
+ for (const T* entry = headSentinel.next; entry != & tailSentinel; entry = entry->next) {
+ result += 1;
+ }
+ return result;
+}
+
+
// --- InputDispatcher::Allocator ---
InputDispatcher::Allocator::Allocator() {
@@ -2733,7 +2627,7 @@
entry->injectionIsAsync = false;
entry->injectorPid = -1;
entry->injectorUid = -1;
- entry->pendingSyncDispatches = 0;
+ entry->pendingForegroundDispatches = 0;
}
InputDispatcher::ConfigurationChangedEntry*
@@ -2797,14 +2691,13 @@
InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry(
EventEntry* eventEntry,
- int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout) {
+ int32_t targetFlags, float xOffset, float yOffset) {
DispatchEntry* entry = mDispatchEntryPool.alloc();
entry->eventEntry = eventEntry;
eventEntry->refCount += 1;
entry->targetFlags = targetFlags;
entry->xOffset = xOffset;
entry->yOffset = yOffset;
- entry->timeout = timeout;
entry->inProgress = false;
entry->headMotionSample = NULL;
entry->tailMotionSample = NULL;
@@ -2896,7 +2789,7 @@
void InputDispatcher::EventEntry::recycle() {
injectionResult = INPUT_EVENT_INJECTION_PENDING;
dispatchInProgress = false;
- pendingSyncDispatches = 0;
+ pendingForegroundDispatches = 0;
}
@@ -3106,9 +2999,7 @@
InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
status(STATUS_NORMAL), inputChannel(inputChannel), inputPublisher(inputChannel),
- nextTimeoutTime(LONG_LONG_MAX),
- lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX),
- lastANRTime(LONG_LONG_MAX) {
+ lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX) {
}
InputDispatcher::Connection::~Connection() {
@@ -3118,18 +3009,6 @@
return inputPublisher.initialize();
}
-void InputDispatcher::Connection::setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout) {
- nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX;
-}
-
-void InputDispatcher::Connection::resetTimeout(nsecs_t currentTime) {
- if (outboundQueue.isEmpty()) {
- nextTimeoutTime = LONG_LONG_MAX;
- } else {
- setNextTimeoutTime(currentTime, outboundQueue.headSentinel.next->timeout);
- }
-}
-
const char* InputDispatcher::Connection::getStatusLabel() const {
switch (status) {
case STATUS_NORMAL:
@@ -3138,9 +3017,6 @@
case STATUS_BROKEN:
return "BROKEN";
- case STATUS_NOT_RESPONDING:
- return "NOT_RESPONDING";
-
case STATUS_ZOMBIE:
return "ZOMBIE";