summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2010-09-15 21:42:28 -0700
committerAndroid Git Automerger <android-git-automerger@android.com>2010-09-15 21:42:28 -0700
commitfa8186c71662ae9c869adbf379ccc2d917638881 (patch)
tree09ec6f08e79949096595440475f61ca2c6078dd8
parentfcec46b3d4fa08a36e904d7474a73032aa583ccd (diff)
parent7642c82c291051919aad87c797e2dc1d77ed55e1 (diff)
downloadframeworks_base-fa8186c71662ae9c869adbf379ccc2d917638881.zip
frameworks_base-fa8186c71662ae9c869adbf379ccc2d917638881.tar.gz
frameworks_base-fa8186c71662ae9c869adbf379ccc2d917638881.tar.bz2
am 7642c82c: am f4d788c9: Merge "Make input dispatcher only ANR for foreground windows." into gingerbread
Merge commit '7642c82c291051919aad87c797e2dc1d77ed55e1' * commit '7642c82c291051919aad87c797e2dc1d77ed55e1': Make input dispatcher only ANR for foreground windows.
-rw-r--r--include/ui/InputDispatcher.h121
-rw-r--r--libs/ui/InputDispatcher.cpp662
-rw-r--r--services/java/com/android/server/InputManager.java24
-rw-r--r--services/java/com/android/server/InputWindow.java9
-rw-r--r--services/java/com/android/server/WindowManagerService.java88
-rw-r--r--services/jni/com_android_server_InputManager.cpp139
6 files changed, 385 insertions, 658 deletions
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 @@ enum {
*/
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 @@ struct InputTarget {
// 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 @@ struct InputWindow {
};
sp<InputChannel> inputChannel;
+ String8 name;
int32_t layoutParamsFlags;
int32_t layoutParamsType;
nsecs_t dispatchingTimeout;
@@ -206,9 +200,11 @@ struct InputWindow {
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 @@ public:
/* 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 @@ public:
*/
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 @@ public:
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 @@ private:
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 @@ private:
int32_t targetFlags;
float xOffset;
float yOffset;
- nsecs_t timeout;
// True if dispatch has started.
bool inProgress;
@@ -540,12 +518,8 @@ private:
// 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 @@ private:
dequeue(first);
return first;
}
+
+ uint32_t count() const;
};
/* Allocates queue entries and performs reference counting as needed. */
@@ -647,7 +623,7 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
// 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 @@ private:
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 @@ private:
// 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 @@ private:
// 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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
*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 @@ bool InputDispatcher::dispatchKeyLocked(
}
entry->dispatchInProgress = true;
- startFindingTargetsLocked();
+ startFindingTargetsLocked(); // resets mCurrentInputTargetsValid
}
// Identify targets.
@@ -618,7 +582,7 @@ bool InputDispatcher::dispatchMotionLocked(
logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
entry->dispatchInProgress = true;
- startFindingTargetsLocked();
+ startFindingTargetsLocked(); // resets mCurrentInputTargetsValid
}
bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
@@ -728,7 +692,7 @@ void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTi
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 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
} 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 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
}
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 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
}
}
-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 @@ void InputDispatcher::resetANRTimeoutsLocked() {
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 @@ int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const Even
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 @@ int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const Even
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 @@ int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const Moti
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 @@ int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const Moti
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 @@ int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const Moti
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 @@ int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const Moti
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 @@ int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const Moti
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 @@ Failed:
}
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 @@ void InputDispatcher::releaseTouchedWindowLocked() {
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 @@ void InputDispatcher::addMonitoringTargetsLocked() {
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 @@ bool InputDispatcher::isWindowObscuredLocked(const InputWindow* window) {
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 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
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 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
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 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
// 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 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
// 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 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
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 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
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 @@ void InputDispatcher::startNextDispatchCycleLocked(nsecs_t currentTime,
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 @@ void InputDispatcher::startNextDispatchCycleLocked(nsecs_t currentTime,
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 @@ void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime,
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::abortDispatchCycleLocked(nsecs_t currentTime,
}
}
-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 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t
// 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 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
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::setInjectionResultLocked(EventEntry* entry, int32_t inject
}
}
-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 @@ void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
{ // 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 @@ void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
mTouchedWallpaperWindows.clear();
}
- bool hadFocusedWindow = mFocusedWindow != NULL;
-
mFocusedWindow = NULL;
mWallpaperWindows.clear();
@@ -2283,9 +2204,36 @@ void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
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::setInputDispatchMode(bool enabled, bool frozen) {
}
}
-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 @@ void InputDispatcher::dumpDispatchStateLocked(String8& dump) {
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 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan
{ // 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 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh
{ // 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 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh
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::onDispatchCycleStartedLocked(
}
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 @@ void InputDispatcher::onDispatchCycleBrokenLocked(
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::doNotifyInputChannelBrokenLockedInterruptible(
}
}
-void InputDispatcher::doNotifyInputChannelANRLockedInterruptible(
+void InputDispatcher::doNotifyANRLockedInterruptible(
CommandEntry* commandEntry) {
- sp<Connection> connection = commandEntry->connection;
-
- if (connection->status != Connection::STATUS_ZOMBIE) {
- mLock.unlock();
-
- nsecs_t newTimeout = mPolicy->notifyInputChannelANR(connection->inputChannel);
-
- mLock.lock();
-
- nsecs_t currentTime = now();
- resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout);
- }
-}
+ mLock.unlock();
-void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible(
- CommandEntry* commandEntry) {
- sp<Connection> connection = commandEntry->connection;
+ nsecs_t newTimeout = mPolicy->notifyANR(
+ commandEntry->inputApplicationHandle, commandEntry->inputChannel);
- if (connection->status != Connection::STATUS_ZOMBIE) {
- mLock.unlock();
-
- mPolicy->notifyInputChannelRecoveredFromANR(connection->inputChannel);
+ mLock.lock();
- mLock.lock();
- }
+ resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel);
}
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
@@ -2695,22 +2590,9 @@ void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* comman
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 @@ void InputDispatcher::dump(String8& dump) {
}
+// --- 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 @@ void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t
entry->injectionIsAsync = false;
entry->injectorPid = -1;
entry->injectorUid = -1;
- entry->pendingSyncDispatches = 0;
+ entry->pendingForegroundDispatches = 0;
}
InputDispatcher::ConfigurationChangedEntry*
@@ -2797,14 +2691,13 @@ InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsec
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::Allocator::appendMotionSample(MotionEntry* motionEntry,
void InputDispatcher::EventEntry::recycle() {
injectionResult = INPUT_EVENT_INJECTION_PENDING;
dispatchInProgress = false;
- pendingSyncDispatches = 0;
+ pendingForegroundDispatches = 0;
}
@@ -3106,9 +2999,7 @@ void InputDispatcher::InputState::clear() {
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 @@ status_t InputDispatcher::Connection::initialize() {
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 @@ const char* InputDispatcher::Connection::getStatusLabel() const {
case STATUS_BROKEN:
return "BROKEN";
- case STATUS_NOT_RESPONDING:
- return "NOT_RESPONDING";
-
case STATUS_ZOMBIE:
return "ZOMBIE";
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 90d036a..024aec5 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -24,18 +24,13 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Environment;
-import android.os.LocalPowerManager;
-import android.os.PowerManager;
import android.os.SystemProperties;
import android.util.Slog;
import android.util.Xml;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
import android.view.Surface;
-import android.view.WindowManagerPolicy;
import java.io.BufferedReader;
import java.io.File;
@@ -83,7 +78,6 @@ public class InputManager {
private static native void nativeSetInputWindows(InputWindow[] windows);
private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
private static native void nativeSetFocusedApplication(InputApplication application);
- private static native void nativePreemptInputDispatch();
private static native InputDevice nativeGetInputDevice(int deviceId);
private static native int[] nativeGetInputDeviceIds();
private static native String nativeDump();
@@ -331,10 +325,6 @@ public class InputManager {
nativeSetFocusedApplication(application);
}
- public void preemptInputDispatch() {
- nativePreemptInputDispatch();
- }
-
public void setInputDispatchMode(boolean enabled, boolean frozen) {
nativeSetInputDispatchMode(enabled, frozen);
}
@@ -395,20 +385,10 @@ public class InputManager {
public void notifyInputChannelBroken(InputChannel inputChannel) {
mWindowManagerService.mInputMonitor.notifyInputChannelBroken(inputChannel);
}
-
- @SuppressWarnings("unused")
- public long notifyInputChannelANR(InputChannel inputChannel) {
- return mWindowManagerService.mInputMonitor.notifyInputChannelANR(inputChannel);
- }
-
- @SuppressWarnings("unused")
- public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
- mWindowManagerService.mInputMonitor.notifyInputChannelRecoveredFromANR(inputChannel);
- }
@SuppressWarnings("unused")
- public long notifyANR(Object token) {
- return mWindowManagerService.mInputMonitor.notifyANR(token);
+ public long notifyANR(Object token, InputChannel inputChannel) {
+ return mWindowManagerService.mInputMonitor.notifyANR(token, inputChannel);
}
@SuppressWarnings("unused")
diff --git a/services/java/com/android/server/InputWindow.java b/services/java/com/android/server/InputWindow.java
index dbc59ef..befc770 100644
--- a/services/java/com/android/server/InputWindow.java
+++ b/services/java/com/android/server/InputWindow.java
@@ -27,6 +27,9 @@ public final class InputWindow {
// The input channel associated with the window.
public InputChannel inputChannel;
+ // The window name.
+ public String name;
+
// Window layout params attributes. (WindowManager.LayoutParams)
public int layoutParamsFlags;
public int layoutParamsType;
@@ -55,6 +58,9 @@ public final class InputWindow {
// Window is visible.
public boolean visible;
+ // Window can receive keys.
+ public boolean canReceiveKeys;
+
// Window has focus.
public boolean hasFocus;
@@ -64,6 +70,9 @@ public final class InputWindow {
// Input event dispatching is paused.
public boolean paused;
+ // Window layer.
+ public int layer;
+
// Id of process and user that owns the window.
public int ownerPid;
public int ownerUid;
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index dacdd7d..fb8e888 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -5088,61 +5088,39 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- /* Notifies the window manager about an input channel that is not responding.
+ /* Notifies the window manager about an application that is not responding.
* Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
*
* Called by the InputManager.
*/
- public long notifyInputChannelANR(InputChannel inputChannel) {
- AppWindowToken token;
- synchronized (mWindowMap) {
- WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
- if (windowState == null) {
- return 0; // window is unknown, abort dispatching
+ public long notifyANR(Object token, InputChannel inputChannel) {
+ AppWindowToken appWindowToken = null;
+ if (inputChannel != null) {
+ synchronized (mWindowMap) {
+ WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+ if (windowState != null) {
+ Slog.i(TAG, "Input event dispatching timed out sending to "
+ + windowState.mAttrs.getTitle());
+ appWindowToken = windowState.mAppToken;
+ }
}
-
- Slog.i(TAG, "Input event dispatching timed out sending to "
- + windowState.mAttrs.getTitle());
- token = windowState.mAppToken;
}
- return notifyANRInternal(token);
- }
-
- /* Notifies the window manager about an input channel spontaneously recovering from ANR
- * by successfully delivering the event that originally timed out.
- *
- * Called by the InputManager.
- */
- public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
- // Nothing to do just now.
- // Just wait for the user to dismiss the ANR dialog.
- }
-
- /* Notifies the window manager about an application that is not responding
- * in general rather than with respect to a particular input channel.
- * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
- *
- * Called by the InputManager.
- */
- public long notifyANR(Object token) {
- AppWindowToken appWindowToken = (AppWindowToken) token;
+ if (appWindowToken == null && token != null) {
+ appWindowToken = (AppWindowToken) token;
+ Slog.i(TAG, "Input event dispatching timed out sending to application "
+ + appWindowToken.stringName);
+ }
- Slog.i(TAG, "Input event dispatching timed out sending to application "
- + appWindowToken.stringName);
- return notifyANRInternal(appWindowToken);
- }
-
- private long notifyANRInternal(AppWindowToken token) {
- if (token != null && token.appToken != null) {
+ if (appWindowToken != null && appWindowToken.appToken != null) {
try {
// Notify the activity manager about the timeout and let it decide whether
// to abort dispatching or keep waiting.
- boolean abort = token.appToken.keyDispatchingTimedOut();
+ boolean abort = appWindowToken.appToken.keyDispatchingTimedOut();
if (! abort) {
// The activity manager declined to abort dispatching.
// Wait a bit longer and timeout again later.
- return token.inputDispatchingTimeoutNanos;
+ return appWindowToken.inputDispatchingTimeoutNanos;
}
} catch (RemoteException ex) {
}
@@ -5195,13 +5173,16 @@ public class WindowManagerService extends IWindowManager.Stub
// Add a window to our list of input windows.
final InputWindow inputWindow = mTempInputWindows.add();
inputWindow.inputChannel = child.mInputChannel;
+ inputWindow.name = child.toString();
inputWindow.layoutParamsFlags = flags;
inputWindow.layoutParamsType = type;
inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
inputWindow.visible = isVisible;
+ inputWindow.canReceiveKeys = child.canReceiveKeys();
inputWindow.hasFocus = hasFocus;
inputWindow.hasWallpaper = hasWallpaper;
inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false;
+ inputWindow.layer = child.mLayer;
inputWindow.ownerPid = child.mSession.mPid;
inputWindow.ownerUid = child.mSession.mUid;
@@ -5294,23 +5275,6 @@ public class WindowManagerService extends IWindowManager.Stub
if (newWindow != mInputFocus) {
if (newWindow != null && newWindow.canReceiveKeys()) {
- // If the new input focus is an error window or appears above the current
- // input focus, preempt any pending synchronous dispatch so that we can
- // start delivering events to the new input focus as soon as possible.
- if ((newWindow.mAttrs.flags & FLAG_SYSTEM_ERROR) != 0) {
- if (DEBUG_INPUT) {
- Slog.v(TAG, "New SYSTEM_ERROR window; resetting state");
- }
- preemptInputDispatchLw();
- } else if (mInputFocus != null && newWindow.mLayer > mInputFocus.mLayer) {
- if (DEBUG_INPUT) {
- Slog.v(TAG, "Transferring focus to new window at higher layer: "
- + "old win layer=" + mInputFocus.mLayer
- + ", new win layer=" + newWindow.mLayer);
- }
- preemptInputDispatchLw();
- }
-
// Displaying a window implicitly causes dispatching to be unpaused.
// This is to protect against bugs if someone pauses dispatching but
// forgets to resume.
@@ -5322,14 +5286,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- /* Tells the dispatcher to stop waiting for its current synchronous event targets.
- * Essentially, just makes those dispatches asynchronous so a new dispatch cycle
- * can begin.
- */
- private void preemptInputDispatchLw() {
- mInputManager.preemptInputDispatch();
- }
-
public void setFocusedAppLw(AppWindowToken newApp) {
// Focused app has changed.
if (newApp == null) {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 6dd619c..cbdfba9 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -49,8 +49,6 @@ static struct {
jmethodID notifyConfigurationChanged;
jmethodID notifyLidSwitchChanged;
jmethodID notifyInputChannelBroken;
- jmethodID notifyInputChannelANR;
- jmethodID notifyInputChannelRecoveredFromANR;
jmethodID notifyANR;
jmethodID virtualKeyDownFeedback;
jmethodID interceptKeyBeforeQueueing;
@@ -85,6 +83,7 @@ static struct {
jclass clazz;
jfieldID inputChannel;
+ jfieldID name;
jfieldID layoutParamsFlags;
jfieldID layoutParamsType;
jfieldID dispatchingTimeoutNanos;
@@ -101,9 +100,11 @@ static struct {
jfieldID touchableAreaRight;
jfieldID touchableAreaBottom;
jfieldID visible;
+ jfieldID canReceiveKeys;
jfieldID hasFocus;
jfieldID hasWallpaper;
jfieldID paused;
+ jfieldID layer;
jfieldID ownerPid;
jfieldID ownerUid;
} gInputWindowClassInfo;
@@ -168,7 +169,6 @@ public:
void setInputWindows(JNIEnv* env, jobjectArray windowObjArray);
void setFocusedApplication(JNIEnv* env, jobject applicationObj);
void setInputDispatchMode(bool enabled, bool frozen);
- void preemptInputDispatch();
/* --- InputReaderPolicyInterface implementation --- */
@@ -191,10 +191,9 @@ public:
/* --- InputDispatcherPolicyInterface implementation --- */
virtual void notifyConfigurationChanged(nsecs_t when);
- virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle);
+ virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<InputChannel>& inputChannel);
virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel);
- virtual nsecs_t notifyInputChannelANR(const sp<InputChannel>& inputChannel);
- virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel);
virtual nsecs_t getKeyRepeatTimeout();
virtual nsecs_t getKeyRepeatDelay();
virtual int32_t getMaxEventsPerSecond();
@@ -702,32 +701,40 @@ void NativeInputManager::notifyConfigurationChanged(nsecs_t when) {
checkAndClearExceptionFromCallback(env, "notifyConfigurationChanged");
}
-nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle) {
+nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<InputChannel>& inputChannel) {
#if DEBUG_INPUT_DISPATCHER_POLICY
LOGD("notifyANR");
#endif
JNIEnv* env = jniEnv();
- ApplicationToken* token = static_cast<ApplicationToken*>(inputApplicationHandle.get());
- jweak tokenObjWeak = token->getTokenObj();
-
- jlong newTimeout;
- jobject tokenObjLocal = env->NewLocalRef(tokenObjWeak);
- if (tokenObjLocal) {
- newTimeout = env->CallLongMethod(mCallbacksObj,
- gCallbacksClassInfo.notifyANR, tokenObjLocal);
- if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
- newTimeout = 0; // abort dispatch
- } else {
- assert(newTimeout >= 0);
- }
+ jobject tokenObjLocal;
+ if (inputApplicationHandle.get()) {
+ ApplicationToken* token = static_cast<ApplicationToken*>(inputApplicationHandle.get());
+ jweak tokenObjWeak = token->getTokenObj();
+ tokenObjLocal = env->NewLocalRef(tokenObjWeak);
+ } else {
+ tokenObjLocal = NULL;
+ }
- env->DeleteLocalRef(tokenObjLocal);
+ jobject inputChannelObjLocal;
+ if (inputChannel.get()) {
+ inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
} else {
+ inputChannelObjLocal = NULL;
+ }
+
+ jlong newTimeout = env->CallLongMethod(mCallbacksObj,
+ gCallbacksClassInfo.notifyANR, tokenObjLocal, inputChannelObjLocal);
+ if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
newTimeout = 0; // abort dispatch
+ } else {
+ assert(newTimeout >= 0);
}
+ env->DeleteLocalRef(tokenObjLocal);
+ env->DeleteLocalRef(inputChannelObjLocal);
return newTimeout;
}
@@ -748,51 +755,6 @@ void NativeInputManager::notifyInputChannelBroken(const sp<InputChannel>& inputC
}
}
-nsecs_t NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("notifyInputChannelANR - inputChannel='%s'",
- inputChannel->getName().string());
-#endif
-
- JNIEnv* env = jniEnv();
-
- jlong newTimeout;
- jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
- if (inputChannelObjLocal) {
- newTimeout = env->CallLongMethod(mCallbacksObj,
- gCallbacksClassInfo.notifyInputChannelANR, inputChannelObjLocal);
- if (checkAndClearExceptionFromCallback(env, "notifyInputChannelANR")) {
- newTimeout = 0; // abort dispatch
- } else {
- assert(newTimeout >= 0);
- }
-
- env->DeleteLocalRef(inputChannelObjLocal);
- } else {
- newTimeout = 0; // abort dispatch
- }
-
- return newTimeout;
-}
-
-void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("notifyInputChannelRecoveredFromANR - inputChannel='%s'",
- inputChannel->getName().string());
-#endif
-
- JNIEnv* env = jniEnv();
-
- jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
- if (inputChannelObjLocal) {
- env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelRecoveredFromANR,
- inputChannelObjLocal);
- checkAndClearExceptionFromCallback(env, "notifyInputChannelRecoveredFromANR");
-
- env->DeleteLocalRef(inputChannelObjLocal);
- }
-}
-
nsecs_t NativeInputManager::getKeyRepeatTimeout() {
if (! isScreenOn()) {
// Disable key repeat when the screen is off.
@@ -855,6 +817,8 @@ bool NativeInputManager::populateWindow(JNIEnv* env, jobject windowObj,
sp<InputChannel> inputChannel =
android_view_InputChannel_getInputChannel(env, inputChannelObj);
if (inputChannel != NULL) {
+ jstring name = jstring(env->GetObjectField(windowObj,
+ gInputWindowClassInfo.name));
jint layoutParamsFlags = env->GetIntField(windowObj,
gInputWindowClassInfo.layoutParamsFlags);
jint layoutParamsType = env->GetIntField(windowObj,
@@ -887,18 +851,25 @@ bool NativeInputManager::populateWindow(JNIEnv* env, jobject windowObj,
gInputWindowClassInfo.touchableAreaBottom);
jboolean visible = env->GetBooleanField(windowObj,
gInputWindowClassInfo.visible);
+ jboolean canReceiveKeys = env->GetBooleanField(windowObj,
+ gInputWindowClassInfo.canReceiveKeys);
jboolean hasFocus = env->GetBooleanField(windowObj,
gInputWindowClassInfo.hasFocus);
jboolean hasWallpaper = env->GetBooleanField(windowObj,
gInputWindowClassInfo.hasWallpaper);
jboolean paused = env->GetBooleanField(windowObj,
gInputWindowClassInfo.paused);
+ jint layer = env->GetIntField(windowObj,
+ gInputWindowClassInfo.layer);
jint ownerPid = env->GetIntField(windowObj,
gInputWindowClassInfo.ownerPid);
jint ownerUid = env->GetIntField(windowObj,
gInputWindowClassInfo.ownerUid);
+ const char* nameStr = env->GetStringUTFChars(name, NULL);
+
outWindow.inputChannel = inputChannel;
+ outWindow.name.setTo(nameStr);
outWindow.layoutParamsFlags = layoutParamsFlags;
outWindow.layoutParamsType = layoutParamsType;
outWindow.dispatchingTimeout = dispatchingTimeoutNanos;
@@ -915,11 +886,15 @@ bool NativeInputManager::populateWindow(JNIEnv* env, jobject windowObj,
outWindow.touchableAreaRight = touchableAreaRight;
outWindow.touchableAreaBottom = touchableAreaBottom;
outWindow.visible = visible;
+ outWindow.canReceiveKeys = canReceiveKeys;
outWindow.hasFocus = hasFocus;
outWindow.hasWallpaper = hasWallpaper;
outWindow.paused = paused;
+ outWindow.layer = layer;
outWindow.ownerPid = ownerPid;
outWindow.ownerUid = ownerUid;
+
+ env->ReleaseStringUTFChars(name, nameStr);
valid = true;
} else {
LOGW("Dropping input target because its input channel is not initialized.");
@@ -973,10 +948,6 @@ void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) {
mInputManager->getDispatcher()->setInputDispatchMode(enabled, frozen);
}
-void NativeInputManager::preemptInputDispatch() {
- mInputManager->getDispatcher()->preemptInputDispatch();
-}
-
bool NativeInputManager::interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
const KeyEvent* keyEvent, uint32_t policyFlags) {
bool isScreenOn = this->isScreenOn();
@@ -1246,15 +1217,6 @@ static void android_server_InputManager_nativeSetInputDispatchMode(JNIEnv* env,
gNativeInputManager->setInputDispatchMode(enabled, frozen);
}
-static void android_server_InputManager_nativePreemptInputDispatch(JNIEnv* env,
- jclass clazz) {
- if (checkInputManagerUnitialized(env)) {
- return;
- }
-
- gNativeInputManager->preemptInputDispatch();
-}
-
static jobject android_server_InputManager_nativeGetInputDevice(JNIEnv* env,
jclass clazz, jint deviceId) {
if (checkInputManagerUnitialized(env)) {
@@ -1357,8 +1319,6 @@ static JNINativeMethod gInputManagerMethods[] = {
(void*) android_server_InputManager_nativeSetFocusedApplication },
{ "nativeSetInputDispatchMode", "(ZZ)V",
(void*) android_server_InputManager_nativeSetInputDispatchMode },
- { "nativePreemptInputDispatch", "()V",
- (void*) android_server_InputManager_nativePreemptInputDispatch },
{ "nativeGetInputDevice", "(I)Landroid/view/InputDevice;",
(void*) android_server_InputManager_nativeGetInputDevice },
{ "nativeGetInputDeviceIds", "()[I",
@@ -1398,14 +1358,8 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, gCallbacksClassInfo.clazz,
"notifyInputChannelBroken", "(Landroid/view/InputChannel;)V");
- GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelANR, gCallbacksClassInfo.clazz,
- "notifyInputChannelANR", "(Landroid/view/InputChannel;)J");
-
- GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelRecoveredFromANR, gCallbacksClassInfo.clazz,
- "notifyInputChannelRecoveredFromANR", "(Landroid/view/InputChannel;)V");
-
GET_METHOD_ID(gCallbacksClassInfo.notifyANR, gCallbacksClassInfo.clazz,
- "notifyANR", "(Ljava/lang/Object;)J");
+ "notifyANR", "(Ljava/lang/Object;Landroid/view/InputChannel;)J");
GET_METHOD_ID(gCallbacksClassInfo.virtualKeyDownFeedback, gCallbacksClassInfo.clazz,
"virtualKeyDownFeedback", "()V");
@@ -1477,6 +1431,9 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_FIELD_ID(gInputWindowClassInfo.inputChannel, gInputWindowClassInfo.clazz,
"inputChannel", "Landroid/view/InputChannel;");
+ GET_FIELD_ID(gInputWindowClassInfo.name, gInputWindowClassInfo.clazz,
+ "name", "Ljava/lang/String;");
+
GET_FIELD_ID(gInputWindowClassInfo.layoutParamsFlags, gInputWindowClassInfo.clazz,
"layoutParamsFlags", "I");
@@ -1525,6 +1482,9 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_FIELD_ID(gInputWindowClassInfo.visible, gInputWindowClassInfo.clazz,
"visible", "Z");
+ GET_FIELD_ID(gInputWindowClassInfo.canReceiveKeys, gInputWindowClassInfo.clazz,
+ "canReceiveKeys", "Z");
+
GET_FIELD_ID(gInputWindowClassInfo.hasFocus, gInputWindowClassInfo.clazz,
"hasFocus", "Z");
@@ -1534,6 +1494,9 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_FIELD_ID(gInputWindowClassInfo.paused, gInputWindowClassInfo.clazz,
"paused", "Z");
+ GET_FIELD_ID(gInputWindowClassInfo.layer, gInputWindowClassInfo.clazz,
+ "layer", "I");
+
GET_FIELD_ID(gInputWindowClassInfo.ownerPid, gInputWindowClassInfo.clazz,
"ownerPid", "I");