diff options
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, _)) |