summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/lshal/Lshal.cpp5
-rw-r--r--cmds/lshal/NullableOStream.h5
-rw-r--r--cmds/lshal/Timeout.h91
-rw-r--r--cmds/lshal/main.cpp3
-rw-r--r--cmds/lshal/test.cpp122
-rw-r--r--libs/renderengine/Android.bp1
-rw-r--r--libs/renderengine/benchmark/Android.bp2
-rw-r--r--libs/renderengine/skia/SkiaRenderEngine.cpp13
-rw-r--r--libs/renderengine/tests/Android.bp2
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.cpp118
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.h6
-rw-r--r--services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h19
-rw-r--r--services/inputflinger/tests/InputDispatcher_test.cpp144
-rw-r--r--services/surfaceflinger/Scheduler/VSyncDispatch.h2
-rw-r--r--services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp21
-rw-r--r--services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h8
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp6
-rw-r--r--services/surfaceflinger/common/FlagManager.cpp4
-rw-r--r--services/surfaceflinger/common/include/common/FlagManager.h2
-rw-r--r--services/surfaceflinger/surfaceflinger_flags.aconfig22
-rw-r--r--services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp3
-rw-r--r--services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp12
22 files changed, 428 insertions, 183 deletions
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
index 6115da75b2..5cdcb23d13 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -232,6 +232,11 @@ Status Lshal::main(const Arg &arg) {
return static_cast<HelpCommand*>(help)->usageOfCommand(mCommand);
}
+ // After Lshal::main() finishes, caller may call _exit(), causing debug
+ // information to prematurely ends. Hence flush().
+ err().flush();
+ out().flush();
+
return status;
}
diff --git a/cmds/lshal/NullableOStream.h b/cmds/lshal/NullableOStream.h
index 7cffcf8193..1576486c1d 100644
--- a/cmds/lshal/NullableOStream.h
+++ b/cmds/lshal/NullableOStream.h
@@ -59,6 +59,11 @@ public:
operator bool() const { // NOLINT(google-explicit-constructor)
return mOs != nullptr;
}
+ void flush() {
+ if (mOs) {
+ mOs->flush();
+ }
+ }
private:
template<typename>
friend class NullableOStream;
diff --git a/cmds/lshal/Timeout.h b/cmds/lshal/Timeout.h
index 37f41beea9..d97ba89122 100644
--- a/cmds/lshal/Timeout.h
+++ b/cmds/lshal/Timeout.h
@@ -16,83 +16,44 @@
#pragma once
-#include <condition_variable>
#include <chrono>
-#include <functional>
-#include <mutex>
-#include <thread>
+#include <future>
#include <hidl/Status.h>
+#include <utils/Errors.h>
namespace android {
namespace lshal {
-class BackgroundTaskState {
-public:
- explicit BackgroundTaskState(std::function<void(void)> &&func)
- : mFunc(std::forward<decltype(func)>(func)) {}
- void notify() {
- std::unique_lock<std::mutex> lock(mMutex);
- mFinished = true;
- lock.unlock();
- mCondVar.notify_all();
- }
- template<class C, class D>
- bool wait(std::chrono::time_point<C, D> end) {
- std::unique_lock<std::mutex> lock(mMutex);
- mCondVar.wait_until(lock, end, [this](){ return this->mFinished; });
- return mFinished;
- }
- void operator()() {
- mFunc();
- }
-private:
- std::mutex mMutex;
- std::condition_variable mCondVar;
- bool mFinished = false;
- std::function<void(void)> mFunc;
-};
-
-void *callAndNotify(void *data) {
- BackgroundTaskState &state = *static_cast<BackgroundTaskState *>(data);
- state();
- state.notify();
- return nullptr;
-}
-
-template<class R, class P>
-bool timeout(std::chrono::duration<R, P> delay, std::function<void(void)> &&func) {
- auto now = std::chrono::system_clock::now();
- BackgroundTaskState state{std::forward<decltype(func)>(func)};
- pthread_t thread;
- if (pthread_create(&thread, nullptr, callAndNotify, &state)) {
- std::cerr << "FATAL: could not create background thread." << std::endl;
- return false;
- }
- bool success = state.wait(now + delay);
- if (!success) {
- pthread_kill(thread, SIGINT);
- }
- pthread_join(thread, nullptr);
- return success;
-}
-
+// Call function on interfaceObject and wait for result until the given timeout has reached.
+// Callback functions pass to timeoutIPC() may be executed after the this function
+// has returned, especially if deadline has been reached. Hence, care must be taken when passing
+// data between the background thread and the main thread. See b/311143089.
template<class R, class P, class Function, class I, class... Args>
typename std::invoke_result<Function, I *, Args...>::type
timeoutIPC(std::chrono::duration<R, P> wait, const sp<I> &interfaceObject, Function &&func,
Args &&... args) {
using ::android::hardware::Status;
- typename std::result_of<Function(I *, Args...)>::type ret{Status::ok()};
- auto boundFunc = std::bind(std::forward<Function>(func),
- interfaceObject.get(), std::forward<Args>(args)...);
- bool success = timeout(wait, [&ret, &boundFunc] {
- ret = std::move(boundFunc());
- });
- if (!success) {
+
+ // Execute on a background thread but do not defer execution.
+ auto future =
+ std::async(std::launch::async, func, interfaceObject, std::forward<Args>(args)...);
+ auto status = future.wait_for(wait);
+ if (status == std::future_status::ready) {
+ return future.get();
+ }
+
+ // This future belongs to a background thread that we no longer care about.
+ // Putting this in the global list avoids std::future::~future() that may wait for the
+ // result to come back.
+ // This leaks memory, but lshal is a debugging tool, so this is fine.
+ static std::vector<decltype(future)> gDeadPool{};
+ gDeadPool.emplace_back(std::move(future));
+
+ if (status == std::future_status::timeout) {
return Status::fromStatusT(TIMED_OUT);
}
- return ret;
+ return Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE, "Illegal future_status");
}
-
-} // namespace lshal
-} // namespace android
+} // namespace lshal
+} // namespace android
diff --git a/cmds/lshal/main.cpp b/cmds/lshal/main.cpp
index 366c9383a2..bd5fa32521 100644
--- a/cmds/lshal/main.cpp
+++ b/cmds/lshal/main.cpp
@@ -18,5 +18,6 @@
int main(int argc, char **argv) {
using namespace ::android::lshal;
- return Lshal{}.main(Arg{argc, argv});
+ // Use _exit() to force terminate background threads in Timeout.h
+ _exit(Lshal{}.main(Arg{argc, argv}));
}
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index cba7c4bf2a..c24f827e73 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#include <chrono>
+#include <future>
+#include <mutex>
+#include "android/hidl/base/1.0/IBase.h"
#define LOG_TAG "Lshal"
#include <android-base/logging.h>
@@ -36,6 +40,8 @@
using namespace testing;
+using std::chrono_literals::operator""ms;
+
using ::android::hidl::base::V1_0::DebugInfo;
using ::android::hidl::base::V1_0::IBase;
using ::android::hidl::manager::V1_0::IServiceManager;
@@ -934,12 +940,9 @@ TEST_F(ListTest, DumpDebug) {
return hardware::Void();
}));
EXPECT_CALL(*serviceManager, get(_, _))
- .WillRepeatedly(
- Invoke([&](const hidl_string&, const hidl_string& instance) -> sp<IBase> {
- int id = getIdFromInstanceName(instance);
- if (id > inheritanceLevel) return nullptr;
- return sp<IBase>(service);
- }));
+ .WillRepeatedly(Invoke([&](const hidl_string&, const hidl_string&) -> sp<IBase> {
+ return sp<IBase>(service);
+ }));
const std::string expected = "[fake description 0]\n"
"Interface\n"
@@ -957,6 +960,110 @@ TEST_F(ListTest, DumpDebug) {
EXPECT_EQ("", err.str());
}
+// In SlowService, everything goes slooooooow. Each IPC call will wait for
+// the specified time before calling the callback function or returning.
+class SlowService : public IBase {
+public:
+ explicit SlowService(std::chrono::milliseconds wait) : mWait(wait) {}
+ android::hardware::Return<void> interfaceDescriptor(interfaceDescriptor_cb cb) override {
+ std::this_thread::sleep_for(mWait);
+ cb(getInterfaceName(1));
+ storeHistory("interfaceDescriptor");
+ return hardware::Void();
+ }
+ android::hardware::Return<void> interfaceChain(interfaceChain_cb cb) override {
+ std::this_thread::sleep_for(mWait);
+ std::vector<hidl_string> ret;
+ ret.push_back(getInterfaceName(1));
+ ret.push_back(IBase::descriptor);
+ cb(ret);
+ storeHistory("interfaceChain");
+ return hardware::Void();
+ }
+ android::hardware::Return<void> getHashChain(getHashChain_cb cb) override {
+ std::this_thread::sleep_for(mWait);
+ std::vector<hidl_hash> ret;
+ ret.push_back(getHashFromId(0));
+ ret.push_back(getHashFromId(0xff));
+ cb(ret);
+ storeHistory("getHashChain");
+ return hardware::Void();
+ }
+ android::hardware::Return<void> debug(const hidl_handle&,
+ const hidl_vec<hidl_string>&) override {
+ std::this_thread::sleep_for(mWait);
+ storeHistory("debug");
+ return Void();
+ }
+
+ template <class R, class P, class Pred>
+ bool waitForHistory(std::chrono::duration<R, P> wait, Pred predicate) {
+ std::unique_lock<std::mutex> lock(mLock);
+ return mCv.wait_for(lock, wait, [&]() { return predicate(mCallHistory); });
+ }
+
+private:
+ void storeHistory(std::string hist) {
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ mCallHistory.emplace_back(std::move(hist));
+ }
+ mCv.notify_all();
+ }
+
+ const std::chrono::milliseconds mWait;
+ std::mutex mLock;
+ std::condition_variable mCv;
+ // List of functions that have finished being called on this interface.
+ std::vector<std::string> mCallHistory;
+};
+
+class TimeoutTest : public ListTest {
+public:
+ void setMockServiceManager(sp<IBase> service) {
+ EXPECT_CALL(*serviceManager, list(_))
+ .WillRepeatedly(Invoke([&](IServiceManager::list_cb cb) {
+ std::vector<hidl_string> ret;
+ ret.push_back(getInterfaceName(1) + "/default");
+ cb(ret);
+ return hardware::Void();
+ }));
+ EXPECT_CALL(*serviceManager, get(_, _))
+ .WillRepeatedly(Invoke([&](const hidl_string&, const hidl_string&) -> sp<IBase> {
+ return service;
+ }));
+ }
+};
+
+TEST_F(TimeoutTest, BackgroundThreadIsKept) {
+ auto lshalIpcTimeout = 100ms;
+ auto serviceIpcTimeout = 200ms;
+ lshal->setWaitTimeForTest(lshalIpcTimeout, lshalIpcTimeout);
+ sp<SlowService> service = new SlowService(serviceIpcTimeout);
+ setMockServiceManager(service);
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_NE(0u, mockList->main(createArg({"lshal", "--types=b", "-i", "--neat"})));
+ EXPECT_THAT(err.str(), HasSubstr("Skipping \"a.h.foo1@1.0::IFoo/default\""));
+ EXPECT_TRUE(service->waitForHistory(serviceIpcTimeout * 5, [](const auto& hist) {
+ return hist.size() == 1 && hist[0] == "interfaceChain";
+ })) << "The background thread should continue after the main thread moves on, but it is killed";
+}
+
+TEST_F(TimeoutTest, BackgroundThreadDoesNotBlockMainThread) {
+ auto lshalIpcTimeout = 100ms;
+ auto serviceIpcTimeout = 2000ms;
+ auto start = std::chrono::system_clock::now();
+ lshal->setWaitTimeForTest(lshalIpcTimeout, lshalIpcTimeout);
+ sp<SlowService> service = new SlowService(serviceIpcTimeout);
+ setMockServiceManager(service);
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_NE(0u, mockList->main(createArg({"lshal", "--types=b", "-i", "--neat"})));
+ EXPECT_LE(std::chrono::system_clock::now(), start + 5 * lshalIpcTimeout)
+ << "The main thread should not be blocked by the background task";
+}
+
class ListVintfTest : public ListTest {
public:
virtual void SetUp() override {
@@ -1079,5 +1186,6 @@ TEST_F(HelpTest, UnknownOptionHelp2) {
int main(int argc, char **argv) {
::testing::InitGoogleMock(&argc, argv);
- return RUN_ALL_TESTS();
+ // Use _exit() to force terminate background threads in Timeout.h
+ _exit(RUN_ALL_TESTS());
}
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 9494ee5b25..fd45840cf8 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -48,6 +48,7 @@ cc_defaults {
static_libs: [
"libshaders",
"libtonemap",
+ "libsurfaceflinger_common",
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
index 55c34cd059..87e21c2d70 100644
--- a/libs/renderengine/benchmark/Android.bp
+++ b/libs/renderengine/benchmark/Android.bp
@@ -37,6 +37,7 @@ cc_benchmark {
static_libs: [
"librenderengine",
"libshaders",
+ "libsurfaceflinger_common",
"libtonemap",
],
cflags: [
@@ -54,6 +55,7 @@ cc_benchmark {
"libsync",
"libui",
"libutils",
+ "server_configurable_flags",
],
data: ["resources/*"],
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 330cc6650a..6e393f03fa 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -53,6 +53,7 @@
#include <SkSurface.h>
#include <SkTileMode.h>
#include <android-base/stringprintf.h>
+#include <common/FlagManager.h>
#include <gui/FenceMonitor.h>
#include <gui/TraceUtils.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
@@ -419,6 +420,9 @@ void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
mGraphicBufferExternalRefs[buffer->getId()]++;
if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) {
+ if (FlagManager::getInstance().renderable_buffer_usage()) {
+ isRenderable = buffer->getUsage() & GRALLOC_USAGE_HW_RENDER;
+ }
std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
std::make_shared<AutoBackendTexture::LocalRef>(grContext,
buffer->toAHardwareBuffer(),
@@ -760,10 +764,11 @@ void SkiaRenderEngine::drawLayersInternal(
// save a snapshot of the activeSurface to use as input to the blur shaders
blurInput = activeSurface->makeImageSnapshot();
- // blit the offscreen framebuffer into the destination AHB, but only
- // if there are blur regions. backgroundBlurRadius blurs the entire
- // image below, so it can skip this step.
- if (layer.blurRegions.size()) {
+ // blit the offscreen framebuffer into the destination AHB. This ensures that
+ // even if the blurred image does not cover the screen (for example, during
+ // a rotation animation, or if blur regions are used), the entire screen is
+ // initialized.
+ if (layer.blurRegions.size() || FlagManager::getInstance().restore_blur_step()) {
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index 50e166d2a7..473e1d46b8 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -44,6 +44,7 @@ cc_test {
"librenderengine_mocks",
"libshaders",
"libtonemap",
+ "libsurfaceflinger_common",
],
header_libs: [
"libtonemap_headers",
@@ -61,5 +62,6 @@ cc_test {
"libsync",
"libui",
"libutils",
+ "server_configurable_flags",
],
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index af72eb9bcb..229d699803 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -121,11 +121,6 @@ constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2
// 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;
@@ -1192,7 +1187,7 @@ bool InputDispatcher::isStaleEvent(nsecs_t currentTime, const EventEntry& entry)
* Return true if the events preceding this incoming motion event should be dropped
* Return false otherwise (the default behaviour)
*/
-bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) {
+bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) const {
const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN &&
isFromSource(motionEntry.source, AINPUT_SOURCE_CLASS_POINTER);
@@ -1234,16 +1229,6 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt
}
}
- // 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;
}
@@ -1291,6 +1276,20 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE
mNextUnblockedEvent = mInboundQueue.back();
needWake = true;
}
+
+ const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN &&
+ isFromSource(motionEntry.source, AINPUT_SOURCE_CLASS_POINTER);
+ if (isPointerDownEvent && mKeyIsWaitingForEventsTimeout) {
+ // Prevent waiting too long for unprocessed events: if we have a pending key event,
+ // and some other events have not yet been processed, the dispatcher will wait for
+ // these events to be processed before dispatching the key event. This is because
+ // the unprocessed events may cause the focus to change (for example, by launching a
+ // new window or tapping a different window). To prevent waiting too long, we force
+ // the key to be sent to the currently focused window when a new tap comes in.
+ ALOGD("Received a new pointer down event, stop waiting for events to process and "
+ "just send the pending key event to the currently focused window.");
+ mKeyIsWaitingForEventsTimeout = now();
+ }
break;
}
case EventEntry::Type::FOCUS: {
@@ -2137,7 +2136,8 @@ bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime,
// Start the timer
// Wait to send key because there are unprocessed events that may cause focus to change
mKeyIsWaitingForEventsTimeout = currentTime +
- std::chrono::duration_cast<std::chrono::nanoseconds>(KEY_WAITING_FOR_EVENTS_TIMEOUT)
+ std::chrono::duration_cast<std::chrono::nanoseconds>(
+ mPolicy.getKeyWaitingForEventsTimeout())
.count();
return true;
}
@@ -4879,7 +4879,7 @@ std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const Inpu
break;
}
default: {
- ALOGE("Cannot verify events of type %" PRId32, event.getType());
+ LOG(ERROR) << "Cannot verify events of type " << ftl::enum_string(event.getType());
return nullptr;
}
}
@@ -5195,6 +5195,7 @@ void InputDispatcher::setInputWindowsLocked(
// Copy old handles for release if they are no longer present.
const std::vector<sp<WindowInfoHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
+ const sp<WindowInfoHandle> removedFocusedWindowHandle = getFocusedWindowHandleLocked(displayId);
updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);
@@ -5203,7 +5204,7 @@ void InputDispatcher::setInputWindowsLocked(
std::optional<FocusResolver::FocusChanges> changes =
mFocusResolver.setInputWindows(displayId, windowHandles);
if (changes) {
- onFocusChangedLocked(*changes);
+ onFocusChangedLocked(*changes, removedFocusedWindowHandle);
}
std::unordered_map<int32_t, TouchState>::iterator stateIt =
@@ -5325,14 +5326,16 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) {
sp<IBinder> oldFocusedWindowToken =
mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
if (oldFocusedWindowToken != nullptr) {
- std::shared_ptr<Connection> connection = getConnectionLocked(oldFocusedWindowToken);
- if (connection != nullptr) {
- CancelationOptions
- options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
- "The display which contains this window no longer has focus.");
- options.displayId = ADISPLAY_ID_NONE;
- synthesizeCancelationEventsForConnectionLocked(connection, options);
+ const auto windowHandle =
+ getWindowHandleLocked(oldFocusedWindowToken, mFocusedDisplayId);
+ if (windowHandle == nullptr) {
+ LOG(FATAL) << __func__ << ": Previously focused token did not have a window";
}
+ CancelationOptions
+ options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
+ "The display which contains this window no longer has focus.");
+ options.displayId = ADISPLAY_ID_NONE;
+ synthesizeCancelationEventsForWindowLocked(windowHandle, options);
}
mFocusedDisplayId = displayId;
@@ -6207,8 +6210,18 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime,
}
traceWaitQueueLength(*connection);
if (fallbackKeyEntry && connection->status == Connection::Status::NORMAL) {
- const InputTarget target{.connection = connection, .flags = dispatchEntry->targetFlags};
- enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry), target);
+ const auto windowHandle = getWindowHandleLocked(connection->getToken());
+ // Only dispatch fallbacks if there is a window for the connection.
+ if (windowHandle != nullptr) {
+ const auto inputTarget =
+ createInputTargetLocked(windowHandle, InputTarget::DispatchMode::AS_IS,
+ dispatchEntry->targetFlags,
+ fallbackKeyEntry->downTime);
+ if (inputTarget.has_value()) {
+ enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry),
+ *inputTarget);
+ }
+ }
}
releaseDispatchEntry(std::move(dispatchEntry));
}
@@ -6440,14 +6453,18 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl
mLock.lock();
- // Cancel the fallback key.
+ // Cancel the fallback key, but only if we still have a window for the channel.
+ // It could have been removed during the policy call.
if (*fallbackKeyCode != AKEYCODE_UNKNOWN) {
- CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
- "application handled the original non-fallback key "
- "or is no longer a foreground target, "
- "canceling previously dispatched fallback key");
- options.keyCode = *fallbackKeyCode;
- synthesizeCancelationEventsForConnectionLocked(connection, options);
+ const auto windowHandle = getWindowHandleLocked(connection->getToken());
+ if (windowHandle != nullptr) {
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
+ "application handled the original non-fallback key "
+ "or is no longer a foreground target, "
+ "canceling previously dispatched fallback key");
+ options.keyCode = *fallbackKeyCode;
+ synthesizeCancelationEventsForConnectionLocked(connection, options);
+ }
}
connection->inputState.removeFallbackKey(originalKeyCode);
}
@@ -6523,10 +6540,13 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl
}
}
- CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
- "canceling fallback, policy no longer desires it");
- options.keyCode = *fallbackKeyCode;
- synthesizeCancelationEventsForConnectionLocked(connection, options);
+ const auto windowHandle = getWindowHandleLocked(connection->getToken());
+ if (windowHandle != nullptr) {
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
+ "canceling fallback, policy no longer desires it");
+ options.keyCode = *fallbackKeyCode;
+ synthesizeCancelationEventsForConnectionLocked(connection, options);
+ }
fallback = false;
*fallbackKeyCode = AKEYCODE_UNKNOWN;
@@ -6665,15 +6685,19 @@ void InputDispatcher::setFocusedWindow(const FocusRequest& request) {
mLooper->wake();
}
-void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& changes) {
+void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& changes,
+ const sp<WindowInfoHandle> removedFocusedWindowHandle) {
if (changes.oldFocus) {
- std::shared_ptr<Connection> focusedConnection = getConnectionLocked(changes.oldFocus);
- if (focusedConnection) {
- CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
- "focus left window");
- synthesizeCancelationEventsForConnectionLocked(focusedConnection, options);
- enqueueFocusEventLocked(changes.oldFocus, /*hasFocus=*/false, changes.reason);
- }
+ const auto resolvedWindow = removedFocusedWindowHandle != nullptr
+ ? removedFocusedWindowHandle
+ : getWindowHandleLocked(changes.oldFocus, changes.displayId);
+ if (resolvedWindow == nullptr) {
+ LOG(FATAL) << __func__ << ": Previously focused token did not have a window";
+ }
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
+ "focus left window");
+ synthesizeCancelationEventsForWindowLocked(resolvedWindow, options);
+ enqueueFocusEventLocked(changes.oldFocus, /*hasFocus=*/false, changes.reason);
}
if (changes.newFocus) {
resetNoFocusedWindowTimeoutLocked();
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 155d485ae4..90a5250fc2 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -470,7 +470,7 @@ private:
bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry);
- bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock);
+ bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) const REQUIRES(mLock);
/**
* Time to stop waiting for the events to be processed while trying to dispatch a key.
@@ -653,7 +653,9 @@ private:
bool handled, nsecs_t consumeTime) REQUIRES(mLock);
void doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
const KeyEntry& entry) REQUIRES(mLock);
- void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock);
+ void onFocusChangedLocked(const FocusResolver::FocusChanges& changes,
+ const sp<gui::WindowInfoHandle> removedFocusedWindowHandle = nullptr)
+ REQUIRES(mLock);
void sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, const sp<IBinder>& newToken)
REQUIRES(mLock);
void sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 9e6209b3d9..62c2b02967 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -131,6 +131,18 @@ public:
return std::chrono::nanoseconds(currentTime - eventTime) >= STALE_EVENT_TIMEOUT;
}
+ /**
+ * Get the additional latency to add while waiting for other input events to process before
+ * dispatching the pending key.
+ * If there are unprocessed events, the pending key will not be dispatched immediately. Instead,
+ * the dispatcher will wait for this timeout, to account for the possibility that the focus
+ * might change due to touch or other events (such as another app getting launched by keys).
+ * This would give the pending key the opportunity to go to a newly focused window instead.
+ */
+ virtual std::chrono::nanoseconds getKeyWaitingForEventsTimeout() {
+ return KEY_WAITING_FOR_EVENTS_TIMEOUT;
+ }
+
/* Notifies the policy that a pointer down event has occurred outside the current focused
* window.
*
@@ -150,6 +162,13 @@ public:
/* Notifies the policy that there was an input device interaction with apps. */
virtual void notifyDeviceInteraction(DeviceId deviceId, nsecs_t timestamp,
const std::set<gui::Uid>& uids) = 0;
+
+private:
+ // 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.
+ static constexpr std::chrono::nanoseconds KEY_WAITING_FOR_EVENTS_TIMEOUT =
+ std::chrono::milliseconds(500);
};
} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 163eab078e..8dd98e12a8 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -363,6 +363,8 @@ public:
mInterceptKeyTimeout = timeout;
}
+ std::chrono::nanoseconds getKeyWaitingForEventsTimeout() override { return 500ms; }
+
void setStaleEventTimeout(std::chrono::nanoseconds timeout) { mStaleEventTimeout = timeout; }
void assertUserActivityNotPoked() {
@@ -1083,8 +1085,8 @@ public:
EXPECT_EQ(inTouchMode, touchModeEvent.isInTouchMode());
}
- void assertNoEvents() {
- std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_NO_EVENT_EXPECTED);
+ void assertNoEvents(std::chrono::milliseconds timeout) {
+ std::unique_ptr<InputEvent> event = consume(timeout);
if (event == nullptr) {
return;
}
@@ -1412,14 +1414,14 @@ public:
mInputReceiver->sendTimeline(inputEventId, timeline);
}
- void assertNoEvents() {
+ void assertNoEvents(std::chrono::milliseconds timeout = CONSUME_TIMEOUT_NO_EVENT_EXPECTED) {
if (mInputReceiver == nullptr &&
mInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) {
return; // Can't receive events if the window does not have input channel
}
ASSERT_NE(nullptr, mInputReceiver)
<< "Window without InputReceiver must specify feature NO_INPUT_CHANNEL";
- mInputReceiver->assertNoEvents();
+ mInputReceiver->assertNoEvents(timeout);
}
sp<IBinder> getToken() { return mInfo.token; }
@@ -1437,13 +1439,6 @@ public:
int getChannelFd() { return mInputReceiver->getChannelFd(); }
-private:
- FakeWindowHandle(std::string name) : mName(name){};
- const std::string mName;
- std::shared_ptr<FakeInputReceiver> mInputReceiver;
- static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger
- friend class sp<FakeWindowHandle>;
-
std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout, bool handled = true) {
if (mInputReceiver == nullptr) {
LOG(FATAL) << "Cannot consume event from a window with no input event receiver";
@@ -1454,6 +1449,13 @@ private:
}
return event;
}
+
+private:
+ FakeWindowHandle(std::string name) : mName(name){};
+ const std::string mName;
+ std::shared_ptr<FakeInputReceiver> mInputReceiver;
+ static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger
+ friend class sp<FakeWindowHandle>;
};
std::atomic<int32_t> FakeWindowHandle::sId{1};
@@ -1512,7 +1514,7 @@ public:
std::unique_ptr<MotionEvent> consumeMotion() { return mInputReceiver.consumeMotion(); }
- void assertNoEvents() { mInputReceiver.assertNoEvents(); }
+ void assertNoEvents() { mInputReceiver.assertNoEvents(CONSUME_TIMEOUT_NO_EVENT_EXPECTED); }
private:
FakeInputReceiver mInputReceiver;
@@ -7261,7 +7263,7 @@ TEST_F(InputDispatcherFallbackKeyTest, CanceledKeyCancelsFallback) {
mWindow->assertNoEvents();
}
-TEST_F(InputDispatcherFallbackKeyTest, WindowRemovedDuringPolicyCall) {
+TEST_F(InputDispatcherFallbackKeyTest, InputChannelRemovedDuringPolicyCall) {
setFallback(AKEYCODE_B);
mDispatcher->notifyKey(
KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
@@ -7294,6 +7296,71 @@ TEST_F(InputDispatcherFallbackKeyTest, WindowRemovedDuringPolicyCall) {
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
}
+TEST_F(InputDispatcherFallbackKeyTest, WindowRemovedDuringPolicyCall) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ mFakePolicy->setUnhandledKeyHandler([&](const KeyEvent& event) {
+ // When the unhandled key is reported to the policy next, remove the window.
+ mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
+ return KeyEventBuilder(event).keyCode(AKEYCODE_B).build();
+ });
+ // Release the original key, which the app will not handle. When this unhandled key is reported
+ // to the policy, the window will be removed.
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+
+ // Since the window was removed, it loses focus, and the channel state will be reset.
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED)));
+ mWindow->consumeFocusEvent(false);
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, WindowRemovedWhileAwaitingFinishedSignal) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ const auto [seq, event] = mWindow->receiveEvent();
+ ASSERT_TRUE(seq.has_value() && event != nullptr) << "Failed to receive fallback event";
+ ASSERT_EQ(event->getType(), InputEventType::KEY);
+ ASSERT_THAT(static_cast<const KeyEvent&>(*event),
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ // Remove the window now, which should generate a cancellations and make the window lose focus.
+ mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A),
+ WithFlags(AKEY_EVENT_FLAG_CANCELED)));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED)));
+ mWindow->consumeFocusEvent(false);
+
+ // Finish the event by reporting it as handled.
+ mWindow->finishEvent(*seq);
+ mWindow->assertNoEvents();
+}
+
class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
protected:
static constexpr std::chrono::nanoseconds KEY_REPEAT_TIMEOUT = 40ms;
@@ -8824,16 +8891,11 @@ TEST_F(InputDispatcherSingleWindowAnr, Policy_DoesNotGetDuplicateAnr) {
/**
* If a window is processing a motion event, and then a key event comes in, the key event should
* not get delivered 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 (100ms) be smaller than
- * KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms).
- *
- * If that value changes, this test should also change.
*/
TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) {
+ // The timeouts in this test are established by relying on the fact that the "key waiting for
+ // events timeout" is equal to 500ms.
+ ASSERT_EQ(mFakePolicy->getKeyWaitingForEventsTimeout(), 500ms);
mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
@@ -8842,23 +8904,18 @@ TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) {
ASSERT_TRUE(downSequenceNum);
const auto& [upSequenceNum, upEvent] = 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.
- InputEventInjectionResult result =
- injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 100ms);
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
+ // Don't finish the events yet, and send a key
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD)
+ .policyFlags(DEFAULT_POLICY_FLAGS | POLICY_FLAG_DISABLE_KEY_REPEAT)
+ .build());
// 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
// Make sure that `assertNoEvents` doesn't wait too long, because it could cause an ANR.
- // Rely here on the fact that it uses CONSUME_TIMEOUT_NO_EVENT_EXPECTED under the hood.
- static_assert(CONSUME_TIMEOUT_NO_EVENT_EXPECTED < 100ms);
- mWindow->assertNoEvents();
+ mWindow->assertNoEvents(100ms);
- std::this_thread::sleep_for(500ms);
+ std::this_thread::sleep_for(400ms);
// 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);
@@ -8873,7 +8930,10 @@ TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) {
* focused window right away.
*/
TEST_F(InputDispatcherSingleWindowAnr,
- PendingKey_IsDroppedWhileMotionIsProcessedAndNewTouchComesIn) {
+ PendingKey_IsDeliveredWhileMotionIsProcessingAndNewTouchComesIn) {
+ // The timeouts in this test are established by relying on the fact that the "key waiting for
+ // events timeout" is equal to 500ms.
+ ASSERT_EQ(mFakePolicy->getKeyWaitingForEventsTimeout(), 500ms);
mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
@@ -8888,15 +8948,19 @@ TEST_F(InputDispatcherSingleWindowAnr,
.policyFlags(DEFAULT_POLICY_FLAGS | POLICY_FLAG_DISABLE_KEY_REPEAT)
.build());
// At this point, key is still pending, and should not be sent to the application yet.
- // Make sure the `assertNoEvents` check doesn't take too long. It uses
- // CONSUME_TIMEOUT_NO_EVENT_EXPECTED under the hood.
- static_assert(CONSUME_TIMEOUT_NO_EVENT_EXPECTED < 100ms);
- mWindow->assertNoEvents();
+ mWindow->assertNoEvents(100ms);
// Now tap down again. It should cause the pending key to go to the focused window right away.
tapOnWindow();
- mWindow->consumeKeyEvent(WithKeyAction(AKEY_EVENT_ACTION_DOWN)); // it doesn't matter that we
- // haven't ack'd the other events yet. We can finish events in any order.
+ // Now that we tapped, we should receive the key immediately.
+ // Since there's still room for slowness, we use 200ms, which is much less than
+ // the "key waiting for events' timeout of 500ms minus the already waited 100ms duration.
+ std::unique_ptr<InputEvent> keyEvent = mWindow->consume(200ms);
+ ASSERT_NE(nullptr, keyEvent);
+ ASSERT_EQ(InputEventType::KEY, keyEvent->getType());
+ ASSERT_THAT(static_cast<KeyEvent&>(*keyEvent), WithKeyAction(AKEY_EVENT_ACTION_DOWN));
+ // 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->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index f978016c9e..434fc36f8a 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -35,7 +35,7 @@ enum class CancelResult { Cancelled, TooLate, Error };
*/
class VSyncDispatch {
public:
- using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare, Hash>;
+ using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare>;
virtual ~VSyncDispatch();
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 5cb0ffbfb7..98aa5338f7 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -267,15 +267,15 @@ void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t /*now*/) {
}
void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) {
- rearmTimerSkippingUpdateFor(now, mCallbacks.end());
+ rearmTimerSkippingUpdateFor(now, mCallbacks.cend());
}
void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
- nsecs_t now, CallbackMap::iterator const& skipUpdateIt) {
+ nsecs_t now, CallbackMap::const_iterator skipUpdateIt) {
std::optional<nsecs_t> min;
std::optional<nsecs_t> targetVsync;
std::optional<std::string_view> nextWakeupName;
- for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
+ for (auto it = mCallbacks.cbegin(); it != mCallbacks.cend(); ++it) {
auto& callback = it->second;
if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) {
continue;
@@ -351,13 +351,12 @@ void VSyncDispatchTimerQueue::timerCallback() {
VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
Callback callback, std::string callbackName) {
std::lock_guard lock(mMutex);
- return CallbackToken{
- mCallbacks
- .emplace(++mCallbackToken,
- std::make_shared<VSyncDispatchTimerQueueEntry>(std::move(callbackName),
- std::move(callback),
- mMinVsyncDistance))
- .first->first};
+ return mCallbacks
+ .try_emplace(CallbackToken{++mCallbackToken},
+ std::make_shared<VSyncDispatchTimerQueueEntry>(std::move(callbackName),
+ std::move(callback),
+ mMinVsyncDistance))
+ .first->first;
}
void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
@@ -367,7 +366,7 @@ void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
auto it = mCallbacks.find(token);
if (it != mCallbacks.end()) {
entry = it->second;
- mCallbacks.erase(it);
+ mCallbacks.erase(it->first);
}
}
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index 3d08410df7..30e52421d4 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -16,14 +16,13 @@
#pragma once
-#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <string_view>
-#include <unordered_map>
#include <android-base/thread_annotations.h>
+#include <ftl/small_map.h>
#include "VSyncDispatch.h"
#include "VsyncSchedule.h"
@@ -135,13 +134,14 @@ private:
VSyncDispatchTimerQueue(const VSyncDispatchTimerQueue&) = delete;
VSyncDispatchTimerQueue& operator=(const VSyncDispatchTimerQueue&) = delete;
+ // The static capacity was chosen to exceed the expected number of callbacks.
using CallbackMap =
- std::unordered_map<CallbackToken, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;
+ ftl::SmallMap<CallbackToken, std::shared_ptr<VSyncDispatchTimerQueueEntry>, 5>;
void timerCallback();
void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex);
void rearmTimer(nsecs_t now) REQUIRES(mMutex);
- void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate)
+ void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::const_iterator skipUpdate)
REQUIRES(mMutex);
void cancelTimer() REQUIRES(mMutex);
ScheduleResult scheduleLocked(CallbackToken, ScheduleTiming) REQUIRES(mMutex);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 89a341ff84..589ef99a63 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3476,8 +3476,12 @@ const char* SurfaceFlinger::processHotplug(PhysicalDisplayId displayId,
auto [displayModes, activeMode] = loadDisplayModes(displayId);
if (!activeMode) {
- // TODO(b/241286153): Report hotplug failure to the framework.
ALOGE("Failed to hotplug display %s", to_string(displayId).c_str());
+ if (FlagManager::getInstance().hotplug2()) {
+ mScheduler->onHotplugConnectionError(mAppConnectionHandle,
+ static_cast<int32_t>(
+ DisplayHotplugEvent::ERROR_UNKNOWN));
+ }
getHwComposer().disconnectDisplay(displayId);
return nullptr;
}
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index b07e7ace06..425d2dac3e 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -130,6 +130,8 @@ void FlagManager::dump(std::string& result) const {
DUMP_READ_ONLY_FLAG(enable_layer_command_batching);
DUMP_READ_ONLY_FLAG(screenshot_fence_preservation);
DUMP_READ_ONLY_FLAG(vulkan_renderengine);
+ DUMP_READ_ONLY_FLAG(renderable_buffer_usage);
+ DUMP_READ_ONLY_FLAG(restore_blur_step);
#undef DUMP_READ_ONLY_FLAG
#undef DUMP_SERVER_FLAG
#undef DUMP_FLAG_INTERVAL
@@ -206,6 +208,8 @@ FLAG_MANAGER_READ_ONLY_FLAG(game_default_frame_rate, "")
FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "")
FLAG_MANAGER_READ_ONLY_FLAG(screenshot_fence_preservation, "debug.sf.screenshot_fence_preservation")
FLAG_MANAGER_READ_ONLY_FLAG(vulkan_renderengine, "debug.renderengine.vulkan")
+FLAG_MANAGER_READ_ONLY_FLAG(renderable_buffer_usage, "")
+FLAG_MANAGER_READ_ONLY_FLAG(restore_blur_step, "debug.renderengine.restore_blur_step")
/// Trunk stable server flags ///
FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 2a30a40730..86efd306a0 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -70,6 +70,8 @@ public:
bool enable_layer_command_batching() const;
bool screenshot_fence_preservation() const;
bool vulkan_renderengine() const;
+ bool renderable_buffer_usage() const;
+ bool restore_blur_step() const;
protected:
// overridden for unit tests
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index fcbef01bfa..0ebc41be7c 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -174,3 +174,25 @@ flag {
bug: "302703346"
is_fixed_read_only: true
}
+
+flag {
+ name: "renderable_buffer_usage"
+ namespace: "core_graphics"
+ description: "Decide whether an ExternalTexture isRenderable based on its buffer's usage."
+ bug: "305445199"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "restore_blur_step"
+ namespace: "core_graphics"
+ description: "Restore drawing the blur input prior to drawing blurred content."
+ bug: "255921628"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index 01762c13b6..110f324c8b 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -1026,6 +1026,7 @@ protected:
};
TEST_F(SmallAreaDetectionTest, SmallDirtyLayer) {
+ SET_FLAG_FOR_TEST(flags::enable_small_area_detection, true);
auto layer = createLegacyAndFrontedEndLayer(1);
nsecs_t time = systemTime();
@@ -1043,6 +1044,7 @@ TEST_F(SmallAreaDetectionTest, SmallDirtyLayer) {
}
TEST_F(SmallAreaDetectionTest, NotSmallDirtyLayer) {
+ SET_FLAG_FOR_TEST(flags::enable_small_area_detection, true);
auto layer = createLegacyAndFrontedEndLayer(1);
nsecs_t time = systemTime();
@@ -1060,6 +1062,7 @@ TEST_F(SmallAreaDetectionTest, NotSmallDirtyLayer) {
}
TEST_F(SmallAreaDetectionTest, smallDirtyLayerWithMatrix) {
+ SET_FLAG_FOR_TEST(flags::enable_small_area_detection, true);
auto layer = createLegacyAndFrontedEndLayer(1);
nsecs_t time = systemTime();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index a270dc91f2..897f9a0319 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -17,8 +17,14 @@
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
+#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
+#include <com_android_graphics_surfaceflinger_flags.h>
+#include <common/test/FlagUtils.h>
#include "DisplayTransactionTestHelpers.h"
+using namespace com::android::graphics::surfaceflinger;
+using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent;
+
namespace android {
class HotplugTest : public DisplayTransactionTest {};
@@ -87,6 +93,8 @@ TEST_F(HotplugTest, ignoresDuplicateDisconnection) {
}
TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) {
+ SET_FLAG_FOR_TEST(flags::connected_display, true);
+
// Inject a primary display.
PrimaryDisplayVariant::injectHwcDisplay(this);
@@ -94,6 +102,10 @@ TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) {
constexpr bool kFailedHotplug = true;
ExternalDisplay::setupHwcHotplugCallExpectations<kFailedHotplug>(this);
+ EXPECT_CALL(*mEventThread,
+ onHotplugConnectionError(static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN)))
+ .Times(1);
+
// Simulate a connect event that fails to load display modes due to HWC already having
// disconnected the display but SF yet having to process the queued disconnect event.
EXPECT_CALL(*mComposer, getActiveConfig(ExternalDisplay::HWC_DISPLAY_ID, _))