Merge "SurfaceFlinger: add explicit eEarlyWakeup start and end" into rvc-dev
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 942c16c..581d3de 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -209,6 +209,10 @@
return fd;
}
+static int OpenForWrite(std::string path) {
+ return Open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+}
static int OpenForRead(std::string path) {
return Open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
@@ -274,6 +278,27 @@
return version_code;
}
+static bool PathExists(const std::string& path) {
+ struct stat sb;
+ return stat(path.c_str(), &sb) == 0;
+}
+
+static bool CopyFileToFile(const std::string& input_file, const std::string& output_file) {
+ if (input_file == output_file) {
+ MYLOGD("Skipping copying bugreport file since the destination is the same (%s)\n",
+ output_file.c_str());
+ return false;
+ }
+ else if (PathExists(output_file)) {
+ MYLOGD("Cannot overwrite an existing file (%s)\n", output_file.c_str());
+ return false;
+ }
+
+ MYLOGD("Going to copy bugreport file (%s) to %s\n", input_file.c_str(), output_file.c_str());
+ android::base::unique_fd out_fd(OpenForWrite(output_file));
+ return CopyFileToFd(input_file, out_fd.get());
+}
+
} // namespace
} // namespace os
} // namespace android
@@ -2092,11 +2117,12 @@
static void ShowUsage() {
fprintf(stderr,
- "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-d] [-p] "
+ "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-d] [-p] "
"[-z] [-s] [-S] [-q] [-P] [-R] [-L] [-V version]\n"
" -h: display this help message\n"
" -b: play sound file instead of vibrate, at beginning of job\n"
" -e: play sound file instead of vibrate, at end of job\n"
+ " -o: write to custom directory (only in limited mode)\n"
" -d: append date to filename\n"
" -p: capture screenshot to filename.png\n"
" -z: generate zipped file\n"
@@ -2267,6 +2293,13 @@
do_text_file = false;
}
}
+
+ std::string final_path = ds.path_;
+ if (ds.options_->OutputToCustomFile()) {
+ final_path = ds.GetPath(ds.options_->out_dir, ".zip");
+ android::os::CopyFileToFile(ds.path_, final_path);
+ }
+
if (ds.options_->use_control_socket) {
if (do_text_file) {
dprintf(ds.control_socket_fd_,
@@ -2274,7 +2307,7 @@
"for more details\n",
ds.log_path_.c_str());
} else {
- dprintf(ds.control_socket_fd_, "OK:%s\n", ds.path_.c_str());
+ dprintf(ds.control_socket_fd_, "OK:%s\n", final_path.c_str());
}
}
}
@@ -2384,6 +2417,7 @@
// clang-format off
case 'd': do_add_date = true; break;
case 'z': do_zip_file = true; break;
+ case 'o': out_dir = optarg; break;
case 's': use_socket = true; break;
case 'S': use_control_socket = true; break;
case 'v': show_header_only = true; break;
@@ -2505,8 +2539,8 @@
* If zipping, a bunch of other files and dumps also get added to the zip archive. The log file also
* gets added to the archive.
*
- * Bugreports are first generated in a local directory and later copied to the caller's fd if
- * supplied.
+ * Bugreports are first generated in a local directory and later copied to the caller's fd
+ * or directory if supplied.
*/
Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
const std::string& calling_package) {
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index dc0848a..0d25d30 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -386,10 +386,12 @@
// The HAL is actually an API surface that can be validated, while the AIDL is not (@hide).
::android::hardware::dumpstate::V1_1::DumpstateMode dumpstate_hal_mode =
::android::hardware::dumpstate::V1_1::DumpstateMode::DEFAULT;
- // File descriptor to output zip file.
+ // File descriptor to output zip file. Takes precedence over out_dir.
android::base::unique_fd bugreport_fd;
// File descriptor to screenshot file.
android::base::unique_fd screenshot_fd;
+ // Custom output directory.
+ std::string out_dir;
// Bugreport mode of the bugreport.
std::string bugreport_mode;
// Command-line arguments as string
@@ -415,6 +417,12 @@
// specified, it is preferred. If not bugreport is written to /bugreports.
return !use_socket;
}
+
+ /* Returns if options specified require writing to custom file location */
+ bool OutputToCustomFile() {
+ // Custom location is only honored in limited mode.
+ return limited_only && !out_dir.empty() && bugreport_fd.get() == -1;
+ }
};
// TODO: initialize fields on constructor
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index e94e51c..c7df1bb 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -172,6 +172,7 @@
EXPECT_FALSE(options_.do_add_date);
EXPECT_FALSE(options_.do_zip_file);
+ EXPECT_EQ("", options_.out_dir);
EXPECT_FALSE(options_.use_socket);
EXPECT_FALSE(options_.use_control_socket);
EXPECT_FALSE(options_.show_header_only);
@@ -352,7 +353,8 @@
const_cast<char*>("-d"),
const_cast<char*>("-z"),
const_cast<char*>("-q"),
- const_cast<char*>("-L")
+ const_cast<char*>("-L"),
+ const_cast<char*>("-o abc")
};
// clang-format on
@@ -364,6 +366,7 @@
EXPECT_TRUE(options_.use_control_socket);
EXPECT_FALSE(options_.do_vibrate);
EXPECT_TRUE(options_.limited_only);
+ EXPECT_EQ(" abc", std::string(options_.out_dir));
// Other options retain default values
EXPECT_FALSE(options_.show_header_only);
diff --git a/include/input/Input.h b/include/input/Input.h
index 9e47318..54b4e5a 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -462,6 +462,8 @@
nsecs_t eventTime);
void initialize(const KeyEvent& from);
+ static const char* actionToString(int32_t action);
+
protected:
int32_t mAction;
int32_t mFlags;
@@ -725,6 +727,8 @@
static const char* getLabel(int32_t axis);
static int32_t getAxisFromLabel(const char* label);
+ static const char* actionToString(int32_t action);
+
protected:
int32_t mAction;
int32_t mActionButton;
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index c243767..31aa685 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -169,6 +169,18 @@
mEventTime = from.mEventTime;
}
+const char* KeyEvent::actionToString(int32_t action) {
+ // Convert KeyEvent action to string
+ switch (action) {
+ case AKEY_EVENT_ACTION_DOWN:
+ return "DOWN";
+ case AKEY_EVENT_ACTION_UP:
+ return "UP";
+ case AKEY_EVENT_ACTION_MULTIPLE:
+ return "MULTIPLE";
+ }
+ return "UNKNOWN";
+}
// --- PointerCoords ---
@@ -678,6 +690,25 @@
return getAxisByLabel(label);
}
+const char* MotionEvent::actionToString(int32_t action) {
+ // Convert MotionEvent action to string
+ switch (action & AMOTION_EVENT_ACTION_MASK) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ return "DOWN";
+ case AMOTION_EVENT_ACTION_MOVE:
+ return "MOVE";
+ case AMOTION_EVENT_ACTION_UP:
+ return "UP";
+ case AMOTION_EVENT_ACTION_CANCEL:
+ return "CANCEL";
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ return "POINTER_DOWN";
+ case AMOTION_EVENT_ACTION_POINTER_UP:
+ return "POINTER_UP";
+ }
+ return "UNKNOWN";
+}
+
// --- FocusEvent ---
void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) {
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 99283be..aa24e8e 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -96,7 +96,7 @@
"EGL_EXT_surface_CTA861_3_metadata "
;
-// Whitelist of extensions exposed to applications if implemented in the vendor driver.
+// Allowed list of extensions exposed to applications if implemented in the vendor driver.
char const * const gExtensionString =
"EGL_KHR_image " // mandatory
"EGL_KHR_image_base " // mandatory
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index a98f4b4..390c6b8 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -22,6 +22,7 @@
filegroup {
name: "libinputdispatcher_sources",
srcs: [
+ "AnrTracker.cpp",
"Connection.cpp",
"Entry.cpp",
"InjectionState.cpp",
diff --git a/services/inputflinger/dispatcher/AnrTracker.cpp b/services/inputflinger/dispatcher/AnrTracker.cpp
new file mode 100644
index 0000000..c3f611e
--- /dev/null
+++ b/services/inputflinger/dispatcher/AnrTracker.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AnrTracker.h"
+
+namespace android::inputdispatcher {
+
+template <typename T>
+static T max(const T& a, const T& b) {
+ return a < b ? b : a;
+}
+
+void AnrTracker::insert(nsecs_t timeoutTime, sp<IBinder> token) {
+ mAnrTimeouts.insert(std::make_pair(timeoutTime, std::move(token)));
+}
+
+/**
+ * Erase a single entry only. If there are multiple duplicate entries
+ * (same time, same connection), then only remove one of them.
+ */
+void AnrTracker::erase(nsecs_t timeoutTime, const sp<IBinder>& token) {
+ auto pair = std::make_pair(timeoutTime, token);
+ auto it = mAnrTimeouts.find(pair);
+ if (it != mAnrTimeouts.end()) {
+ mAnrTimeouts.erase(it);
+ }
+}
+
+void AnrTracker::eraseToken(const sp<IBinder>& token) {
+ for (auto it = mAnrTimeouts.begin(); it != mAnrTimeouts.end();) {
+ if (it->second == token) {
+ it = mAnrTimeouts.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+bool AnrTracker::empty() const {
+ return mAnrTimeouts.empty();
+}
+
+// If empty() is false, return the time at which the next connection should cause an ANR
+// If empty() is true, return LONG_LONG_MAX
+nsecs_t AnrTracker::firstTimeout() const {
+ if (mAnrTimeouts.empty()) {
+ return std::numeric_limits<nsecs_t>::max();
+ }
+ return mAnrTimeouts.begin()->first;
+}
+
+const sp<IBinder>& AnrTracker::firstToken() const {
+ return mAnrTimeouts.begin()->second;
+}
+
+void AnrTracker::clear() {
+ mAnrTimeouts.clear();
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/AnrTracker.h b/services/inputflinger/dispatcher/AnrTracker.h
new file mode 100644
index 0000000..097dba5
--- /dev/null
+++ b/services/inputflinger/dispatcher/AnrTracker.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
+#define _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
+
+#include <binder/IBinder.h>
+#include <utils/Timers.h>
+#include <set>
+
+namespace android::inputdispatcher {
+
+/**
+ * Keeps track of the times when each connection is going to ANR.
+ * Provides the ability to quickly find the connection that is going to cause ANR next.
+ */
+class AnrTracker {
+public:
+ void insert(nsecs_t timeoutTime, sp<IBinder> token);
+ void erase(nsecs_t timeoutTime, const sp<IBinder>& token);
+ void eraseToken(const sp<IBinder>& token);
+ void clear();
+
+ bool empty() const;
+ // If empty() is false, return the time at which the next connection should cause an ANR
+ // If empty() is true, return LONG_LONG_MAX
+ nsecs_t firstTimeout() const;
+ // Return the token of the next connection that should cause an ANR.
+ // Do not call this unless empty() is false, you will encounter undefined behaviour.
+ const sp<IBinder>& firstToken() const;
+
+private:
+ // Optimization: use a multiset to keep track of the event timeouts. When an event is sent
+ // to the InputConsumer, we add an entry to this structure. We look at the smallest value to
+ // determine if any of the connections is unresponsive, and to determine when we should wake
+ // next for the future ANR check.
+ // Using a multiset helps quickly look up the next timeout due.
+ //
+ // We must use a multi-set, because it is plausible (although highly unlikely) to have entries
+ // from the same connection and same timestamp, but different sequence numbers.
+ // We are not tracking sequence numbers, and just allow duplicates to exist.
+ std::multiset<std::pair<nsecs_t /*timeoutTime*/, sp<IBinder> /*connectionToken*/>> mAnrTimeouts;
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp
index 188212b..f5ea563 100644
--- a/services/inputflinger/dispatcher/Connection.cpp
+++ b/services/inputflinger/dispatcher/Connection.cpp
@@ -26,8 +26,7 @@
inputChannel(inputChannel),
monitor(monitor),
inputPublisher(inputChannel),
- inputState(idGenerator),
- inputPublisherBlocked(false) {}
+ inputState(idGenerator) {}
Connection::~Connection() {}
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
index bb3f2fe..3b33f29 100644
--- a/services/inputflinger/dispatcher/Connection.h
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -47,9 +47,10 @@
InputPublisher inputPublisher;
InputState inputState;
- // True if the socket is full and no further events can be published until
- // the application consumes some of the input.
- bool inputPublisherBlocked;
+ // True if this connection is responsive.
+ // If this connection is not responsive, avoid publishing more events to it until the
+ // application consumes some of the input.
+ bool responsive = true;
// Queue of events that need to be published to the connection.
std::deque<DispatchEntry*> outboundQueue;
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 21c8ae1..fdbb1d1 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -28,38 +28,6 @@
namespace android::inputdispatcher {
-static std::string motionActionToString(int32_t action) {
- // Convert MotionEvent action to string
- switch (action & AMOTION_EVENT_ACTION_MASK) {
- case AMOTION_EVENT_ACTION_DOWN:
- return "DOWN";
- case AMOTION_EVENT_ACTION_MOVE:
- return "MOVE";
- case AMOTION_EVENT_ACTION_UP:
- return "UP";
- case AMOTION_EVENT_ACTION_CANCEL:
- return "CANCEL";
- case AMOTION_EVENT_ACTION_POINTER_DOWN:
- return "POINTER_DOWN";
- case AMOTION_EVENT_ACTION_POINTER_UP:
- return "POINTER_UP";
- }
- return StringPrintf("%" PRId32, action);
-}
-
-static std::string keyActionToString(int32_t action) {
- // Convert KeyEvent action to string
- switch (action) {
- case AKEY_EVENT_ACTION_DOWN:
- return "DOWN";
- case AKEY_EVENT_ACTION_UP:
- return "UP";
- case AKEY_EVENT_ACTION_MULTIPLE:
- return "MULTIPLE";
- }
- return StringPrintf("%" PRId32, action);
-}
-
VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry) {
return {{VerifiedInputEvent::Type::KEY, entry.deviceId, entry.eventTime, entry.source,
entry.displayId},
@@ -191,7 +159,7 @@
msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, "
"flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
"repeatCount=%d), policyFlags=0x%08x",
- deviceId, source, displayId, keyActionToString(action).c_str(), flags,
+ deviceId, source, displayId, KeyEvent::actionToString(action), flags,
keyCode, scanCode, metaState, repeatCount, policyFlags);
}
@@ -253,7 +221,7 @@
"buttonState=0x%08x, "
"classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, "
"xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[",
- deviceId, source, displayId, motionActionToString(action).c_str(),
+ deviceId, source, displayId, MotionEvent::actionToString(action),
actionButton, flags, metaState, buttonState,
motionClassificationToString(classification), edgeFlags, xPrecision,
yPrecision, xCursorPosition, yCursorPosition);
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index a135409..6b7697d 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -198,7 +198,11 @@
float globalScaleFactor;
float windowXScale = 1.0f;
float windowYScale = 1.0f;
+ // Both deliveryTime and timeoutTime are only populated when the entry is sent to the app,
+ // and will be undefined before that.
nsecs_t deliveryTime; // time when the event was actually delivered
+ // An ANR will be triggered if a response for this entry is not received by timeoutTime
+ nsecs_t timeoutTime;
// Set to the resolved ID, action and flags when the event is enqueued.
int32_t resolvedEventId;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 8dddd6d..3865f29 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -89,18 +89,17 @@
// before considering it stale and dropping it.
constexpr nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec
-// Amount of time to allow touch events to be streamed out to a connection before requiring
-// that the first event be finished. This value extends the ANR timeout by the specified
-// amount. For example, if streaming is allowed to get ahead by one second relative to the
-// queue of waiting unfinished events, then ANRs will similarly be delayed by one second.
-constexpr nsecs_t STREAM_AHEAD_EVENT_TIMEOUT = 500 * 1000000LL; // 0.5sec
-
// Log a warning when an event takes longer than this to process, even if an ANR does not occur.
constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2sec
// Log a warning when an interception call takes longer than this to process.
constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms;
+// Additional key latency in case a connection is still processing some motion events.
+// This will help with the case when a user touched a button that opens a new window,
+// and gives us the chance to dispatch the key to this new window.
+constexpr std::chrono::nanoseconds KEY_WAITING_FOR_EVENTS_TIMEOUT = 500ms;
+
// Number of recent events to keep for debugging purposes.
constexpr size_t RECENT_QUEUE_MAX_SIZE = 10;
@@ -328,6 +327,18 @@
return dispatchEntry;
}
+static void addGestureMonitors(const std::vector<Monitor>& monitors,
+ std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
+ float yOffset = 0) {
+ if (monitors.empty()) {
+ return;
+ }
+ outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size());
+ for (const Monitor& monitor : monitors) {
+ outTouchedMonitors.emplace_back(monitor, xOffset, yOffset);
+ }
+}
+
static std::array<uint8_t, 128> getRandomKey() {
std::array<uint8_t, 128> key;
if (RAND_bytes(key.data(), key.size()) != 1) {
@@ -392,8 +403,7 @@
// To avoid leaking stack in case that call never comes, and for tests,
// initialize it here anyways.
mInTouchMode(true),
- mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
- mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
+ mFocusedDisplayId(ADISPLAY_ID_DEFAULT) {
mLooper = new Looper(false);
mReporter = createInputReporter();
@@ -453,6 +463,11 @@
nextWakeupTime = LONG_LONG_MIN;
}
+ // If we are still waiting for ack on some events,
+ // we might have to wake up earlier to check if an app is anr'ing.
+ const nsecs_t nextAnrCheck = processAnrsLocked();
+ nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
+
// We are about to enter an infinitely long sleep, because we have no commands or
// pending or queued events
if (nextWakeupTime == LONG_LONG_MAX) {
@@ -466,6 +481,55 @@
mLooper->pollOnce(timeoutMillis);
}
+/**
+ * Check if any of the connections' wait queues have events that are too old.
+ * If we waited for events to be ack'ed for more than the window timeout, raise an ANR.
+ * Return the time at which we should wake up next.
+ */
+nsecs_t InputDispatcher::processAnrsLocked() {
+ const nsecs_t currentTime = now();
+ nsecs_t nextAnrCheck = LONG_LONG_MAX;
+ // Check if we are waiting for a focused window to appear. Raise ANR if waited too long
+ if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
+ if (currentTime >= *mNoFocusedWindowTimeoutTime) {
+ onAnrLocked(mAwaitedFocusedApplication);
+ mAwaitedFocusedApplication.clear();
+ return LONG_LONG_MIN;
+ } else {
+ // Keep waiting
+ const nsecs_t millisRemaining = ns2ms(*mNoFocusedWindowTimeoutTime - currentTime);
+ ALOGW("Still no focused window. Will drop the event in %" PRId64 "ms", millisRemaining);
+ nextAnrCheck = *mNoFocusedWindowTimeoutTime;
+ }
+ }
+
+ // Check if any connection ANRs are due
+ nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());
+ if (currentTime < nextAnrCheck) { // most likely scenario
+ return nextAnrCheck; // everything is normal. Let's check again at nextAnrCheck
+ }
+
+ // If we reached here, we have an unresponsive connection.
+ sp<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
+ if (connection == nullptr) {
+ ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout());
+ return nextAnrCheck;
+ }
+ connection->responsive = false;
+ // Stop waking up for this unresponsive connection
+ mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
+ onAnrLocked(connection);
+ return LONG_LONG_MIN;
+}
+
+nsecs_t InputDispatcher::getDispatchingTimeoutLocked(const sp<IBinder>& token) {
+ sp<InputWindowHandle> window = getWindowHandleLocked(token);
+ if (window != nullptr) {
+ return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT).count();
+ }
+ return DEFAULT_INPUT_DISPATCHING_TIMEOUT.count();
+}
+
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
nsecs_t currentTime = now();
@@ -529,9 +593,6 @@
if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
pokeUserActivityLocked(*mPendingEvent);
}
-
- // Get ready to dispatch the event.
- resetAnrTimeoutsLocked();
}
// Now we have an event to dispatch.
@@ -625,24 +686,57 @@
* Return false otherwise (the default behaviour)
*/
bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) {
- bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN &&
+ const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN &&
(motionEntry.source & AINPUT_SOURCE_CLASS_POINTER);
- if (isPointerDownEvent &&
- mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY &&
- mInputTargetWaitApplicationToken != nullptr) {
+
+ // Optimize case where the current application is unresponsive and the user
+ // decides to touch a window in a different application.
+ // If the application takes too long to catch up then we drop all events preceding
+ // the touch into the other window.
+ if (isPointerDownEvent && mAwaitedFocusedApplication != nullptr) {
int32_t displayId = motionEntry.displayId;
int32_t x = static_cast<int32_t>(
motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
int32_t y = static_cast<int32_t>(
motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
- sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
+ sp<InputWindowHandle> touchedWindowHandle =
+ findTouchedWindowAtLocked(displayId, x, y, nullptr);
if (touchedWindowHandle != nullptr &&
- touchedWindowHandle->getApplicationToken() != mInputTargetWaitApplicationToken) {
+ touchedWindowHandle->getApplicationToken() !=
+ mAwaitedFocusedApplication->getApplicationToken()) {
// User touched a different application than the one we are waiting on.
- // Flag the event, and start pruning the input queue.
- ALOGI("Pruning input queue because user touched a different application");
+ ALOGI("Pruning input queue because user touched a different application while waiting "
+ "for %s",
+ mAwaitedFocusedApplication->getName().c_str());
return true;
}
+
+ // Alternatively, maybe there's a gesture monitor that could handle this event
+ std::vector<TouchedMonitor> gestureMonitors =
+ findTouchedGestureMonitorsLocked(displayId, {});
+ for (TouchedMonitor& gestureMonitor : gestureMonitors) {
+ sp<Connection> connection =
+ getConnectionLocked(gestureMonitor.monitor.inputChannel->getConnectionToken());
+ if (connection->responsive) {
+ // This monitor could take more input. Drop all events preceding this
+ // event, so that gesture monitor could get a chance to receive the stream
+ ALOGW("Pruning the input queue because %s is unresponsive, but we have a "
+ "responsive gesture monitor that may handle the event",
+ mAwaitedFocusedApplication->getName().c_str());
+ return true;
+ }
+ }
+ }
+
+ // Prevent getting stuck: if we have a pending key event, and some motion events that have not
+ // yet been processed by some connections, the dispatcher will wait for these motion
+ // events to be processed before dispatching the key event. This is because these motion events
+ // may cause a new window to be launched, which the user might expect to receive focus.
+ // To prevent waiting forever for such events, just send the key to the currently focused window
+ if (isPointerDownEvent && mKeyIsWaitingForEventsTimeout) {
+ ALOGD("Received a new pointer down event, stop waiting for events to process and "
+ "just send the pending key event to the focused window.");
+ mKeyIsWaitingForEventsTimeout = now();
}
return false;
}
@@ -676,10 +770,6 @@
}
case EventEntry::Type::MOTION: {
- // Optimize case where the current application is unresponsive and the user
- // decides to touch a window in a different application.
- // If the application takes too long to catch up then we drop all events preceding
- // the touch into the other window.
if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(*entry))) {
mNextUnblockedEvent = entry;
needWake = true;
@@ -710,8 +800,13 @@
}
sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
- int32_t y, bool addOutsideTargets,
+ int32_t y, TouchState* touchState,
+ bool addOutsideTargets,
bool addPortalWindows) {
+ if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {
+ LOG_ALWAYS_FATAL(
+ "Must provide a valid touch state if adding portal windows or outside targets");
+ }
// Traverse windows from front to back to find touched window.
const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
@@ -730,9 +825,9 @@
portalToDisplayId != displayId) {
if (addPortalWindows) {
// For the monitoring channels of the display.
- mTempTouchState.addPortalWindow(windowHandle);
+ touchState->addPortalWindow(windowHandle);
}
- return findTouchedWindowAtLocked(portalToDisplayId, x, y,
+ return findTouchedWindowAtLocked(portalToDisplayId, x, y, touchState,
addOutsideTargets, addPortalWindows);
}
// Found window.
@@ -741,9 +836,9 @@
}
if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
- mTempTouchState.addOrUpdateWindow(windowHandle,
- InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
- BitSet32(0));
+ touchState->addOrUpdateWindow(windowHandle,
+ InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
+ BitSet32(0));
}
}
}
@@ -752,7 +847,7 @@
}
std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked(
- int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) {
+ int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const {
std::vector<TouchedMonitor> touchedMonitors;
std::vector<Monitor> monitors = getValueByKey(mGestureMonitorsByDisplay, displayId);
@@ -766,18 +861,6 @@
return touchedMonitors;
}
-void InputDispatcher::addGestureMonitors(const std::vector<Monitor>& monitors,
- std::vector<TouchedMonitor>& outTouchedMonitors,
- float xOffset, float yOffset) {
- if (monitors.empty()) {
- return;
- }
- outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size());
- for (const Monitor& monitor : monitors) {
- outTouchedMonitors.emplace_back(monitor, xOffset, yOffset);
- }
-}
-
void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) {
const char* reason;
switch (dropReason) {
@@ -901,7 +984,6 @@
void InputDispatcher::releasePendingEventLocked() {
if (mPendingEvent) {
- resetAnrTimeoutsLocked();
releaseInboundEventLocked(mPendingEvent);
mPendingEvent = nullptr;
}
@@ -1197,9 +1279,10 @@
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
if (isPointerEvent) {
- ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(entry->displayId);
- if (stateIndex >= 0) {
- const TouchState& state = mTouchStatesByDisplay.valueAt(stateIndex);
+ std::unordered_map<int32_t, TouchState>::iterator it =
+ mTouchStatesByDisplay.find(entry->displayId);
+ if (it != mTouchStatesByDisplay.end()) {
+ const TouchState& state = it->second;
if (!state.portalWindows.empty()) {
// The event has gone through these portal windows, so we add monitoring targets of
// the corresponding displays as well.
@@ -1278,109 +1361,29 @@
}
}
-int32_t InputDispatcher::handleTargetsNotReadyLocked(
- nsecs_t currentTime, const EventEntry& entry,
- const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime, const char* reason) {
- if (applicationHandle == nullptr && windowHandle == nullptr) {
- if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
- if (DEBUG_FOCUS) {
- ALOGD("Waiting for system to become ready for input. Reason: %s", reason);
- }
- mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
- mInputTargetWaitStartTime = currentTime;
- mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
- mInputTargetWaitTimeoutExpired = false;
- mInputTargetWaitApplicationToken.clear();
- }
- } else {
- if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
- ALOGI("Waiting for application to become ready for input: %s. Reason: %s",
- getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason);
- std::chrono::nanoseconds timeout;
- if (windowHandle != nullptr) {
- timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
- } else if (applicationHandle != nullptr) {
- timeout =
- applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
- } else {
- timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
- }
-
- mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
- mInputTargetWaitStartTime = currentTime;
- mInputTargetWaitTimeoutTime = currentTime + timeout.count();
- mInputTargetWaitTimeoutExpired = false;
- mInputTargetWaitApplicationToken.clear();
-
- if (windowHandle != nullptr) {
- mInputTargetWaitApplicationToken = windowHandle->getApplicationToken();
- }
- if (mInputTargetWaitApplicationToken == nullptr && applicationHandle != nullptr) {
- mInputTargetWaitApplicationToken = applicationHandle->getApplicationToken();
- }
- }
- }
-
- if (mInputTargetWaitTimeoutExpired) {
- return INPUT_EVENT_INJECTION_TIMED_OUT;
- }
-
- if (currentTime >= mInputTargetWaitTimeoutTime) {
- onAnrLocked(currentTime, applicationHandle, windowHandle, entry.eventTime,
- mInputTargetWaitStartTime, reason);
-
- // Force poll loop to wake up immediately on next iteration once we get the
- // ANR response back from the policy.
- *nextWakeupTime = LONG_LONG_MIN;
- return INPUT_EVENT_INJECTION_PENDING;
- } else {
- // Force poll loop to wake up when timeout is due.
- if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
- *nextWakeupTime = mInputTargetWaitTimeoutTime;
- }
- return INPUT_EVENT_INJECTION_PENDING;
+void InputDispatcher::cancelEventsForAnrLocked(const sp<Connection>& connection) {
+ // We will not be breaking any connections here, even if the policy wants us to abort dispatch.
+ // If the policy decides to close the app, we will get a channel removal event via
+ // unregisterInputChannel, and will clean up the connection that way. We are already not
+ // sending new pointers to the connection when it blocked, but focused events will continue to
+ // pile up.
+ ALOGW("Canceling events for %s because it is unresponsive",
+ connection->inputChannel->getName().c_str());
+ if (connection->status == Connection::STATUS_NORMAL) {
+ CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
+ "application not responding");
+ synthesizeCancelationEventsForConnectionLocked(connection, options);
}
}
-void InputDispatcher::removeWindowByTokenLocked(const sp<IBinder>& token) {
- for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
- TouchState& state = mTouchStatesByDisplay.editValueAt(d);
- state.removeWindowByToken(token);
- }
-}
-
-void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(
- nsecs_t timeoutExtension, const sp<IBinder>& inputConnectionToken) {
- if (timeoutExtension > 0) {
- // Extend the timeout.
- mInputTargetWaitTimeoutTime = now() + timeoutExtension;
- } else {
- // Give up.
- mInputTargetWaitTimeoutExpired = true;
-
- // Input state will not be realistic. Mark it out of sync.
- sp<Connection> connection = getConnectionLocked(inputConnectionToken);
- if (connection != nullptr) {
- removeWindowByTokenLocked(inputConnectionToken);
-
- if (connection->status == Connection::STATUS_NORMAL) {
- CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
- "application not responding");
- synthesizeCancelationEventsForConnectionLocked(connection, options);
- }
- }
- }
-}
-
-void InputDispatcher::resetAnrTimeoutsLocked() {
+void InputDispatcher::resetNoFocusedWindowTimeoutLocked() {
if (DEBUG_FOCUS) {
ALOGD("Resetting ANR timeouts.");
}
// Reset input target wait timeout.
- mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
- mInputTargetWaitApplicationToken.clear();
+ mNoFocusedWindowTimeoutTime = std::nullopt;
+ mAwaitedFocusedApplication.clear();
}
/**
@@ -1411,6 +1414,36 @@
return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId;
}
+bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime,
+ const char* focusedWindowName) {
+ if (mAnrTracker.empty()) {
+ // already processed all events that we waited for
+ mKeyIsWaitingForEventsTimeout = std::nullopt;
+ return false;
+ }
+
+ if (!mKeyIsWaitingForEventsTimeout.has_value()) {
+ // Start the timer
+ ALOGD("Waiting to send key to %s because there are unprocessed events that may cause "
+ "focus to change",
+ focusedWindowName);
+ mKeyIsWaitingForEventsTimeout = currentTime + KEY_WAITING_FOR_EVENTS_TIMEOUT.count();
+ return true;
+ }
+
+ // We still have pending events, and already started the timer
+ if (currentTime < *mKeyIsWaitingForEventsTimeout) {
+ return true; // Still waiting
+ }
+
+ // Waited too long, and some connection still hasn't processed all motions
+ // Just send the key to the focused window
+ ALOGW("Dispatching key to %s even though there are other unprocessed events",
+ focusedWindowName);
+ mKeyIsWaitingForEventsTimeout = std::nullopt;
+ return false;
+}
+
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
const EventEntry& entry,
std::vector<InputTarget>& inputTargets,
@@ -1425,31 +1458,70 @@
// If there is no currently focused window and no focused application
// then drop the event.
- if (focusedWindowHandle == nullptr) {
- if (focusedApplicationHandle != nullptr) {
- return handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle,
- nullptr, nextWakeupTime,
- "Waiting because no window has focus but there is "
- "a focused application that may eventually add a "
- "window when it finishes starting up.");
- }
-
- ALOGI("Dropping event because there is no focused window or focused application in display "
- "%" PRId32 ".",
- displayId);
+ if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
+ ALOGI("Dropping %s event because there is no focused window or focused application in "
+ "display %" PRId32 ".",
+ EventEntry::typeToString(entry.type), displayId);
return INPUT_EVENT_INJECTION_FAILED;
}
+ // Compatibility behavior: raise ANR if there is a focused application, but no focused window.
+ // Only start counting when we have a focused event to dispatch. The ANR is canceled if we
+ // start interacting with another application via touch (app switch). This code can be removed
+ // if the "no focused window ANR" is moved to the policy. Input doesn't know whether
+ // an app is expected to have a focused window.
+ if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) {
+ if (!mNoFocusedWindowTimeoutTime.has_value()) {
+ // We just discovered that there's no focused window. Start the ANR timer
+ const nsecs_t timeout = focusedApplicationHandle->getDispatchingTimeout(
+ DEFAULT_INPUT_DISPATCHING_TIMEOUT.count());
+ mNoFocusedWindowTimeoutTime = currentTime + timeout;
+ mAwaitedFocusedApplication = focusedApplicationHandle;
+ ALOGW("Waiting because no window has focus but %s may eventually add a "
+ "window when it finishes starting up. Will wait for %" PRId64 "ms",
+ mAwaitedFocusedApplication->getName().c_str(), ns2ms(timeout));
+ *nextWakeupTime = *mNoFocusedWindowTimeoutTime;
+ return INPUT_EVENT_INJECTION_PENDING;
+ } else if (currentTime > *mNoFocusedWindowTimeoutTime) {
+ // Already raised ANR. Drop the event
+ ALOGE("Dropping %s event because there is no focused window",
+ EventEntry::typeToString(entry.type));
+ return INPUT_EVENT_INJECTION_FAILED;
+ } else {
+ // Still waiting for the focused window
+ return INPUT_EVENT_INJECTION_PENDING;
+ }
+ }
+
+ // we have a valid, non-null focused window
+ resetNoFocusedWindowTimeoutLocked();
+
// Check permissions.
if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {
return INPUT_EVENT_INJECTION_PERMISSION_DENIED;
}
- // Check whether the window is ready for more input.
- reason = checkWindowReadyForMoreInputLocked(currentTime, focusedWindowHandle, entry, "focused");
- if (!reason.empty()) {
- return handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle,
- focusedWindowHandle, nextWakeupTime, reason.c_str());
+ if (focusedWindowHandle->getInfo()->paused) {
+ ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
+ return INPUT_EVENT_INJECTION_PENDING;
+ }
+
+ // If the event is a key event, then we must wait for all previous events to
+ // complete before delivering it because previous events may have the
+ // side-effect of transferring focus to a different window and we want to
+ // ensure that the following keys are sent to the new window.
+ //
+ // Suppose the user touches a button in a window then immediately presses "A".
+ // If the button causes a pop-up window to appear then we want to ensure that
+ // the "A" key is delivered to the new pop-up window. This is because users
+ // often anticipate pending UI changes when typing on a keyboard.
+ // To obtain this behavior, we must serialize key events with respect to all
+ // prior input events.
+ if (entry.type == EventEntry::Type::KEY) {
+ if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {
+ *nextWakeupTime = *mKeyIsWaitingForEventsTimeout;
+ return INPUT_EVENT_INJECTION_PENDING;
+ }
}
// Success! Output targets.
@@ -1461,6 +1533,32 @@
return INPUT_EVENT_INJECTION_SUCCEEDED;
}
+/**
+ * Given a list of monitors, remove the ones we cannot find a connection for, and the ones
+ * that are currently unresponsive.
+ */
+std::vector<TouchedMonitor> InputDispatcher::selectResponsiveMonitorsLocked(
+ const std::vector<TouchedMonitor>& monitors) const {
+ std::vector<TouchedMonitor> responsiveMonitors;
+ std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors),
+ [this](const TouchedMonitor& monitor) REQUIRES(mLock) {
+ sp<Connection> connection = getConnectionLocked(
+ monitor.monitor.inputChannel->getConnectionToken());
+ if (connection == nullptr) {
+ ALOGE("Could not find connection for monitor %s",
+ monitor.monitor.inputChannel->getName().c_str());
+ return false;
+ }
+ if (!connection->responsive) {
+ ALOGW("Unresponsive monitor %s will not get the new gesture",
+ connection->inputChannel->getName().c_str());
+ return false;
+ }
+ return true;
+ });
+ return responsiveMonitors;
+}
+
int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
const MotionEntry& entry,
std::vector<InputTarget>& inputTargets,
@@ -1484,20 +1582,22 @@
InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
sp<InputWindowHandle> newHoverWindowHandle;
- // Copy current touch state into mTempTouchState.
- // This state is always reset at the end of this function, so if we don't find state
- // for the specified display then our initial state will be empty.
+ // Copy current touch state into tempTouchState.
+ // This state will be used to update mTouchStatesByDisplay at the end of this function.
+ // If no state for the specified display exists, then our initial state will be empty.
const TouchState* oldState = nullptr;
- ssize_t oldStateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
- if (oldStateIndex >= 0) {
- oldState = &mTouchStatesByDisplay.valueAt(oldStateIndex);
- mTempTouchState.copyFrom(*oldState);
+ TouchState tempTouchState;
+ std::unordered_map<int32_t, TouchState>::iterator oldStateIt =
+ mTouchStatesByDisplay.find(displayId);
+ if (oldStateIt != mTouchStatesByDisplay.end()) {
+ oldState = &(oldStateIt->second);
+ tempTouchState.copyFrom(*oldState);
}
- bool isSplit = mTempTouchState.split;
- bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0 &&
- (mTempTouchState.deviceId != entry.deviceId || mTempTouchState.source != entry.source ||
- mTempTouchState.displayId != displayId);
+ bool isSplit = tempTouchState.split;
+ bool switchedDevice = tempTouchState.deviceId >= 0 && tempTouchState.displayId >= 0 &&
+ (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source ||
+ tempTouchState.displayId != displayId);
bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
@@ -1507,30 +1607,26 @@
bool wrongDevice = false;
if (newGesture) {
bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
- if (switchedDevice && mTempTouchState.down && !down && !isHoverAction) {
- if (DEBUG_FOCUS) {
- ALOGD("Dropping event because a pointer for a different device is already down "
- "in display %" PRId32,
- displayId);
- }
+ if (switchedDevice && tempTouchState.down && !down && !isHoverAction) {
+ ALOGI("Dropping event because a pointer for a different device is already down "
+ "in display %" PRId32,
+ displayId);
// TODO: test multiple simultaneous input streams.
injectionResult = INPUT_EVENT_INJECTION_FAILED;
switchedDevice = false;
wrongDevice = true;
goto Failed;
}
- mTempTouchState.reset();
- mTempTouchState.down = down;
- mTempTouchState.deviceId = entry.deviceId;
- mTempTouchState.source = entry.source;
- mTempTouchState.displayId = displayId;
+ tempTouchState.reset();
+ tempTouchState.down = down;
+ tempTouchState.deviceId = entry.deviceId;
+ tempTouchState.source = entry.source;
+ tempTouchState.displayId = displayId;
isSplit = false;
} else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
- if (DEBUG_FOCUS) {
- ALOGI("Dropping move event because a pointer for a different device is already active "
- "in display %" PRId32,
- displayId);
- }
+ ALOGI("Dropping move event because a pointer for a different device is already active "
+ "in display %" PRId32,
+ displayId);
// TODO: test multiple simultaneous input streams.
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
switchedDevice = false;
@@ -1554,11 +1650,11 @@
}
bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
sp<InputWindowHandle> newTouchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y, isDown /*addOutsideTargets*/,
- true /*addPortalWindows*/);
+ findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
+ isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
std::vector<TouchedMonitor> newGestureMonitors = isDown
- ? findTouchedGestureMonitorsLocked(displayId, mTempTouchState.portalWindows)
+ ? findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows)
: std::vector<TouchedMonitor>{};
// Figure out whether splitting will be allowed for this window.
@@ -1575,9 +1671,32 @@
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
// Try to assign the pointer to the first foreground window we find, if there is one.
- newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle();
+ newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
}
+ if (newTouchedWindowHandle != nullptr && newTouchedWindowHandle->getInfo()->paused) {
+ ALOGI("Not sending touch event to %s because it is paused",
+ newTouchedWindowHandle->getName().c_str());
+ newTouchedWindowHandle = nullptr;
+ }
+
+ if (newTouchedWindowHandle != nullptr) {
+ sp<Connection> connection = getConnectionLocked(newTouchedWindowHandle->getToken());
+ if (connection == nullptr) {
+ ALOGI("Could not find connection for %s",
+ newTouchedWindowHandle->getName().c_str());
+ newTouchedWindowHandle = nullptr;
+ } else if (!connection->responsive) {
+ // don't send the new touch to an unresponsive window
+ ALOGW("Unresponsive window %s will not get the new gesture at %" PRIu64,
+ newTouchedWindowHandle->getName().c_str(), entry.eventTime);
+ newTouchedWindowHandle = nullptr;
+ }
+ }
+
+ // Also don't send the new touch event to unresponsive gesture monitors
+ newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors);
+
if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) {
ALOGI("Dropping event because there is no touchable window or gesture monitor at "
"(%d, %d) in display %" PRId32 ".",
@@ -1611,15 +1730,15 @@
uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
pointerIds.markBit(pointerId);
}
- mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
+ tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
}
- mTempTouchState.addGestureMonitors(newGestureMonitors);
+ tempTouchState.addGestureMonitors(newGestureMonitors);
} else {
/* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
// If the pointer is not currently down, then ignore the event.
- if (!mTempTouchState.down) {
+ if (!tempTouchState.down) {
if (DEBUG_FOCUS) {
ALOGD("Dropping event because the pointer is not down or we previously "
"dropped the pointer down event in display %" PRId32,
@@ -1631,14 +1750,14 @@
// Check whether touches should slip outside of the current foreground window.
if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
- mTempTouchState.isSlippery()) {
+ tempTouchState.isSlippery()) {
int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
sp<InputWindowHandle> oldTouchedWindowHandle =
- mTempTouchState.getFirstForegroundWindowHandle();
+ tempTouchState.getFirstForegroundWindowHandle();
sp<InputWindowHandle> newTouchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y);
+ findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
if (oldTouchedWindowHandle != newTouchedWindowHandle &&
oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
if (DEBUG_FOCUS) {
@@ -1647,9 +1766,9 @@
newTouchedWindowHandle->getName().c_str(), displayId);
}
// Make a slippery exit from the old window.
- mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,
- BitSet32(0));
+ tempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
+ InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,
+ BitSet32(0));
// Make a slippery entrance into the new window.
if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
@@ -1669,7 +1788,7 @@
if (isSplit) {
pointerIds.markBit(entry.pointerProperties[0].id);
}
- mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
+ tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
}
}
}
@@ -1681,9 +1800,8 @@
ALOGD("Sending hover exit event to window %s.",
mLastHoverWindowHandle->getName().c_str());
#endif
- mTempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT,
- BitSet32(0));
+ tempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
+ InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
}
// Let the new window know that the hover sequence is starting.
@@ -1692,9 +1810,9 @@
ALOGD("Sending hover enter event to window %s.",
newHoverWindowHandle->getName().c_str());
#endif
- mTempTouchState.addOrUpdateWindow(newHoverWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
- BitSet32(0));
+ tempTouchState.addOrUpdateWindow(newHoverWindowHandle,
+ InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
+ BitSet32(0));
}
}
@@ -1702,7 +1820,7 @@
// is at least one touched foreground window.
{
bool haveForegroundWindow = false;
- for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
+ for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
haveForegroundWindow = true;
if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) {
@@ -1712,13 +1830,11 @@
}
}
}
- bool hasGestureMonitor = !mTempTouchState.gestureMonitors.empty();
+ bool hasGestureMonitor = !tempTouchState.gestureMonitors.empty();
if (!haveForegroundWindow && !hasGestureMonitor) {
- if (DEBUG_FOCUS) {
- ALOGD("Dropping event because there is no touched foreground window in display "
- "%" PRId32 " or gesture monitor to receive it.",
- displayId);
- }
+ ALOGI("Dropping event because there is no touched foreground window in display "
+ "%" PRId32 " or gesture monitor to receive it.",
+ displayId);
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
}
@@ -1731,37 +1847,22 @@
// set the policy flag that we will not reveal coordinate information to this window.
if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
sp<InputWindowHandle> foregroundWindowHandle =
- mTempTouchState.getFirstForegroundWindowHandle();
+ tempTouchState.getFirstForegroundWindowHandle();
if (foregroundWindowHandle) {
const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
- for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
+ for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
- mTempTouchState.addOrUpdateWindow(inputWindowHandle,
- InputTarget::FLAG_ZERO_COORDS,
- BitSet32(0));
+ tempTouchState.addOrUpdateWindow(inputWindowHandle,
+ InputTarget::FLAG_ZERO_COORDS,
+ BitSet32(0));
}
}
}
}
}
- // Ensure all touched foreground windows are ready for new input.
- for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
- if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
- // Check whether the window is ready for more input.
- std::string reason =
- checkWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle,
- entry, "touched");
- if (!reason.empty()) {
- return handleTargetsNotReadyLocked(currentTime, entry, nullptr,
- touchedWindow.windowHandle, nextWakeupTime,
- reason.c_str());
- }
- }
- }
-
// If this is the first pointer going down and the touched window has a wallpaper
// then also add the touched wallpaper windows so they are locked in for the duration
// of the touch gesture.
@@ -1770,7 +1871,7 @@
// to View.onGenericMotionEvent to enable wallpapers to handle these events.
if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
sp<InputWindowHandle> foregroundWindowHandle =
- mTempTouchState.getFirstForegroundWindowHandle();
+ tempTouchState.getFirstForegroundWindowHandle();
if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) {
const std::vector<sp<InputWindowHandle>> windowHandles =
getWindowHandlesLocked(displayId);
@@ -1778,7 +1879,7 @@
const InputWindowInfo* info = windowHandle->getInfo();
if (info->displayId == displayId &&
windowHandle->getInfo()->layoutParamsType == InputWindowInfo::TYPE_WALLPAPER) {
- mTempTouchState
+ tempTouchState
.addOrUpdateWindow(windowHandle,
InputTarget::FLAG_WINDOW_IS_OBSCURED |
InputTarget::
@@ -1793,19 +1894,19 @@
// Success! Output targets.
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
- for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
+ for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
touchedWindow.pointerIds, inputTargets);
}
- for (const TouchedMonitor& touchedMonitor : mTempTouchState.gestureMonitors) {
+ for (const TouchedMonitor& touchedMonitor : tempTouchState.gestureMonitors) {
addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,
touchedMonitor.yOffset, inputTargets);
}
// Drop the outside or hover touch windows since we will not care about them
// in the next iteration.
- mTempTouchState.filterNonAsIsTouchWindows();
+ tempTouchState.filterNonAsIsTouchWindows();
Failed:
// Check injection permission once and for all.
@@ -1839,17 +1940,17 @@
}
*outConflictingPointerActions = true;
}
- mTempTouchState.reset();
+ tempTouchState.reset();
if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- mTempTouchState.deviceId = entry.deviceId;
- mTempTouchState.source = entry.source;
- mTempTouchState.displayId = displayId;
+ tempTouchState.deviceId = entry.deviceId;
+ tempTouchState.source = entry.source;
+ tempTouchState.displayId = displayId;
}
} else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
// All pointers up or canceled.
- mTempTouchState.reset();
+ tempTouchState.reset();
} else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
// First pointer went down.
if (oldState && oldState->down) {
@@ -1864,12 +1965,12 @@
int32_t pointerIndex = getMotionEventActionPointerIndex(action);
uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
- for (size_t i = 0; i < mTempTouchState.windows.size();) {
- TouchedWindow& touchedWindow = mTempTouchState.windows[i];
+ for (size_t i = 0; i < tempTouchState.windows.size();) {
+ TouchedWindow& touchedWindow = tempTouchState.windows[i];
if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
touchedWindow.pointerIds.clearBit(pointerId);
if (touchedWindow.pointerIds.isEmpty()) {
- mTempTouchState.windows.erase(mTempTouchState.windows.begin() + i);
+ tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
continue;
}
}
@@ -1881,14 +1982,10 @@
// Save changes unless the action was scroll in which case the temporary touch
// state was only valid for this one action.
if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
- if (mTempTouchState.displayId >= 0) {
- if (oldStateIndex >= 0) {
- mTouchStatesByDisplay.editValueAt(oldStateIndex).copyFrom(mTempTouchState);
- } else {
- mTouchStatesByDisplay.add(displayId, mTempTouchState);
- }
- } else if (oldStateIndex >= 0) {
- mTouchStatesByDisplay.removeItemsAt(oldStateIndex);
+ if (tempTouchState.displayId >= 0) {
+ mTouchStatesByDisplay[displayId] = tempTouchState;
+ } else {
+ mTouchStatesByDisplay.erase(displayId);
}
}
@@ -1991,14 +2088,9 @@
auto otherInfo = otherHandle->getInfo();
if (!otherInfo->visible) {
return false;
- } else if (info->ownerPid == otherInfo->ownerPid && otherHandle->getToken() == nullptr) {
- // In general, if ownerPid is the same we don't want to generate occlusion
- // events. This line is now necessary since we are including all Surfaces
- // in occlusion calculation, so if we didn't check PID like this SurfaceView
- // would occlude their parents. On the other hand before we started including
- // all surfaces in occlusion calculation and had this line, we would count
- // windows with an input channel from the same PID as occluding, and so we
- // preserve this behavior with the getToken() == null check.
+ } else if (info->ownerPid == otherInfo->ownerPid) {
+ // If ownerPid is the same we don't generate occlusion events as there
+ // is no in-process security boundary.
return false;
} else if (otherInfo->isTrustedOverlay()) {
return false;
@@ -2043,92 +2135,6 @@
return false;
}
-std::string InputDispatcher::checkWindowReadyForMoreInputLocked(
- nsecs_t currentTime, const sp<InputWindowHandle>& windowHandle,
- const EventEntry& eventEntry, const char* targetType) {
- // If the window is paused then keep waiting.
- if (windowHandle->getInfo()->paused) {
- return StringPrintf("Waiting because the %s window is paused.", targetType);
- }
-
- // If the window's connection is not registered then keep waiting.
- sp<Connection> connection = getConnectionLocked(windowHandle->getToken());
- if (connection == nullptr) {
- return StringPrintf("Waiting because the %s window's input channel is not "
- "registered with the input dispatcher. The window may be in the "
- "process of being removed.",
- targetType);
- }
-
- // If the connection is dead then keep waiting.
- if (connection->status != Connection::STATUS_NORMAL) {
- return StringPrintf("Waiting because the %s window's input connection is %s."
- "The window may be in the process of being removed.",
- targetType, connection->getStatusLabel());
- }
-
- // If the connection is backed up then keep waiting.
- if (connection->inputPublisherBlocked) {
- return StringPrintf("Waiting because the %s window's input channel is full. "
- "Outbound queue length: %zu. Wait queue length: %zu.",
- targetType, connection->outboundQueue.size(),
- connection->waitQueue.size());
- }
-
- // Ensure that the dispatch queues aren't too far backed up for this event.
- if (eventEntry.type == EventEntry::Type::KEY) {
- // If the event is a key event, then we must wait for all previous events to
- // complete before delivering it because previous events may have the
- // side-effect of transferring focus to a different window and we want to
- // ensure that the following keys are sent to the new window.
- //
- // Suppose the user touches a button in a window then immediately presses "A".
- // If the button causes a pop-up window to appear then we want to ensure that
- // the "A" key is delivered to the new pop-up window. This is because users
- // often anticipate pending UI changes when typing on a keyboard.
- // To obtain this behavior, we must serialize key events with respect to all
- // prior input events.
- if (!connection->outboundQueue.empty() || !connection->waitQueue.empty()) {
- return StringPrintf("Waiting to send key event because the %s window has not "
- "finished processing all of the input events that were previously "
- "delivered to it. Outbound queue length: %zu. Wait queue length: "
- "%zu.",
- targetType, connection->outboundQueue.size(),
- connection->waitQueue.size());
- }
- } else {
- // Touch events can always be sent to a window immediately because the user intended
- // to touch whatever was visible at the time. Even if focus changes or a new
- // window appears moments later, the touch event was meant to be delivered to
- // whatever window happened to be on screen at the time.
- //
- // Generic motion events, such as trackball or joystick events are a little trickier.
- // Like key events, generic motion events are delivered to the focused window.
- // Unlike key events, generic motion events don't tend to transfer focus to other
- // windows and it is not important for them to be serialized. So we prefer to deliver
- // generic motion events as soon as possible to improve efficiency and reduce lag
- // through batching.
- //
- // The one case where we pause input event delivery is when the wait queue is piling
- // up with lots of events because the application is not responding.
- // This condition ensures that ANRs are detected reliably.
- if (!connection->waitQueue.empty() &&
- currentTime >=
- connection->waitQueue.front()->deliveryTime + STREAM_AHEAD_EVENT_TIMEOUT) {
- return StringPrintf("Waiting to send non-key event because the %s window has not "
- "finished processing certain input events that were delivered to "
- "it over "
- "%0.1fms ago. Wait queue length: %zu. Wait queue head age: "
- "%0.1fms.",
- targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f,
- connection->waitQueue.size(),
- (currentTime - connection->waitQueue.front()->deliveryTime) *
- 0.000001f);
- }
- }
- return "";
-}
-
std::string InputDispatcher::getApplicationWindowLabel(
const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle) {
@@ -2461,6 +2467,9 @@
while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.front();
dispatchEntry->deliveryTime = currentTime;
+ const nsecs_t timeout =
+ getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken());
+ dispatchEntry->timeoutTime = currentTime + timeout;
// Publish the event.
status_t status;
@@ -2580,7 +2589,6 @@
"waiting for the application to catch up",
connection->getInputChannelName().c_str());
#endif
- connection->inputPublisherBlocked = true;
}
} else {
ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
@@ -2597,6 +2605,10 @@
dispatchEntry));
traceOutboundQueueLength(connection);
connection->waitQueue.push_back(dispatchEntry);
+ if (connection->responsive) {
+ mAnrTracker.insert(dispatchEntry->timeoutTime,
+ connection->inputChannel->getConnectionToken());
+ }
traceWaitQueueLength(connection);
}
}
@@ -2631,8 +2643,6 @@
connection->getInputChannelName().c_str(), seq, toString(handled));
#endif
- connection->inputPublisherBlocked = false;
-
if (connection->status == Connection::STATUS_BROKEN ||
connection->status == Connection::STATUS_ZOMBIE) {
return;
@@ -3043,7 +3053,7 @@
if (newKeyCode != AKEYCODE_UNKNOWN) {
std::scoped_lock _l(mLock);
struct KeyReplacement replacement = {keyCode, deviceId};
- mReplacedKeys.add(replacement, newKeyCode);
+ mReplacedKeys[replacement] = newKeyCode;
keyCode = newKeyCode;
metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
}
@@ -3053,10 +3063,10 @@
// even if the modifier was released between the down and the up events.
std::scoped_lock _l(mLock);
struct KeyReplacement replacement = {keyCode, deviceId};
- ssize_t index = mReplacedKeys.indexOfKey(replacement);
- if (index >= 0) {
- keyCode = mReplacedKeys.valueAt(index);
- mReplacedKeys.removeItemsAt(index);
+ auto replacementIt = mReplacedKeys.find(replacement);
+ if (replacementIt != mReplacedKeys.end()) {
+ keyCode = replacementIt->second;
+ mReplacedKeys.erase(replacementIt);
metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
}
}
@@ -3738,9 +3748,10 @@
}
}
- ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
- if (stateIndex >= 0) {
- TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
+ std::unordered_map<int32_t, TouchState>::iterator stateIt =
+ mTouchStatesByDisplay.find(displayId);
+ if (stateIt != mTouchStatesByDisplay.end()) {
+ TouchState& state = stateIt->second;
for (size_t i = 0; i < state.windows.size();) {
TouchedWindow& touchedWindow = state.windows[i];
if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
@@ -3787,15 +3798,17 @@
sp<InputApplicationHandle> oldFocusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
+
+ if (oldFocusedApplicationHandle == mAwaitedFocusedApplication &&
+ inputApplicationHandle != oldFocusedApplicationHandle) {
+ resetNoFocusedWindowTimeoutLocked();
+ }
+
if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) {
if (oldFocusedApplicationHandle != inputApplicationHandle) {
- if (oldFocusedApplicationHandle != nullptr) {
- resetAnrTimeoutsLocked();
- }
mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
}
} else if (oldFocusedApplicationHandle != nullptr) {
- resetAnrTimeoutsLocked();
oldFocusedApplicationHandle.clear();
mFocusedApplicationHandlesByDisplay.erase(displayId);
}
@@ -3876,7 +3889,7 @@
if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
if (mDispatchFrozen && !frozen) {
- resetAnrTimeoutsLocked();
+ resetNoFocusedWindowTimeoutLocked();
}
if (mDispatchEnabled && !enabled) {
@@ -3955,8 +3968,8 @@
}
bool found = false;
- for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
- TouchState& state = mTouchStatesByDisplay.editValueAt(d);
+ for (std::pair<const int32_t, TouchState>& pair : mTouchStatesByDisplay) {
+ TouchState& state = pair.second;
for (size_t i = 0; i < state.windows.size(); i++) {
const TouchedWindow& touchedWindow = state.windows[i];
if (touchedWindow.windowHandle == fromWindowHandle) {
@@ -4016,8 +4029,9 @@
resetKeyRepeatLocked();
releasePendingEventLocked();
drainInboundQueueLocked();
- resetAnrTimeoutsLocked();
+ resetNoFocusedWindowTimeoutLocked();
+ mAnrTracker.clear();
mTouchStatesByDisplay.clear();
mLastHoverWindowHandle.clear();
mReplacedKeys.clear();
@@ -4070,10 +4084,10 @@
dump += StringPrintf(INDENT "FocusedWindows: <none>\n");
}
- if (!mTouchStatesByDisplay.isEmpty()) {
+ if (!mTouchStatesByDisplay.empty()) {
dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
- for (size_t i = 0; i < mTouchStatesByDisplay.size(); i++) {
- const TouchState& state = mTouchStatesByDisplay.valueAt(i);
+ for (const std::pair<int32_t, TouchState>& pair : mTouchStatesByDisplay) {
+ const TouchState& state = pair.second;
dump += StringPrintf(INDENT2 "%d: down=%s, split=%s, deviceId=%d, source=0x%08x\n",
state.displayId, toString(state.down), toString(state.split),
state.deviceId, state.source);
@@ -4197,12 +4211,12 @@
dump += INDENT "InboundQueue: <empty>\n";
}
- if (!mReplacedKeys.isEmpty()) {
+ if (!mReplacedKeys.empty()) {
dump += INDENT "ReplacedKeys:\n";
- for (size_t i = 0; i < mReplacedKeys.size(); i++) {
- const KeyReplacement& replacement = mReplacedKeys.keyAt(i);
- int32_t newKeyCode = mReplacedKeys.valueAt(i);
- dump += StringPrintf(INDENT2 "%zu: originalKeyCode=%d, deviceId=%d, newKeyCode=%d\n", i,
+ for (const std::pair<KeyReplacement, int32_t>& pair : mReplacedKeys) {
+ const KeyReplacement& replacement = pair.first;
+ int32_t newKeyCode = pair.second;
+ dump += StringPrintf(INDENT2 "originalKeyCode=%d, deviceId=%d -> newKeyCode=%d\n",
replacement.keyCode, replacement.deviceId, newKeyCode);
}
} else {
@@ -4214,11 +4228,10 @@
for (const auto& pair : mConnectionsByFd) {
const sp<Connection>& connection = pair.second;
dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', "
- "status=%s, monitor=%s, inputPublisherBlocked=%s\n",
+ "status=%s, monitor=%s, responsive=%s\n",
pair.first, connection->getInputChannelName().c_str(),
connection->getWindowName().c_str(), connection->getStatusLabel(),
- toString(connection->monitor),
- toString(connection->inputPublisherBlocked));
+ toString(connection->monitor), toString(connection->responsive));
if (!connection->outboundQueue.empty()) {
dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n",
@@ -4419,13 +4432,14 @@
}
int32_t displayId = foundDisplayId.value();
- ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
- if (stateIndex < 0) {
+ std::unordered_map<int32_t, TouchState>::iterator stateIt =
+ mTouchStatesByDisplay.find(displayId);
+ if (stateIt == mTouchStatesByDisplay.end()) {
ALOGW("Failed to pilfer pointers: no pointers on display %" PRId32 ".", displayId);
return BAD_VALUE;
}
- TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
+ TouchState& state = stateIt->second;
std::optional<int32_t> foundDeviceId;
for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) {
if (touchedMonitor.monitor.inputChannel->getConnectionToken() == token) {
@@ -4485,6 +4499,7 @@
}
void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
+ mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
removeByValue(mConnectionsByFd, connection);
}
@@ -4522,17 +4537,69 @@
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::onAnrLocked(nsecs_t currentTime,
- const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime,
- nsecs_t waitStartTime, const char* reason) {
- float dispatchLatency = (currentTime - eventTime) * 0.000001f;
- float waitDuration = (currentTime - waitStartTime) * 0.000001f;
- ALOGI("Application is not responding: %s. "
- "It has been %0.1fms since event, %0.1fms since wait started. Reason: %s",
- getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), dispatchLatency,
- waitDuration, reason);
+void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
+ // Since we are allowing the policy to extend the timeout, maybe the waitQueue
+ // is already healthy again. Don't raise ANR in this situation
+ if (connection->waitQueue.empty()) {
+ ALOGI("Not raising ANR because the connection %s has recovered",
+ connection->inputChannel->getName().c_str());
+ return;
+ }
+ /**
+ * The "oldestEntry" is the entry that was first sent to the application. That entry, however,
+ * may not be the one that caused the timeout to occur. One possibility is that window timeout
+ * has changed. This could cause newer entries to time out before the already dispatched
+ * entries. In that situation, the newest entries caused ANR. But in all likelihood, the app
+ * processes the events linearly. So providing information about the oldest entry seems to be
+ * most useful.
+ */
+ DispatchEntry* oldestEntry = *connection->waitQueue.begin();
+ const nsecs_t currentWait = now() - oldestEntry->deliveryTime;
+ std::string reason =
+ android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",
+ connection->inputChannel->getName().c_str(),
+ ns2ms(currentWait),
+ oldestEntry->eventEntry->getDescription().c_str());
+ updateLastAnrStateLocked(getWindowHandleLocked(connection->inputChannel->getConnectionToken()),
+ reason);
+
+ std::unique_ptr<CommandEntry> commandEntry =
+ std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
+ commandEntry->inputApplicationHandle = nullptr;
+ commandEntry->inputChannel = connection->inputChannel;
+ commandEntry->reason = std::move(reason);
+ postCommandLocked(std::move(commandEntry));
+}
+
+void InputDispatcher::onAnrLocked(const sp<InputApplicationHandle>& application) {
+ std::string reason = android::base::StringPrintf("%s does not have a focused window",
+ application->getName().c_str());
+
+ updateLastAnrStateLocked(application, reason);
+
+ std::unique_ptr<CommandEntry> commandEntry =
+ std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
+ commandEntry->inputApplicationHandle = application;
+ commandEntry->inputChannel = nullptr;
+ commandEntry->reason = std::move(reason);
+ postCommandLocked(std::move(commandEntry));
+}
+
+void InputDispatcher::updateLastAnrStateLocked(const sp<InputWindowHandle>& window,
+ const std::string& reason) {
+ const std::string windowLabel = getApplicationWindowLabel(nullptr, window);
+ updateLastAnrStateLocked(windowLabel, reason);
+}
+
+void InputDispatcher::updateLastAnrStateLocked(const sp<InputApplicationHandle>& application,
+ const std::string& reason) {
+ const std::string windowLabel = getApplicationWindowLabel(application, nullptr);
+ updateLastAnrStateLocked(windowLabel, reason);
+}
+
+void InputDispatcher::updateLastAnrStateLocked(const std::string& windowLabel,
+ const std::string& reason) {
// Capture a record of the InputDispatcher state at the time of the ANR.
time_t t = time(nullptr);
struct tm tm;
@@ -4542,21 +4609,9 @@
mLastAnrState.clear();
mLastAnrState += INDENT "ANR:\n";
mLastAnrState += StringPrintf(INDENT2 "Time: %s\n", timestr);
- mLastAnrState +=
- StringPrintf(INDENT2 "Window: %s\n",
- getApplicationWindowLabel(applicationHandle, windowHandle).c_str());
- mLastAnrState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency);
- mLastAnrState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration);
- mLastAnrState += StringPrintf(INDENT2 "Reason: %s\n", reason);
+ mLastAnrState += StringPrintf(INDENT2 "Reason: %s\n", reason.c_str());
+ mLastAnrState += StringPrintf(INDENT2 "Window: %s\n", windowLabel.c_str());
dumpDispatchStateLocked(mLastAnrState);
-
- std::unique_ptr<CommandEntry> commandEntry =
- std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
- commandEntry->inputApplicationHandle = applicationHandle;
- commandEntry->inputChannel =
- windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr;
- commandEntry->reason = reason;
- postCommandLocked(std::move(commandEntry));
}
void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) {
@@ -4597,13 +4652,50 @@
mLock.lock();
- resumeAfterTargetsNotReadyTimeoutLocked(timeoutExtension, token);
+ if (timeoutExtension > 0) {
+ extendAnrTimeoutsLocked(commandEntry->inputApplicationHandle, token, timeoutExtension);
+ } else {
+ // stop waking up for events in this connection, it is already not responding
+ sp<Connection> connection = getConnectionLocked(token);
+ if (connection == nullptr) {
+ return;
+ }
+ cancelEventsForAnrLocked(connection);
+ }
+}
+
+void InputDispatcher::extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& application,
+ const sp<IBinder>& connectionToken,
+ nsecs_t timeoutExtension) {
+ sp<Connection> connection = getConnectionLocked(connectionToken);
+ if (connection == nullptr) {
+ if (mNoFocusedWindowTimeoutTime.has_value() && application != nullptr) {
+ // Maybe ANR happened because there's no focused window?
+ mNoFocusedWindowTimeoutTime = now() + timeoutExtension;
+ mAwaitedFocusedApplication = application;
+ } else {
+ // It's also possible that the connection already disappeared. No action necessary.
+ }
+ return;
+ }
+
+ ALOGI("Raised ANR, but the policy wants to keep waiting on %s for %" PRId64 "ms longer",
+ connection->inputChannel->getName().c_str(), ns2ms(timeoutExtension));
+
+ connection->responsive = true;
+ const nsecs_t newTimeout = now() + timeoutExtension;
+ for (DispatchEntry* entry : connection->waitQueue) {
+ if (newTimeout >= entry->timeoutTime) {
+ // Already removed old entries when connection was marked unresponsive
+ entry->timeoutTime = newTimeout;
+ mAnrTracker.insert(entry->timeoutTime, connectionToken);
+ }
+ }
}
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
CommandEntry* commandEntry) {
KeyEntry* entry = commandEntry->keyEntry;
-
KeyEvent event = createKeyEvent(*entry);
mLock.unlock();
@@ -4637,6 +4729,20 @@
mLock.lock();
}
+/**
+ * Connection is responsive if it has no events in the waitQueue that are older than the
+ * current time.
+ */
+static bool isConnectionResponsive(const Connection& connection) {
+ const nsecs_t currentTime = now();
+ for (const DispatchEntry* entry : connection.waitQueue) {
+ if (entry->timeoutTime < currentTime) {
+ return false;
+ }
+ }
+ return true;
+}
+
void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {
sp<Connection> connection = commandEntry->connection;
const nsecs_t finishTime = commandEntry->eventTime;
@@ -4649,7 +4755,6 @@
return;
}
DispatchEntry* dispatchEntry = *dispatchEntryIt;
-
const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
@@ -4678,6 +4783,11 @@
if (dispatchEntryIt != connection->waitQueue.end()) {
dispatchEntry = *dispatchEntryIt;
connection->waitQueue.erase(dispatchEntryIt);
+ mAnrTracker.erase(dispatchEntry->timeoutTime,
+ connection->inputChannel->getConnectionToken());
+ if (!connection->responsive) {
+ connection->responsive = isConnectionResponsive(*connection);
+ }
traceWaitQueueLength(connection);
if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
connection->outboundQueue.push_front(dispatchEntry);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index ff7be87..e679c6b 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -17,6 +17,7 @@
#ifndef _UI_INPUT_DISPATCHER_H
#define _UI_INPUT_DISPATCHER_H
+#include "AnrTracker.h"
#include "CancelationOptions.h"
#include "Entry.h"
#include "InjectionState.h"
@@ -191,6 +192,7 @@
EventEntry* mNextUnblockedEvent GUARDED_BY(mLock);
sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
+ TouchState* touchState,
bool addOutsideTargets = false,
bool addPortalWindows = false) REQUIRES(mLock);
@@ -214,7 +216,6 @@
std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
REQUIRES(mLock);
-
// Input channels that will receive a copy of all input events sent to the provided display.
std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock);
@@ -255,12 +256,14 @@
bool operator==(const KeyReplacement& rhs) const {
return keyCode == rhs.keyCode && deviceId == rhs.deviceId;
}
- bool operator<(const KeyReplacement& rhs) const {
- return keyCode != rhs.keyCode ? keyCode < rhs.keyCode : deviceId < rhs.deviceId;
+ };
+ struct KeyReplacementHash {
+ size_t operator()(const KeyReplacement& key) const {
+ return std::hash<int32_t>()(key.keyCode) ^ (std::hash<int32_t>()(key.deviceId) << 1);
}
};
// Maps the key code replaced, device id tuple to the key code it was replaced with
- KeyedVector<KeyReplacement, int32_t> mReplacedKeys GUARDED_BY(mLock);
+ std::unordered_map<KeyReplacement, int32_t, KeyReplacementHash> mReplacedKeys GUARDED_BY(mLock);
// Process certain Meta + Key combinations
void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, int32_t& keyCode,
int32_t& metaState);
@@ -270,6 +273,9 @@
bool runCommandsLockedInterruptible() REQUIRES(mLock);
void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock);
+ nsecs_t processAnrsLocked() REQUIRES(mLock);
+ nsecs_t getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock);
+
// Input filter processing.
bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock);
bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock);
@@ -308,8 +314,7 @@
std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay
GUARDED_BY(mLock);
- KeyedVector<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
- TouchState mTempTouchState GUARDED_BY(mLock);
+ std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
// Focused applications.
std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay
@@ -336,38 +341,53 @@
void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry);
- // Keeping track of ANR timeouts.
- enum InputTargetWaitCause {
- INPUT_TARGET_WAIT_CAUSE_NONE,
- INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY,
- INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY,
- };
-
- InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock);
- nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock);
- nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock);
- bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock);
- sp<IBinder> mInputTargetWaitApplicationToken GUARDED_BY(mLock);
+ /**
+ * This field is set if there is no focused window, and we have an event that requires
+ * a focused window to be dispatched (for example, a KeyEvent).
+ * When this happens, we will wait until *mNoFocusedWindowTimeoutTime before
+ * dropping the event and raising an ANR for that application.
+ * This is useful if an application is slow to add a focused window.
+ */
+ std::optional<nsecs_t> mNoFocusedWindowTimeoutTime GUARDED_BY(mLock);
bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock);
+ /**
+ * Time to stop waiting for the events to be processed while trying to dispatch a key.
+ * When this time expires, we just send the pending key event to the currently focused window,
+ * without waiting on other events to be processed first.
+ */
+ std::optional<nsecs_t> mKeyIsWaitingForEventsTimeout GUARDED_BY(mLock);
+ bool shouldWaitToSendKeyLocked(nsecs_t currentTime, const char* focusedWindowName)
+ REQUIRES(mLock);
+
+ /**
+ * The focused application at the time when no focused window was present.
+ * Used to raise an ANR when we have no focused window.
+ */
+ sp<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(mLock);
+
+ // Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
+ // AnrTracker must be kept in-sync with all responsive connection.waitQueues.
+ // If a connection is not responsive, then the entries should not be added to the AnrTracker.
+ // Once a connection becomes unresponsive, its entries are removed from AnrTracker to
+ // prevent unneeded wakeups.
+ AnrTracker mAnrTracker GUARDED_BY(mLock);
+ void extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& application,
+ const sp<IBinder>& connectionToken, nsecs_t timeoutExtension)
+ REQUIRES(mLock);
+
// Contains the last window which received a hover event.
sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
- // Finding targets for input events.
- int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry& entry,
- const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle,
- nsecs_t* nextWakeupTime, const char* reason)
- REQUIRES(mLock);
-
- void removeWindowByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
-
- void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
- const sp<IBinder>& inputConnectionToken)
- REQUIRES(mLock);
+ void cancelEventsForAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock);
- void resetAnrTimeoutsLocked() REQUIRES(mLock);
+ // If a focused application changes, we should stop counting down the "no focused window" time,
+ // because we will have no way of knowing when the previous application actually added a window.
+ // This also means that we will miss cases like pulling down notification shade when the
+ // focused application does not have a focused window (no ANR will be raised if notification
+ // shade is pulled down while we are counting down the timeout).
+ void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock);
int32_t getTargetDisplayId(const EventEntry& entry);
int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry,
@@ -378,11 +398,10 @@
nsecs_t* nextWakeupTime,
bool* outConflictingPointerActions) REQUIRES(mLock);
std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(
- int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows)
+ int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const
REQUIRES(mLock);
- void addGestureMonitors(const std::vector<Monitor>& monitors,
- std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
- float yOffset = 0);
+ std::vector<TouchedMonitor> selectResponsiveMonitorsLocked(
+ const std::vector<TouchedMonitor>& gestureMonitors) const REQUIRES(mLock);
void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
BitSet32 pointerIds, std::vector<InputTarget>& inputTargets)
@@ -401,11 +420,6 @@
std::string getApplicationWindowLabel(const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle);
- std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
- const sp<InputWindowHandle>& windowHandle,
- const EventEntry& eventEntry,
- const char* targetType) REQUIRES(mLock);
-
// Manage the dispatch cycle for a single connection.
// These methods are deliberately not Interruptible because doing all of the work
// with the mutex held makes it easier to ensure that connection invariants are maintained.
@@ -475,9 +489,14 @@
REQUIRES(mLock);
void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
- void onAnrLocked(nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime,
- nsecs_t waitStartTime, const char* reason) REQUIRES(mLock);
+ void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
+ void onAnrLocked(const sp<InputApplicationHandle>& application) REQUIRES(mLock);
+ void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
+ REQUIRES(mLock);
+ void updateLastAnrStateLocked(const sp<InputApplicationHandle>& application,
+ const std::string& reason) REQUIRES(mLock);
+ void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
+ REQUIRES(mLock);
// Outbound policy interactions.
void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry)
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 73d2272..a0d2f4f 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -27,6 +27,7 @@
"libinputflinger_defaults",
],
srcs: [
+ "AnrTracker_test.cpp",
"BlockingQueue_test.cpp",
"EventHub_test.cpp",
"TestInputListener.cpp",
diff --git a/services/inputflinger/tests/AnrTracker_test.cpp b/services/inputflinger/tests/AnrTracker_test.cpp
new file mode 100644
index 0000000..b561da1
--- /dev/null
+++ b/services/inputflinger/tests/AnrTracker_test.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../AnrTracker.h"
+
+#include <binder/Binder.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+namespace inputdispatcher {
+
+// --- AnrTrackerTest ---
+
+/**
+ * Add a single entry and ensure it's returned as first, even if the token isn't valid
+ */
+TEST(AnrTrackerTest, SingleEntry_First) {
+ AnrTracker tracker;
+
+ tracker.insert(1, nullptr);
+
+ ASSERT_EQ(1, tracker.firstTimeout());
+ ASSERT_EQ(tracker.firstToken(), nullptr);
+}
+
+TEST(AnrTrackerTest, MultipleEntries_RemoveToken) {
+ AnrTracker tracker;
+
+ sp<IBinder> token1 = new BBinder();
+ sp<IBinder> token2 = new BBinder();
+
+ tracker.insert(1, token1);
+ tracker.insert(2, token2);
+ tracker.insert(3, token1);
+ tracker.insert(4, token2);
+ tracker.insert(5, token1);
+
+ tracker.eraseToken(token1);
+
+ ASSERT_EQ(2, tracker.firstTimeout());
+}
+
+TEST(AnrTrackerTest, AddAndRemove_Empty) {
+ AnrTracker tracker;
+
+ ASSERT_TRUE(tracker.empty());
+
+ tracker.insert(1, nullptr);
+ ASSERT_FALSE(tracker.empty());
+
+ tracker.erase(1, nullptr);
+ ASSERT_TRUE(tracker.empty());
+}
+
+TEST(AnrTrackerTest, Clear) {
+ AnrTracker tracker;
+
+ tracker.insert(1, nullptr);
+ tracker.clear();
+ ASSERT_TRUE(tracker.empty());
+}
+
+TEST(AnrTrackerTest, SingleToken_MaintainsOrder) {
+ AnrTracker tracker;
+
+ ASSERT_TRUE(tracker.empty());
+
+ tracker.insert(2, nullptr);
+ tracker.insert(5, nullptr);
+ tracker.insert(0, nullptr);
+
+ ASSERT_EQ(0, tracker.firstTimeout());
+ ASSERT_EQ(nullptr, tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, MultipleTokens_MaintainsOrder) {
+ AnrTracker tracker;
+
+ sp<IBinder> token1 = new BBinder();
+ sp<IBinder> token2 = new BBinder();
+
+ tracker.insert(2, token1);
+ tracker.insert(5, token2);
+ tracker.insert(0, token2);
+
+ ASSERT_EQ(0, tracker.firstTimeout());
+ ASSERT_EQ(token2, tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, MultipleTokens_IdenticalTimes) {
+ AnrTracker tracker;
+
+ sp<IBinder> token1 = new BBinder();
+ sp<IBinder> token2 = new BBinder();
+
+ tracker.insert(2, token1);
+ tracker.insert(2, token2);
+ tracker.insert(10, token2);
+
+ ASSERT_EQ(2, tracker.firstTimeout());
+ // Doesn't matter which token is returned - both are valid results
+ ASSERT_TRUE(token1 == tracker.firstToken() || token2 == tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, MultipleTokens_IdenticalTimesRemove) {
+ AnrTracker tracker;
+
+ sp<IBinder> token1 = new BBinder();
+ sp<IBinder> token2 = new BBinder();
+
+ tracker.insert(2, token1);
+ tracker.insert(2, token2);
+ tracker.insert(10, token2);
+
+ tracker.erase(2, token2);
+
+ ASSERT_EQ(2, tracker.firstTimeout());
+ ASSERT_EQ(token1, tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, Empty_DoesntCrash) {
+ AnrTracker tracker;
+
+ ASSERT_TRUE(tracker.empty());
+
+ ASSERT_EQ(LONG_LONG_MAX, tracker.firstTimeout());
+ // Can't call firstToken() if tracker.empty()
+}
+
+TEST(AnrTrackerTest, RemoveInvalidItem_DoesntCrash) {
+ AnrTracker tracker;
+
+ tracker.insert(1, nullptr);
+
+ // Remove with non-matching timestamp
+ tracker.erase(2, nullptr);
+ ASSERT_EQ(1, tracker.firstTimeout());
+ ASSERT_EQ(nullptr, tracker.firstToken());
+
+ // Remove with non-matching token
+ tracker.erase(1, new BBinder());
+ ASSERT_EQ(1, tracker.firstTimeout());
+ ASSERT_EQ(nullptr, tracker.firstToken());
+
+ // Remove with both non-matching
+ tracker.erase(2, new BBinder());
+ ASSERT_EQ(1, tracker.firstTimeout());
+ ASSERT_EQ(nullptr, tracker.firstToken());
+}
+
+} // namespace inputdispatcher
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 13e8354..1a133dc 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -126,6 +126,14 @@
void assertNotifyAnrWasCalled(std::chrono::nanoseconds timeout,
const sp<InputApplicationHandle>& expectedApplication,
const sp<IBinder>& expectedToken) {
+ std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData;
+ ASSERT_NO_FATAL_FAILURE(anrData = getNotifyAnrData(timeout));
+ ASSERT_EQ(expectedApplication, anrData.first);
+ ASSERT_EQ(expectedToken, anrData.second);
+ }
+
+ std::pair<sp<InputApplicationHandle>, sp<IBinder>> getNotifyAnrData(
+ std::chrono::nanoseconds timeout) {
const std::chrono::time_point start = std::chrono::steady_clock::now();
std::unique_lock lock(mLock);
std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
@@ -136,16 +144,33 @@
// before checking if ANR was called.
// Since dispatcher is not guaranteed to call notifyAnr right away, we need to provide
// it some time to act. 100ms seems reasonable.
- mNotifyAnr.wait_for(lock, timeToWait,
- [this]() REQUIRES(mLock) { return mNotifyAnrWasCalled; });
+ mNotifyAnr.wait_for(lock, timeToWait, [this]() REQUIRES(mLock) {
+ return !mAnrApplications.empty() && !mAnrWindowTokens.empty();
+ });
const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
- ASSERT_TRUE(mNotifyAnrWasCalled);
+ if (mAnrApplications.empty() || mAnrWindowTokens.empty()) {
+ ADD_FAILURE() << "Did not receive ANR callback";
+ }
// Ensure that the ANR didn't get raised too early. We can't be too strict here because
// the dispatcher started counting before this function was called
- ASSERT_TRUE(timeout - 100ms < waited); // check (waited < timeout + 100ms) done by wait_for
- mNotifyAnrWasCalled = false;
- ASSERT_EQ(expectedApplication, mLastAnrApplication);
- ASSERT_EQ(expectedToken, mLastAnrWindowToken);
+ if (std::chrono::abs(timeout - waited) > 100ms) {
+ ADD_FAILURE() << "ANR was raised too early or too late. Expected "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count()
+ << "ms, but waited "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
+ << "ms instead";
+ }
+ std::pair<sp<InputApplicationHandle>, sp<IBinder>> result =
+ std::make_pair(mAnrApplications.front(), mAnrWindowTokens.front());
+ mAnrApplications.pop();
+ mAnrWindowTokens.pop();
+ return result;
+ }
+
+ void assertNotifyAnrWasNotCalled() {
+ std::scoped_lock lock(mLock);
+ ASSERT_TRUE(mAnrApplications.empty());
+ ASSERT_TRUE(mAnrWindowTokens.empty());
}
void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
@@ -153,6 +178,8 @@
mConfig.keyRepeatDelay = delay;
}
+ void setAnrTimeout(std::chrono::nanoseconds timeout) { mAnrTimeout = timeout; }
+
private:
std::mutex mLock;
std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -161,9 +188,8 @@
std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
// ANR handling
- bool mNotifyAnrWasCalled GUARDED_BY(mLock) = false;
- sp<InputApplicationHandle> mLastAnrApplication GUARDED_BY(mLock);
- sp<IBinder> mLastAnrWindowToken GUARDED_BY(mLock);
+ std::queue<sp<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
+ std::queue<sp<IBinder>> mAnrWindowTokens GUARDED_BY(mLock);
std::condition_variable mNotifyAnr;
std::chrono::nanoseconds mAnrTimeout = 0ms;
@@ -175,9 +201,8 @@
virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& application,
const sp<IBinder>& windowToken, const std::string&) override {
std::scoped_lock lock(mLock);
- mLastAnrApplication = application;
- mLastAnrWindowToken = windowToken;
- mNotifyAnrWasCalled = true;
+ mAnrApplications.push(application);
+ mAnrWindowTokens.push(windowToken);
mNotifyAnr.notify_all();
return mAnrTimeout.count();
}
@@ -643,7 +668,7 @@
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(expectedEventType, event->getType())
- << mName.c_str() << "expected " << inputEventTypeToString(expectedEventType)
+ << mName.c_str() << " expected " << inputEventTypeToString(expectedEventType)
<< " event, got " << inputEventTypeToString(event->getType()) << " event";
EXPECT_EQ(expectedDisplayId, event->getDisplayId());
@@ -688,9 +713,24 @@
void assertNoEvents() {
InputEvent* event = consume();
- ASSERT_EQ(nullptr, event)
- << mName.c_str()
- << ": should not have received any events, so consume() should return NULL";
+ if (event == nullptr) {
+ return;
+ }
+ if (event->getType() == AINPUT_EVENT_TYPE_KEY) {
+ KeyEvent& keyEvent = static_cast<KeyEvent&>(*event);
+ ADD_FAILURE() << "Received key event "
+ << KeyEvent::actionToString(keyEvent.getAction());
+ } else if (event->getType() == AINPUT_EVENT_TYPE_MOTION) {
+ MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
+ ADD_FAILURE() << "Received motion event "
+ << MotionEvent::actionToString(motionEvent.getAction());
+ } else if (event->getType() == AINPUT_EVENT_TYPE_FOCUS) {
+ FocusEvent& focusEvent = static_cast<FocusEvent&>(*event);
+ ADD_FAILURE() << "Received focus event, hasFocus = "
+ << (focusEvent.getHasFocus() ? "true" : "false");
+ }
+ FAIL() << mName.c_str()
+ << ": should not have received any events, so consume() should return NULL";
}
sp<IBinder> getToken() { return mConsumer->getChannel()->getConnectionToken(); }
@@ -754,6 +794,8 @@
mInfo.dispatchingTimeout = timeout.count();
}
+ void setPaused(bool paused) { mInfo.paused = paused; }
+
void setFrame(const Rect& frame) {
mInfo.frameLeft = frame.left;
mInfo.frameTop = frame.top;
@@ -775,6 +817,10 @@
expectedFlags);
}
+ void consumeKeyUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, expectedDisplayId, expectedFlags);
+ }
+
void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, expectedDisplayId,
@@ -826,12 +872,12 @@
expectedFlags);
}
- std::optional<uint32_t> receiveEvent() {
+ std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
if (mInputReceiver == nullptr) {
ADD_FAILURE() << "Invalid receive event on window with no receiver";
return std::nullopt;
}
- return mInputReceiver->receiveEvent();
+ return mInputReceiver->receiveEvent(outEvent);
}
void finishEvent(uint32_t sequenceNum) {
@@ -865,7 +911,9 @@
std::atomic<int32_t> FakeWindowHandle::sId{1};
static int32_t injectKey(const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
- int32_t displayId = ADISPLAY_ID_NONE) {
+ int32_t displayId = ADISPLAY_ID_NONE,
+ int32_t syncMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+ std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT) {
KeyEvent event;
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -875,10 +923,9 @@
repeatCount, currentTime, currentTime);
// Inject event until dispatch out.
- return dispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
- INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+ return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, syncMode,
+ injectionTimeout,
+ POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
}
static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
@@ -886,11 +933,19 @@
return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId);
}
+static int32_t injectKeyUp(const sp<InputDispatcher>& dispatcher,
+ int32_t displayId = ADISPLAY_ID_NONE) {
+ return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId);
+}
+
static int32_t injectMotionEvent(
const sp<InputDispatcher>& dispatcher, int32_t action, int32_t source, int32_t displayId,
const PointF& position,
const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION}) {
+ AMOTION_EVENT_INVALID_CURSOR_POSITION},
+ std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
+ int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+ nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC)) {
MotionEvent event;
PointerProperties pointerProperties[1];
PointerCoords pointerCoords[1];
@@ -903,7 +958,6 @@
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y);
- nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid motion down event.
event.initialize(InputEvent::nextId(), DEVICE_ID, source, displayId, INVALID_HMAC, action,
/* actionButton */ 0,
@@ -911,14 +965,13 @@
/* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
/* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0,
/* xPrecision */ 0, /* yPrecision */ 0, cursorPosition.x, cursorPosition.y,
- currentTime, currentTime,
+ eventTime, eventTime,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
// Inject event until dispatch out.
- return dispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
- INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+ return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode,
+ injectionTimeout,
+ POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
}
static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
@@ -1429,6 +1482,10 @@
expectedDisplayId, expectedFlags);
}
+ std::optional<int32_t> receiveEvent() { return mInputReceiver->receiveEvent(); }
+
+ void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }
+
void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN,
expectedDisplayId, expectedFlags);
@@ -1507,6 +1564,21 @@
monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
}
+TEST_F(InputDispatcherTest, UnresponsiveGestureMonitor_GetsAnr) {
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+ std::optional<uint32_t> consumeSeq = monitor.receiveEvent();
+ ASSERT_TRUE(consumeSeq);
+
+ mFakePolicy->assertNotifyAnrWasCalled(DISPATCHING_TIMEOUT, nullptr, monitor.getToken());
+ monitor.finishEvent(*consumeSeq);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
TEST_F(InputDispatcherTest, TestMoveEvent) {
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
sp<FakeWindowHandle> window =
@@ -2329,23 +2401,40 @@
}
};
+// Send a tap and respond, which should not cause an ANR.
+TEST_F(InputDispatcherSingleWindowAnr, WhenTouchIsConsumed_NoAnr) {
+ tapOnWindow();
+ mWindow->consumeMotionDown();
+ mWindow->consumeMotionUp();
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// Send a regular key and respond, which should not cause an ANR.
+TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher));
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
// Send an event to the app and have the app not respond right away.
-// Make sure that ANR is raised
+// When ANR is raised, policy will tell the dispatcher to cancel the events for that window.
+// So InputDispatcher will enqueue ACTION_CANCEL event as well.
TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) {
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
- // Also, overwhelm the socket to make sure ANR starts
- for (size_t i = 0; i < 100; i++) {
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {WINDOW_LOCATION.x, WINDOW_LOCATION.y + i});
- }
-
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+
+ // The remaining lines are not really needed for the test, but kept as a sanity check
+ mWindow->finishEvent(*sequenceNum);
+ mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
+ ADISPLAY_ID_DEFAULT, 0 /*flags*/);
ASSERT_TRUE(mDispatcher->waitForIdle());
}
@@ -2355,14 +2444,591 @@
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher));
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
ASSERT_TRUE(sequenceNum);
-
- // Start ANR process by sending a 2nd key, which would trigger the check for whether
- // waitQueue is empty
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 1);
-
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, mWindow->getToken());
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
ASSERT_TRUE(mDispatcher->waitForIdle());
}
+// We have a focused application, but no focused window
+TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) {
+ mWindow->setFocus(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mWindow->consumeFocusEvent(false);
+
+ // taps on the window work as normal
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ WINDOW_LOCATION));
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
+ mDispatcher->waitForIdle();
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+
+ // Once a focused event arrives, we get an ANR for this application
+ // We specify the injection timeout to be smaller than the application timeout, to ensure that
+ // injection times out (instead of failing).
+ const int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+ const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// We have a focused application, but no focused window
+// If the policy wants to keep waiting on the focused window to be added, make sure
+// that this timeout extension is honored and ANR is raised again.
+TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_ExtendsAnr) {
+ mWindow->setFocus(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mWindow->consumeFocusEvent(false);
+ const std::chrono::duration timeout = 5ms;
+ mFakePolicy->setAnrTimeout(timeout);
+
+ // Once a focused event arrives, we get an ANR for this application
+ // We specify the injection timeout to be smaller than the application timeout, to ensure that
+ // injection times out (instead of failing).
+ const int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+ const std::chrono::duration appTimeout =
+ mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(appTimeout, mApplication, nullptr /*windowToken*/);
+
+ // After the extended time has passed, ANR should be raised again
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+
+ // If we stop extending the timeout, dispatcher should go to idle.
+ // Another ANR may be raised during this time
+ mFakePolicy->setAnrTimeout(0ms);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// We have a focused application, but no focused window
+TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DropsFocusedEvents) {
+ mWindow->setFocus(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mWindow->consumeFocusEvent(false);
+
+ // Once a focused event arrives, we get an ANR for this application
+ const int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+
+ const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+
+ // Future focused events get dropped right away
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, injectKeyDown(mDispatcher));
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mWindow->assertNoEvents();
+}
+
+/**
+ * Ensure that the implementation is valid. Since we are using multiset to keep track of the
+ * ANR timeouts, we are allowing entries with identical timestamps in the same connection.
+ * If we process 1 of the events, but ANR on the second event with the same timestamp,
+ * the ANR mechanism should still work.
+ *
+ * In this test, we are injecting DOWN and UP events with the same timestamps, and acknowledging the
+ * DOWN event, while not responding on the second one.
+ */
+TEST_F(InputDispatcherSingleWindowAnr, Anr_HandlesEventsWithIdenticalTimestamps) {
+ nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
+ {AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION},
+ 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime);
+
+ // Now send ACTION_UP, with identical timestamp
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
+ {AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION},
+ 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime);
+
+ // We have now sent down and up. Let's consume first event and then ANR on the second.
+ mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+}
+
+// If an app is not responding to a key event, gesture monitors should continue to receive
+// new motion events
+TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnKey) {
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT));
+ mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher, ADISPLAY_ID_DEFAULT));
+
+ // Stuck on the ACTION_UP
+ const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken());
+
+ // New tap will go to the gesture monitor, but not to the window
+ tapOnWindow();
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion
+ mDispatcher->waitForIdle();
+ mWindow->assertNoEvents();
+ monitor.assertNoEvents();
+}
+
+// If an app is not responding to a motion event, gesture monitors should continue to receive
+// new motion events
+TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnMotion) {
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ tapOnWindow();
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ mWindow->consumeMotionDown();
+ // Stuck on the ACTION_UP
+ const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken());
+
+ // New tap will go to the gesture monitor, but not to the window
+ tapOnWindow();
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion
+ mDispatcher->waitForIdle();
+ mWindow->assertNoEvents();
+ monitor.assertNoEvents();
+}
+
+// If a window is unresponsive, then you get anr. if the window later catches up and starts to
+// process events, you don't get an anr. When the window later becomes unresponsive again, you
+// get an ANR again.
+// 1. tap -> block on ACTION_UP -> receive ANR
+// 2. consume all pending events (= queue becomes healthy again)
+// 3. tap again -> block on ACTION_UP again -> receive ANR second time
+TEST_F(InputDispatcherSingleWindowAnr, SameWindow_CanReceiveAnrTwice) {
+ tapOnWindow();
+
+ mWindow->consumeMotionDown();
+ // Block on ACTION_UP
+ const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mWindow->consumeMotionUp(); // Now the connection should be healthy again
+ mDispatcher->waitForIdle();
+ mWindow->assertNoEvents();
+
+ tapOnWindow();
+ mWindow->consumeMotionDown();
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mWindow->consumeMotionUp();
+
+ mDispatcher->waitForIdle();
+ mWindow->assertNoEvents();
+}
+
+// If the policy tells us to raise ANR again after some time, ensure that the timeout extension
+// is honored
+TEST_F(InputDispatcherSingleWindowAnr, Policy_CanExtendTimeout) {
+ const std::chrono::duration timeout = 5ms;
+ mFakePolicy->setAnrTimeout(timeout);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ WINDOW_LOCATION));
+
+ const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(windowTimeout, nullptr /*application*/,
+ mWindow->getToken());
+
+ // Since the policy wanted to extend ANR, make sure it is called again after the extension
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mFakePolicy->setAnrTimeout(0ms);
+ std::this_thread::sleep_for(windowTimeout);
+ // We are not checking if ANR has been called, because it may have been called again by the
+ // time we set the timeout to 0
+
+ // When the policy finally says stop, we should get ACTION_CANCEL
+ mWindow->consumeMotionDown();
+ mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
+ ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+ mWindow->assertNoEvents();
+}
+
+/**
+ * If a window is processing a motion event, and then a key event comes in, the key event should
+ * not to to the focused window until the motion is processed.
+ *
+ * Warning!!!
+ * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT
+ * and the injection timeout that we specify when injecting the key.
+ * We must have the injection timeout (10ms) be smaller than
+ * KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms).
+ *
+ * If that value changes, this test should also change.
+ */
+TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) {
+ mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+
+ tapOnWindow();
+ std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent();
+ ASSERT_TRUE(downSequenceNum);
+ std::optional<uint32_t> upSequenceNum = mWindow->receiveEvent();
+ ASSERT_TRUE(upSequenceNum);
+ // Don't finish the events yet, and send a key
+ // Injection will "succeed" because we will eventually give up and send the key to the focused
+ // window even if motions are still being processed. But because the injection timeout is short,
+ // we will receive INJECTION_TIMED_OUT as the result.
+
+ int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+ // Key will not be sent to the window, yet, because the window is still processing events
+ // and the key remains pending, waiting for the touch events to be processed
+ std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
+ ASSERT_FALSE(keySequenceNum);
+
+ std::this_thread::sleep_for(500ms);
+ // if we wait long enough though, dispatcher will give up, and still send the key
+ // to the focused window, even though we have not yet finished the motion event
+ mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ mWindow->finishEvent(*downSequenceNum);
+ mWindow->finishEvent(*upSequenceNum);
+}
+
+/**
+ * If a window is processing a motion event, and then a key event comes in, the key event should
+ * not go to the focused window until the motion is processed.
+ * If then a new motion comes in, then the pending key event should be going to the currently
+ * focused window right away.
+ */
+TEST_F(InputDispatcherSingleWindowAnr,
+ PendingKey_IsDroppedWhileMotionIsProcessedAndNewTouchComesIn) {
+ mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+
+ tapOnWindow();
+ std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent();
+ ASSERT_TRUE(downSequenceNum);
+ std::optional<uint32_t> upSequenceNum = mWindow->receiveEvent();
+ ASSERT_TRUE(upSequenceNum);
+ // Don't finish the events yet, and send a key
+ // Injection is async, so it will succeed
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
+ ADISPLAY_ID_DEFAULT, INPUT_EVENT_INJECTION_SYNC_NONE));
+ // At this point, key is still pending, and should not be sent to the application yet.
+ std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
+ ASSERT_FALSE(keySequenceNum);
+
+ // Now tap down again. It should cause the pending key to go to the focused window right away.
+ tapOnWindow();
+ mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); // it doesn't matter that we haven't ack'd
+ // the other events yet. We can finish events in any order.
+ mWindow->finishEvent(*downSequenceNum); // first tap's ACTION_DOWN
+ mWindow->finishEvent(*upSequenceNum); // first tap's ACTION_UP
+ mWindow->consumeMotionDown();
+ mWindow->consumeMotionUp();
+ mWindow->assertNoEvents();
+}
+
+class InputDispatcherMultiWindowAnr : public InputDispatcherTest {
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+
+ mApplication = new FakeApplicationHandle();
+ mApplication->setDispatchingTimeout(10ms);
+ mUnfocusedWindow =
+ new FakeWindowHandle(mApplication, mDispatcher, "Unfocused", ADISPLAY_ID_DEFAULT);
+ mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
+ // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
+ // window.
+ // Adding FLAG_WATCH_OUTSIDE_TOUCH to receive ACTION_OUTSIDE when another window is tapped
+ mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
+ InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH |
+ InputWindowInfo::FLAG_SPLIT_TOUCH);
+
+ mFocusedWindow =
+ new FakeWindowHandle(mApplication, mDispatcher, "Focused", ADISPLAY_ID_DEFAULT);
+ mFocusedWindow->setDispatchingTimeout(10ms);
+ mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
+ mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
+ InputWindowInfo::FLAG_SPLIT_TOUCH);
+
+ // Set focused application.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
+ mFocusedWindow->setFocus(true);
+
+ // Expect one focus window exist in display.
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+ mFocusedWindow->consumeFocusEvent(true);
+ }
+
+ virtual void TearDown() override {
+ InputDispatcherTest::TearDown();
+
+ mUnfocusedWindow.clear();
+ mFocusedWindow.clear();
+ }
+
+protected:
+ sp<FakeApplicationHandle> mApplication;
+ sp<FakeWindowHandle> mUnfocusedWindow;
+ sp<FakeWindowHandle> mFocusedWindow;
+ static constexpr PointF UNFOCUSED_WINDOW_LOCATION = {20, 20};
+ static constexpr PointF FOCUSED_WINDOW_LOCATION = {75, 75};
+ static constexpr PointF LOCATION_OUTSIDE_ALL_WINDOWS = {40, 40};
+
+ void tapOnFocusedWindow() { tap(FOCUSED_WINDOW_LOCATION); }
+
+ void tapOnUnfocusedWindow() { tap(UNFOCUSED_WINDOW_LOCATION); }
+
+private:
+ void tap(const PointF& location) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ location));
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ location));
+ }
+};
+
+// If we have 2 windows that are both unresponsive, the one with the shortest timeout
+// should be ANR'd first.
+TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ FOCUSED_WINDOW_LOCATION))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ mFocusedWindow->consumeMotionDown();
+ mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
+ ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+ // We consumed all events, so no ANR
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ FOCUSED_WINDOW_LOCATION));
+ std::optional<uint32_t> unfocusedSequenceNum = mUnfocusedWindow->receiveEvent();
+ ASSERT_TRUE(unfocusedSequenceNum);
+ std::optional<uint32_t> focusedSequenceNum = mFocusedWindow->receiveEvent();
+ ASSERT_TRUE(focusedSequenceNum);
+
+ const std::chrono::duration timeout =
+ mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
+ mFocusedWindow->getToken());
+
+ mFocusedWindow->finishEvent(*focusedSequenceNum);
+ mUnfocusedWindow->finishEvent(*unfocusedSequenceNum);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// If we have 2 windows with identical timeouts that are both unresponsive,
+// it doesn't matter which order they should have ANR.
+// But we should receive ANR for both.
+TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout) {
+ // Set the timeout for unfocused window to match the focused window
+ mUnfocusedWindow->setDispatchingTimeout(10ms);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+
+ tapOnFocusedWindow();
+ // we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window
+ std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData1 =
+ mFakePolicy->getNotifyAnrData(10ms);
+ std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData2 =
+ mFakePolicy->getNotifyAnrData(0ms);
+
+ // We don't know which window will ANR first. But both of them should happen eventually.
+ ASSERT_TRUE(mFocusedWindow->getToken() == anrData1.second ||
+ mFocusedWindow->getToken() == anrData2.second);
+ ASSERT_TRUE(mUnfocusedWindow->getToken() == anrData1.second ||
+ mUnfocusedWindow->getToken() == anrData2.second);
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// If a window is already not responding, the second tap on the same window should be ignored.
+// We should also log an error to account for the dropped event (not tested here).
+// At the same time, FLAG_WATCH_OUTSIDE_TOUCH targets should not receive any events.
+TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) {
+ tapOnFocusedWindow();
+ mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
+ ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+ // Receive the events, but don't respond
+ std::optional<uint32_t> downEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_DOWN
+ ASSERT_TRUE(downEventSequenceNum);
+ std::optional<uint32_t> upEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_UP
+ ASSERT_TRUE(upEventSequenceNum);
+ const std::chrono::duration timeout =
+ mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
+ mFocusedWindow->getToken());
+
+ // Tap once again
+ // We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ FOCUSED_WINDOW_LOCATION));
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ FOCUSED_WINDOW_LOCATION));
+ // Unfocused window does not receive ACTION_OUTSIDE because the tapped window is not a
+ // valid touch target
+ mUnfocusedWindow->assertNoEvents();
+
+ // Consume the first tap
+ mFocusedWindow->finishEvent(*downEventSequenceNum);
+ mFocusedWindow->finishEvent(*upEventSequenceNum);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ // The second tap did not go to the focused window
+ mFocusedWindow->assertNoEvents();
+ // should not have another ANR after the window just became healthy again
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// If you tap outside of all windows, there will not be ANR
+TEST_F(InputDispatcherMultiWindowAnr, TapOutsideAllWindows_DoesNotAnr) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ LOCATION_OUTSIDE_ALL_WINDOWS));
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// Since the focused window is paused, tapping on it should not produce any events
+TEST_F(InputDispatcherMultiWindowAnr, Window_CanBePaused) {
+ mFocusedWindow->setPaused(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ FOCUSED_WINDOW_LOCATION));
+
+ std::this_thread::sleep_for(mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT));
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ // Should not ANR because the window is paused, and touches shouldn't go to it
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+
+ mFocusedWindow->assertNoEvents();
+ mUnfocusedWindow->assertNoEvents();
+}
+
+/**
+ * If a window is processing a motion event, and then a key event comes in, the key event should
+ * not to to the focused window until the motion is processed.
+ * If a different window becomes focused at this time, the key should go to that window instead.
+ *
+ * Warning!!!
+ * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT
+ * and the injection timeout that we specify when injecting the key.
+ * We must have the injection timeout (10ms) be smaller than
+ * KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms).
+ *
+ * If that value changes, this test should also change.
+ */
+TEST_F(InputDispatcherMultiWindowAnr, PendingKey_GoesToNewlyFocusedWindow) {
+ // Set a long ANR timeout to prevent it from triggering
+ mFocusedWindow->setDispatchingTimeout(2s);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+
+ tapOnUnfocusedWindow();
+ std::optional<uint32_t> downSequenceNum = mUnfocusedWindow->receiveEvent();
+ ASSERT_TRUE(downSequenceNum);
+ std::optional<uint32_t> upSequenceNum = mUnfocusedWindow->receiveEvent();
+ ASSERT_TRUE(upSequenceNum);
+ // Don't finish the events yet, and send a key
+ // Injection will succeed because we will eventually give up and send the key to the focused
+ // window even if motions are still being processed.
+
+ int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result);
+ // Key will not be sent to the window, yet, because the window is still processing events
+ // and the key remains pending, waiting for the touch events to be processed
+ std::optional<uint32_t> keySequenceNum = mFocusedWindow->receiveEvent();
+ ASSERT_FALSE(keySequenceNum);
+
+ // Switch the focus to the "unfocused" window that we tapped. Expect the key to go there
+ mFocusedWindow->setFocus(false);
+ mUnfocusedWindow->setFocus(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+
+ // Focus events should precede the key events
+ mUnfocusedWindow->consumeFocusEvent(true);
+ mFocusedWindow->consumeFocusEvent(false);
+
+ // Finish the tap events, which should unblock dispatcher
+ mUnfocusedWindow->finishEvent(*downSequenceNum);
+ mUnfocusedWindow->finishEvent(*upSequenceNum);
+
+ // Now that all queues are cleared and no backlog in the connections, the key event
+ // can finally go to the newly focused "mUnfocusedWindow".
+ mUnfocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ mFocusedWindow->assertNoEvents();
+ mUnfocusedWindow->assertNoEvents();
+}
+
+// When the touch stream is split across 2 windows, and one of them does not respond,
+// then ANR should be raised and the touch should be canceled for the unresponsive window.
+// The other window should not be affected by that.
+TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) {
+ // Touch Window 1
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {FOCUSED_WINDOW_LOCATION});
+ mDispatcher->notifyMotion(&motionArgs);
+ mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
+ ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+
+ // Touch Window 2
+ int32_t actionPointerDown =
+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+ motionArgs =
+ generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {FOCUSED_WINDOW_LOCATION, UNFOCUSED_WINDOW_LOCATION});
+ mDispatcher->notifyMotion(&motionArgs);
+
+ const std::chrono::duration timeout =
+ mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
+ mFocusedWindow->getToken());
+
+ mUnfocusedWindow->consumeMotionDown();
+ mFocusedWindow->consumeMotionDown();
+ // Focused window may or may not receive ACTION_MOVE
+ // But it should definitely receive ACTION_CANCEL due to the ANR
+ InputEvent* event;
+ std::optional<int32_t> moveOrCancelSequenceNum = mFocusedWindow->receiveEvent(&event);
+ ASSERT_TRUE(moveOrCancelSequenceNum);
+ mFocusedWindow->finishEvent(*moveOrCancelSequenceNum);
+ ASSERT_NE(nullptr, event);
+ ASSERT_EQ(event->getType(), AINPUT_EVENT_TYPE_MOTION);
+ MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
+ if (motionEvent.getAction() == AMOTION_EVENT_ACTION_MOVE) {
+ mFocusedWindow->consumeMotionCancel();
+ } else {
+ ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionEvent.getAction());
+ }
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mUnfocusedWindow->assertNoEvents();
+ mFocusedWindow->assertNoEvents();
+}
+
} // namespace android::inputdispatcher
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index dbdffec..a3f1b52 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -1676,6 +1676,7 @@
if (found == mReturnData.end()) {
outClientTargetProperty->pixelFormat = PixelFormat::RGBA_8888;
outClientTargetProperty->dataspace = Dataspace::UNKNOWN;
+ return;
}
ReturnData& data = found->second;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 8ac0561..08559bd 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -894,6 +894,10 @@
mComposer.setLayerPerFrameMetadata(mDisplayId, mId, perFrameMetadatas));
if (validTypes & HdrMetadata::HDR10PLUS) {
+ if (CC_UNLIKELY(mHdrMetadata.hdr10plus.size() == 0)) {
+ return Error::BAD_PARAMETER;
+ }
+
std::vector<Hwc2::PerFrameMetadataBlob> perFrameMetadataBlobs;
perFrameMetadataBlobs.push_back(
{Hwc2::PerFrameMetadataKey::HDR10_PLUS_SEI, mHdrMetadata.hdr10plus});
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3255aac..13049ed 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -866,13 +866,12 @@
}
// If we still have pending updates, we need to ensure SurfaceFlinger
- // will keep calling doTransaction, and so we set the transaction flags.
+ // will keep calling doTransaction, and so we force a traversal.
// However, our pending states won't clear until a frame is available,
- // and so there is no need to specifically trigger a wakeup. Rather
- // we set the flags and wait for something else to wake us up.
+ // and so there is no need to specifically trigger a wakeup.
if (!mPendingStates.empty()) {
setTransactionFlags(eTransactionNeeded);
- mFlinger->setTransactionFlagsNoWake(eTraversalNeeded);
+ mFlinger->setTraversalNeeded();
}
mCurrentState.modified = false;
@@ -2408,7 +2407,15 @@
// Position the touchable region relative to frame screen location and restrict it to frame
// bounds.
info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop);
- info.visible = canReceiveInput();
+ // For compatibility reasons we let layers which can receive input
+ // receive input before they have actually submitted a buffer. Because
+ // of this we use canReceiveInput instead of isVisible to check the
+ // policy-visibility, ignoring the buffer state. However for layers with
+ // hasInputInfo()==false we can use the real visibility state.
+ // We are just using these layers for occlusion detection in
+ // InputDispatcher, and obviously if they aren't visible they can't occlude
+ // anything.
+ info.visible = hasInputInfo() ? canReceiveInput() : isVisible();
auto cropLayer = mDrawingState.touchableRegionCrop.promote();
if (info.replaceTouchableRegionWithCrop) {
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index abeacfe..a596bce 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -338,10 +338,14 @@
}
auto& callback = it->second;
- if (callback->wakeupTime()) {
+ auto const wakeupTime = callback->wakeupTime();
+ if (wakeupTime) {
callback->disarm();
- mIntendedWakeupTime = kInvalidTime;
- rearmTimer(mTimeKeeper->now());
+
+ if (*wakeupTime == mIntendedWakeupTime) {
+ mIntendedWakeupTime = kInvalidTime;
+ rearmTimer(mTimeKeeper->now());
+ }
return CancelResult::Cancelled;
}
return CancelResult::TooLate;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 82d8f08..ef315e2 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -258,7 +258,7 @@
const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
const String16 sDump("android.permission.DUMP");
-const char* KERNEL_IDLE_TIMER_PROP = "vendor.display.enable_kernel_idle_timer";
+const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
// ---------------------------------------------------------------------------
int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
@@ -1690,7 +1690,7 @@
if (const auto displayId = getInternalDisplayIdLocked()) {
sp<DisplayDevice> display = getDefaultDisplayDeviceLocked();
if (display && display->isPoweredOn()) {
- setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
+ getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState);
}
}
}
@@ -2017,8 +2017,9 @@
bool flushedATransaction = flushTransactionQueues();
- bool runHandleTransaction = transactionFlags &&
- ((transactionFlags != eTransactionFlushNeeded) || flushedATransaction);
+ bool runHandleTransaction =
+ (transactionFlags && (transactionFlags != eTransactionFlushNeeded)) ||
+ flushedATransaction;
if (runHandleTransaction) {
handleTransaction(eTransactionMask);
@@ -2764,7 +2765,8 @@
* (perform the transaction for each of them if needed)
*/
- if ((transactionFlags & eTraversalNeeded) || mTraversalNeededMainThread) {
+ if ((transactionFlags & eTraversalNeeded) || mForceTraversal) {
+ mForceTraversal = false;
mCurrentState.traverse([&](Layer* layer) {
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
if (!trFlags) return;
@@ -2777,7 +2779,6 @@
mInputInfoChanged = true;
}
});
- mTraversalNeededMainThread = false;
}
/*
@@ -3248,8 +3249,8 @@
return old;
}
-uint32_t SurfaceFlinger::setTransactionFlagsNoWake(uint32_t flags) {
- return mTransactionFlags.fetch_or(flags);
+void SurfaceFlinger::setTraversalNeeded() {
+ mForceTraversal = true;
}
bool SurfaceFlinger::flushTransactionQueues() {
@@ -3449,7 +3450,7 @@
// so we don't have to wake up again next frame to preform an uneeded traversal.
if (isMainThread && (transactionFlags & eTraversalNeeded)) {
transactionFlags = transactionFlags & (~eTraversalNeeded);
- mTraversalNeededMainThread = true;
+ mForceTraversal = true;
}
const auto transactionStart = [](uint32_t flags) {
@@ -4200,13 +4201,6 @@
static_cast<void>(schedule([this]() MAIN_THREAD { onInitializeDisplays(); }));
}
-void SurfaceFlinger::setVsyncEnabledInHWC(DisplayId displayId, hal::Vsync enabled) {
- if (mHWCVsyncState != enabled) {
- getHwComposer().setVsyncEnabled(displayId, enabled);
- mHWCVsyncState = enabled;
- }
-}
-
void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
if (display->isVirtual()) {
ALOGE("%s: Invalid operation on virtual display", __FUNCTION__);
@@ -4235,7 +4229,7 @@
}
getHwComposer().setPowerMode(*displayId, mode);
if (display->isPrimary() && mode != hal::PowerMode::DOZE_SUSPEND) {
- setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
+ getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState);
mScheduler->onScreenAcquired(mAppConnectionHandle);
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
}
@@ -4254,7 +4248,7 @@
}
// Make sure HWVsync is disabled before turning off the display
- setVsyncEnabledInHWC(*displayId, hal::Vsync::DISABLE);
+ getHwComposer().setVsyncEnabled(*displayId, hal::Vsync::DISABLE);
getHwComposer().setPowerMode(*displayId, mode);
mVisibleRegionsDirty = true;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e7bfdc7..38db62b 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -302,7 +302,6 @@
// main thread function to enable/disable h/w composer event
void setPrimaryVsyncEnabledInternal(bool enabled) REQUIRES(mStateLock);
- void setVsyncEnabledInHWC(DisplayId displayId, hal::Vsync enabled);
// called on the main thread by MessageQueue when an internal message
// is received
@@ -628,12 +627,12 @@
uint32_t peekTransactionFlags();
// Can only be called from the main thread or with mStateLock held
uint32_t setTransactionFlags(uint32_t flags);
- // Set the transaction flags, but don't trigger a wakeup! We use this cases where
- // there are still pending transactions but we know they won't be ready until a frame
+ // Indicate SF should call doTraversal on layers, but don't trigger a wakeup! We use this cases
+ // where there are still pending transactions but we know they won't be ready until a frame
// arrives from a different layer. So we need to ensure we performTransaction from invalidate
// but there is no need to try and wake up immediately to do it. Rather we rely on
- // onFrameAvailable to wake us up.
- uint32_t setTransactionFlagsNoWake(uint32_t flags);
+ // onFrameAvailable or another layer update to wake us up.
+ void setTraversalNeeded();
uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart);
void commitTransaction() REQUIRES(mStateLock);
void commitOffscreenLayers();
@@ -1001,7 +1000,7 @@
bool mTransactionPending = false;
bool mAnimTransactionPending = false;
SortedVector<sp<Layer>> mLayersPendingRemoval;
- bool mTraversalNeededMainThread = false;
+ bool mForceTraversal = false;
// global color transform states
Daltonizer mDaltonizer;
@@ -1209,6 +1208,7 @@
std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
std::atomic<nsecs_t> mExpectedPresentTime = 0;
+ hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
/* ------------------------------------------------------------------------
* Generic Layer Metadata
@@ -1279,10 +1279,6 @@
// be any issues with a raw pointer referencing an invalid object.
std::unordered_set<Layer*> mOffscreenLayers;
- // Flags to capture the state of Vsync in HWC
- hal::Vsync mHWCVsyncState = hal::Vsync::DISABLE;
- hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
-
// Fields tracking the current jank event: when it started and how many
// janky frames there are.
nsecs_t mMissedFrameJankStart = 0;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index c90b1b8..894ee6d 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -87,7 +87,9 @@
StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
const auto iter = deltas.find("present2present");
if (iter != deltas.end()) {
- StringAppendF(&result, "averageFPS = %.3f\n", 1000.0 / iter->second.averageTime());
+ const float averageTime = iter->second.averageTime();
+ const float averageFPS = averageTime < 1.0f ? 0.0f : 1000.0f / averageTime;
+ StringAppendF(&result, "averageFPS = %.3f\n", averageFPS);
}
for (const auto& ele : deltas) {
StringAppendF(&result, "%s histogram is as below:\n", ele.first.c_str());
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 7a1c7c6..63a34af 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -833,6 +833,15 @@
ASSERT_EQ(0, globalProto.stats_size());
}
+TEST_F(TimeStatsTest, noInfInAverageFPS) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 1000000);
+
+ const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ EXPECT_THAT(result, HasSubstr("averageFPS = 0.000"));
+}
+
namespace {
std::string buildExpectedHistogramBytestring(const std::vector<int32_t>& times,
const std::vector<int32_t>& frameCounts) {
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 793cb8b..d940dc5 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -701,6 +701,52 @@
EXPECT_THAT(cb.mCalls.size(), Eq(1));
}
+// b/154303580.
+TEST_F(VSyncDispatchTimerQueueTest, skipsRearmingWhenNotNextScheduled) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
+ CountingCallback cb1(mDispatch);
+ CountingCallback cb2(mDispatch);
+
+ EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled);
+
+ mMockClock.setLag(100);
+ mMockClock.advanceBy(620);
+
+ EXPECT_EQ(mDispatch.cancel(cb2), CancelResult::Cancelled);
+
+ mMockClock.advanceBy(80);
+
+ EXPECT_THAT(cb1.mCalls.size(), Eq(1));
+ EXPECT_THAT(cb2.mCalls.size(), Eq(0));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenCancelledAndIsNextScheduled) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 1280)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
+ CountingCallback cb1(mDispatch);
+ CountingCallback cb2(mDispatch);
+
+ EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled);
+
+ mMockClock.setLag(100);
+ mMockClock.advanceBy(620);
+
+ EXPECT_EQ(mDispatch.cancel(cb1), CancelResult::Cancelled);
+
+ EXPECT_THAT(cb1.mCalls.size(), Eq(0));
+ EXPECT_THAT(cb2.mCalls.size(), Eq(0));
+ mMockClock.advanceToNextCallback();
+
+ EXPECT_THAT(cb1.mCalls.size(), Eq(0));
+ EXPECT_THAT(cb2.mCalls.size(), Eq(1));
+}
+
class VSyncDispatchTimerQueueEntryTest : public testing::Test {
protected:
nsecs_t const mPeriod = 1000;
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index cf370fa..ef0719d 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -22,7 +22,7 @@
import xml.etree.ElementTree as element_tree
# Extensions unsupported on Android.
-_BLACKLISTED_EXTENSIONS = [
+_BLOCKED_EXTENSIONS = [
'VK_EXT_acquire_xlib_display',
'VK_EXT_direct_mode_display',
'VK_EXT_display_control',
@@ -192,7 +192,7 @@
if cmd not in extension_dict:
return True
else:
- if extension_dict[cmd] not in _BLACKLISTED_EXTENSIONS:
+ if extension_dict[cmd] not in _BLOCKED_EXTENSIONS:
return True
return False