diff options
156 files changed, 2909 insertions, 1212 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 481c28ef6b..5ee6b155f2 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -1563,6 +1563,13 @@ static void DumpstateLimitedOnly() { RunDumpsys("DROPBOX SYSTEM SERVER CRASHES", {"dropbox", "-p", "system_server_crash"}); RunDumpsys("DROPBOX SYSTEM APP CRASHES", {"dropbox", "-p", "system_app_crash"}); + + printf("========================================================\n"); + printf("== ANR Traces\n"); + printf("========================================================\n"); + + AddAnrTraceFiles(); + printf("========================================================\n"); printf("== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_, ds.progress_->Get(), ds.progress_->GetMax(), ds.progress_->GetInitialMax()); diff --git a/cmds/lshal/Timeout.h b/cmds/lshal/Timeout.h index d97ba89122..37f41beea9 100644 --- a/cmds/lshal/Timeout.h +++ b/cmds/lshal/Timeout.h @@ -16,44 +16,83 @@ #pragma once +#include <condition_variable> #include <chrono> -#include <future> +#include <functional> +#include <mutex> +#include <thread> #include <hidl/Status.h> -#include <utils/Errors.h> namespace android { namespace lshal { -// 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. +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; +} + 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; - - // 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) { + 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) { return Status::fromStatusT(TIMED_OUT); } - return Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE, "Illegal future_status"); + return ret; } -} // namespace lshal -} // namespace android + +} // namespace lshal +} // namespace android diff --git a/cmds/lshal/main.cpp b/cmds/lshal/main.cpp index bd5fa32521..366c9383a2 100644 --- a/cmds/lshal/main.cpp +++ b/cmds/lshal/main.cpp @@ -18,6 +18,5 @@ int main(int argc, char **argv) { using namespace ::android::lshal; - // Use _exit() to force terminate background threads in Timeout.h - _exit(Lshal{}.main(Arg{argc, argv})); + return Lshal{}.main(Arg{argc, argv}); } diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp index c24f827e73..cba7c4bf2a 100644 --- a/cmds/lshal/test.cpp +++ b/cmds/lshal/test.cpp @@ -14,10 +14,6 @@ * 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> @@ -40,8 +36,6 @@ 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; @@ -940,9 +934,12 @@ TEST_F(ListTest, DumpDebug) { return hardware::Void(); })); EXPECT_CALL(*serviceManager, get(_, _)) - .WillRepeatedly(Invoke([&](const hidl_string&, const hidl_string&) -> sp<IBase> { - return sp<IBase>(service); - })); + .WillRepeatedly( + Invoke([&](const hidl_string&, const hidl_string& instance) -> sp<IBase> { + int id = getIdFromInstanceName(instance); + if (id > inheritanceLevel) return nullptr; + return sp<IBase>(service); + })); const std::string expected = "[fake description 0]\n" "Interface\n" @@ -960,110 +957,6 @@ 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 { @@ -1186,6 +1079,5 @@ TEST_F(HelpTest, UnknownOptionHelp2) { int main(int argc, char **argv) { ::testing::InitGoogleMock(&argc, argv); - // Use _exit() to force terminate background threads in Timeout.h - _exit(RUN_ALL_TESTS()); + return RUN_ALL_TESTS(); } diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index a4018385c7..adcef911fb 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -150,7 +150,7 @@ static std::optional<std::string> getVintfUpdatableApex(const std::string& name) std::optional<std::string> updatableViaApex; forEachManifest([&](const ManifestWithDescription& mwd) { - mwd.manifest->forEachInstance([&](const auto& manifestInstance) { + bool cont = mwd.manifest->forEachInstance([&](const auto& manifestInstance) { if (manifestInstance.format() != vintf::HalFormat::AIDL) return true; if (manifestInstance.package() != aname.package) return true; if (manifestInstance.interface() != aname.iface) return true; @@ -158,8 +158,7 @@ static std::optional<std::string> getVintfUpdatableApex(const std::string& name) updatableViaApex = manifestInstance.updatableViaApex(); return false; // break (libvintf uses opposite convention) }); - if (updatableViaApex.has_value()) return true; // break (found match) - return false; // continue + return !cont; }); return updatableViaApex; diff --git a/data/etc/android.hardware.location.gps.xml b/data/etc/android.hardware.location.gps.xml index 72ab73228e..2a55370832 100644 --- a/data/etc/android.hardware.location.gps.xml +++ b/data/etc/android.hardware.location.gps.xml @@ -17,6 +17,5 @@ <!-- These are the location-related features for devices that include GPS. --> <permissions> <feature name="android.hardware.location" /> - <feature name="android.hardware.location.network" /> <feature name="android.hardware.location.gps" /> </permissions> diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml index 95b8110b6e..beb69f81dd 100644 --- a/data/etc/car_core_hardware.xml +++ b/data/etc/car_core_hardware.xml @@ -28,7 +28,6 @@ <feature name="android.hardware.audio.output" /> <feature name="android.hardware.location" /> - <feature name="android.hardware.location.network" /> <feature name="android.hardware.bluetooth" /> <feature name="android.hardware.touchscreen" /> <feature name="android.hardware.microphone" /> diff --git a/include/android/keycodes.h b/include/android/keycodes.h index f8fb256fae..79cdbcaf7b 100644 --- a/include/android/keycodes.h +++ b/include/android/keycodes.h @@ -839,6 +839,10 @@ enum { AKEYCODE_MACRO_3 = 315, /** User customizable key #4. */ AKEYCODE_MACRO_4 = 316, + /** Open Emoji picker */ + AKEYCODE_EMOJI_PICKER = 317, + /** Take Screenshot */ + AKEYCODE_SCREENSHOT = 318, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/include/ftl/details/future.h b/include/ftl/details/future.h index df1323e8be..8d82e0ffd2 100644 --- a/include/ftl/details/future.h +++ b/include/ftl/details/future.h @@ -73,8 +73,18 @@ class BaseFuture<Self, T, std::future> { return std::get<Impl>(self()).get(); } + template <class Rep, class Period> + std::future_status wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const { + if (std::holds_alternative<T>(self())) { + return std::future_status::ready; + } + + return std::get<Impl>(self()).wait_for(timeout_duration); + } + private: auto& self() { return static_cast<Self&>(*this).future_; } + const auto& self() const { return static_cast<const Self&>(*this).future_; } }; template <typename Self, typename T> @@ -90,6 +100,15 @@ class BaseFuture<Self, T, std::shared_future> { return std::get<Impl>(self()).get(); } + template <class Rep, class Period> + std::future_status wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const { + if (std::holds_alternative<T>(self())) { + return std::future_status::ready; + } + + return std::get<Impl>(self()).wait_for(timeout_duration); + } + private: const auto& self() const { return static_cast<const Self&>(*this).future_; } }; diff --git a/include/ftl/expected.h b/include/ftl/expected.h new file mode 100644 index 0000000000..12b6102b6f --- /dev/null +++ b/include/ftl/expected.h @@ -0,0 +1,65 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/expected.h> +#include <ftl/optional.h> + +#include <utility> + +namespace android::ftl { + +// Superset of base::expected<T, E> with monadic operations. +// +// TODO: Extend std::expected<T, E> in C++23. +// +template <typename T, typename E> +struct Expected final : base::expected<T, E> { + using Base = base::expected<T, E>; + using Base::expected; + + using Base::error; + using Base::has_value; + using Base::value; + + template <typename P> + constexpr bool has_error(P predicate) const { + return !has_value() && predicate(error()); + } + + constexpr Optional<T> value_opt() const& { + return has_value() ? Optional(value()) : std::nullopt; + } + + constexpr Optional<T> value_opt() && { + return has_value() ? Optional(std::move(value())) : std::nullopt; + } + + // Delete new for this class. Its base doesn't have a virtual destructor, and + // if it got deleted via base class pointer, it would cause undefined + // behavior. There's not a good reason to allocate this object on the heap + // anyway. + static void* operator new(size_t) = delete; + static void* operator new[](size_t) = delete; +}; + +template <typename E> +constexpr auto Unexpected(E&& error) { + return base::unexpected(std::forward<E>(error)); +} + +} // namespace android::ftl diff --git a/include/ftl/future.h b/include/ftl/future.h index c78f9b76b6..dad180ff8e 100644 --- a/include/ftl/future.h +++ b/include/ftl/future.h @@ -51,6 +51,7 @@ class Future final : public details::BaseFuture<Future<T, FutureImpl>, T, Future // Forwarding functions. Base::share is only defined when FutureImpl is std::future, whereas the // following are defined for either FutureImpl: using Base::get; + using Base::wait_for; // Attaches a continuation to the future. The continuation is a function that maps T to either R // or ftl::Future<R>. In the former case, the chain wraps the result in a future as if by diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index b7751f704a..57b659d9ee 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -75,6 +75,17 @@ struct InputDeviceIdentifier { bool operator!=(const InputDeviceIdentifier&) const = default; }; +/** + * Holds View related behaviors for an InputDevice. + */ +struct InputDeviceViewBehavior { + /** + * The smooth scroll behavior that applies for all source/axis, if defined by the device. + * Empty optional if the device has not specified the default smooth scroll behavior. + */ + std::optional<bool> shouldSmoothScroll; +}; + /* Types of input device sensors. Keep sync with core/java/android/hardware/Sensor.java */ enum class InputDeviceSensorType : int32_t { ACCELEROMETER = ASENSOR_TYPE_ACCELEROMETER, @@ -266,7 +277,8 @@ public: void initialize(int32_t id, int32_t generation, int32_t controllerNumber, const InputDeviceIdentifier& identifier, const std::string& alias, - bool isExternal, bool hasMic, int32_t associatedDisplayId); + bool isExternal, bool hasMic, int32_t associatedDisplayId, + InputDeviceViewBehavior viewBehavior = {{}}); inline int32_t getId() const { return mId; } inline int32_t getControllerNumber() const { return mControllerNumber; } @@ -298,6 +310,8 @@ public: return mKeyboardLayoutInfo; } + inline const InputDeviceViewBehavior& getViewBehavior() const { return mViewBehavior; } + inline void setKeyCharacterMap(const std::shared_ptr<KeyCharacterMap> value) { mKeyCharacterMap = value; } @@ -359,6 +373,8 @@ private: std::unordered_map<int32_t, InputDeviceLightInfo> mLights; /* Map from battery ID to battery info */ std::unordered_map<int32_t, InputDeviceBatteryInfo> mBatteries; + /** The View related behaviors for the device. */ + InputDeviceViewBehavior mViewBehavior; }; /* Types of input device configuration files. */ diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 2d64872beb..d53e8c6cce 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -35,18 +35,16 @@ #include <android-base/result.h> #include <android-base/unique_fd.h> +#include <android/os/InputChannelCore.h> #include <binder/IBinder.h> -#include <binder/Parcelable.h> #include <input/Input.h> #include <input/InputVerifier.h> #include <sys/stat.h> #include <ui/Transform.h> #include <utils/BitSet.h> #include <utils/Errors.h> -#include <utils/RefBase.h> #include <utils/Timers.h> - namespace android { class Parcel; @@ -231,18 +229,15 @@ struct InputMessage { * input messages across processes. Each channel has a descriptive name for debugging purposes. * * Each endpoint has its own InputChannel object that specifies its file descriptor. + * For parceling, this relies on android::os::InputChannelCore, defined in aidl. * * The input channel is closed when all references to it are released. */ -class InputChannel : public Parcelable { +class InputChannel : private android::os::InputChannelCore { public: - static std::unique_ptr<InputChannel> create(const std::string& name, - android::base::unique_fd fd, sp<IBinder> token); - InputChannel() = default; - InputChannel(const InputChannel& other) - : mName(other.mName), mFd(other.dupFd()), mToken(other.mToken){}; - InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token); - ~InputChannel() override; + static std::unique_ptr<InputChannel> create(android::os::InputChannelCore&& parceledChannel); + ~InputChannel(); + /** * Create a pair of input channels. * The two returned input channels are equivalent, and are labeled as "server" and "client" @@ -254,9 +249,8 @@ public: std::unique_ptr<InputChannel>& outServerChannel, std::unique_ptr<InputChannel>& outClientChannel); - inline std::string getName() const { return mName; } - inline const android::base::unique_fd& getFd() const { return mFd; } - inline sp<IBinder> getToken() const { return mToken; } + inline std::string getName() const { return name; } + inline int getFd() const { return fd.get(); } /* Send a message to the other endpoint. * @@ -304,10 +298,7 @@ public: /* Return a new object that has a duplicate of this channel's fd. */ std::unique_ptr<InputChannel> dup() const; - void copyTo(InputChannel& outChannel) const; - - status_t readFromParcel(const android::Parcel* parcel) override; - status_t writeToParcel(android::Parcel* parcel) const override; + void copyTo(android::os::InputChannelCore& outChannel) const; /** * The connection token is used to identify the input connection, i.e. @@ -323,26 +314,11 @@ public: */ sp<IBinder> getConnectionToken() const; - bool operator==(const InputChannel& inputChannel) const { - struct stat lhs, rhs; - if (fstat(mFd.get(), &lhs) != 0) { - return false; - } - if (fstat(inputChannel.getFd().get(), &rhs) != 0) { - return false; - } - // If file descriptors are pointing to same inode they are duplicated fds. - return inputChannel.getName() == getName() && inputChannel.getConnectionToken() == mToken && - lhs.st_ino == rhs.st_ino; - } - private: - base::unique_fd dupFd() const; - - std::string mName; - base::unique_fd mFd; + static std::unique_ptr<InputChannel> create(const std::string& name, + android::base::unique_fd fd, sp<IBinder> token); - sp<IBinder> mToken; + InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token); }; /* @@ -357,7 +333,7 @@ public: ~InputPublisher(); /* Gets the underlying input channel. */ - inline std::shared_ptr<InputChannel> getChannel() { return mChannel; } + inline std::shared_ptr<InputChannel> getChannel() const { return mChannel; } /* Publishes a key event to the input channel. * diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index 918680d6a7..5ac965f566 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -10,11 +10,15 @@ package { cc_test { name: "ftl_test", test_suites: ["device-tests"], + header_libs: [ + "libbase_headers", + ], srcs: [ "algorithm_test.cpp", "cast_test.cpp", "concat_test.cpp", "enum_test.cpp", + "expected_test.cpp", "fake_guard_test.cpp", "flags_test.cpp", "function_test.cpp", diff --git a/libs/ftl/expected_test.cpp b/libs/ftl/expected_test.cpp new file mode 100644 index 0000000000..8cb07e4696 --- /dev/null +++ b/libs/ftl/expected_test.cpp @@ -0,0 +1,77 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ftl/expected.h> +#include <gtest/gtest.h> + +#include <string> +#include <system_error> + +namespace android::test { + +using IntExp = ftl::Expected<int, std::errc>; +using StringExp = ftl::Expected<std::string, std::errc>; + +using namespace std::string_literals; + +TEST(Expected, Construct) { + // Default value. + EXPECT_TRUE(IntExp().has_value()); + EXPECT_EQ(IntExp(), IntExp(0)); + + EXPECT_TRUE(StringExp().has_value()); + EXPECT_EQ(StringExp(), StringExp("")); + + // Value. + ASSERT_TRUE(IntExp(42).has_value()); + EXPECT_EQ(42, IntExp(42).value()); + + ASSERT_TRUE(StringExp("test").has_value()); + EXPECT_EQ("test"s, StringExp("test").value()); + + // Error. + const auto exp = StringExp(ftl::Unexpected(std::errc::invalid_argument)); + ASSERT_FALSE(exp.has_value()); + EXPECT_EQ(std::errc::invalid_argument, exp.error()); +} + +TEST(Expected, HasError) { + EXPECT_FALSE(IntExp(123).has_error([](auto) { return true; })); + EXPECT_FALSE(IntExp(ftl::Unexpected(std::errc::io_error)).has_error([](auto) { return false; })); + + EXPECT_TRUE(StringExp(ftl::Unexpected(std::errc::permission_denied)).has_error([](auto e) { + return e == std::errc::permission_denied; + })); +} + +TEST(Expected, ValueOpt) { + EXPECT_EQ(ftl::Optional(-1), IntExp(-1).value_opt()); + EXPECT_EQ(std::nullopt, IntExp(ftl::Unexpected(std::errc::broken_pipe)).value_opt()); + + { + const StringExp exp("foo"s); + EXPECT_EQ(ftl::Optional('f'), + exp.value_opt().transform([](const auto& s) { return s.front(); })); + EXPECT_EQ("foo"s, exp.value()); + } + { + StringExp exp("foobar"s); + EXPECT_EQ(ftl::Optional(6), std::move(exp).value_opt().transform(&std::string::length)); + EXPECT_TRUE(exp.value().empty()); + } +} + +} // namespace android::test diff --git a/libs/ftl/future_test.cpp b/libs/ftl/future_test.cpp index 5a245b681c..1140639c87 100644 --- a/libs/ftl/future_test.cpp +++ b/libs/ftl/future_test.cpp @@ -102,4 +102,42 @@ TEST(Future, Chain) { decrement_thread.join(); } +TEST(Future, WaitFor) { + using namespace std::chrono_literals; + { + auto future = ftl::yield(42); + // Check that we can wait_for multiple times without invalidating the future + EXPECT_EQ(future.wait_for(1s), std::future_status::ready); + EXPECT_EQ(future.wait_for(1s), std::future_status::ready); + EXPECT_EQ(future.get(), 42); + } + + { + std::condition_variable cv; + std::mutex m; + bool ready = false; + + std::packaged_task<int32_t()> get_int([&] { + std::unique_lock lk(m); + cv.wait(lk, [&] { return ready; }); + return 24; + }); + + auto get_future = ftl::Future(get_int.get_future()); + std::thread get_thread(std::move(get_int)); + + EXPECT_EQ(get_future.wait_for(0s), std::future_status::timeout); + { + std::unique_lock lk(m); + ready = true; + } + cv.notify_one(); + + EXPECT_EQ(get_future.wait_for(1s), std::future_status::ready); + EXPECT_EQ(get_future.get(), 24); + + get_thread.join(); + } +} + } // namespace android::test diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp index 7d37fd37cb..4518b67d4c 100644 --- a/libs/gui/Choreographer.cpp +++ b/libs/gui/Choreographer.cpp @@ -116,7 +116,7 @@ Choreographer::~Choreographer() { std::lock_guard<std::mutex> _l(gChoreographers.lock); gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(), gChoreographers.ptrs.end(), - [=](Choreographer* c) { return c == this; }), + [=, this](Choreographer* c) { return c == this; }), gChoreographers.ptrs.end()); // Only poke DisplayManagerGlobal to unregister if we previously registered // callbacks. diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 38fab9cdaa..7564c26316 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -199,7 +199,7 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeParcelable, trustedPresentationListener); SAFE_PARCEL(output.writeFloat, currentHdrSdrRatio); SAFE_PARCEL(output.writeFloat, desiredHdrSdrRatio); - SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(cachingHint)) + SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(cachingHint)); return NO_ERROR; } @@ -484,6 +484,12 @@ void layer_state_t::sanitize(int32_t permissions) { flags &= ~eLayerIsDisplayDecoration; ALOGE("Stripped attempt to set LayerIsDisplayDecoration in sanitize"); } + if ((mask & eCanOccludePresentation) && + !(permissions & Permission::ACCESS_SURFACE_FLINGER)) { + flags &= ~eCanOccludePresentation; + mask &= ~eCanOccludePresentation; + ALOGE("Stripped attempt to set eCanOccludePresentation in sanitize"); + } } if (what & layer_state_t::eInputInfoChanged) { diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp index 95b2641f04..9429d2cc15 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -109,7 +109,8 @@ bool WindowInfo::operator==(const WindowInfo& info) const { info.inputConfig == inputConfig && info.displayId == displayId && info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop && info.applicationInfo == applicationInfo && info.layoutParamsType == layoutParamsType && - info.layoutParamsFlags == layoutParamsFlags; + info.layoutParamsFlags == layoutParamsFlags && + info.canOccludePresentation == canOccludePresentation; } status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { @@ -158,8 +159,9 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->write(touchableRegion) ?: parcel->writeBool(replaceTouchableRegionWithCrop) ?: parcel->writeStrongBinder(touchableRegionCropHandle.promote()) ?: - parcel->writeStrongBinder(windowToken); - parcel->writeStrongBinder(focusTransferTarget); + parcel->writeStrongBinder(windowToken) ?: + parcel->writeStrongBinder(focusTransferTarget) ?: + parcel->writeBool(canOccludePresentation); // clang-format on return status; } @@ -210,7 +212,8 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { parcel->readBool(&replaceTouchableRegionWithCrop) ?: parcel->readNullableStrongBinder(&touchableRegionCropHandleSp) ?: parcel->readNullableStrongBinder(&windowToken) ?: - parcel->readNullableStrongBinder(&focusTransferTarget); + parcel->readNullableStrongBinder(&focusTransferTarget) ?: + parcel->readBool(&canOccludePresentation); // clang-format on @@ -273,6 +276,7 @@ std::ostream& operator<<(std::ostream& out, const WindowInfo& info) { << "ms, token=" << info.token.get() << ", touchOcclusionMode=" << ftl::enum_string(info.touchOcclusionMode) << "\n" << transform; + if (info.canOccludePresentation) out << " canOccludePresentation"; return out; } diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 6b8e8248b8..920310ea9e 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -161,6 +161,9 @@ struct layer_state_t { // See SurfaceView scaling behavior for more details. eIgnoreDestinationFrame = 0x400, eLayerIsRefreshRateIndicator = 0x800, // REFRESH_RATE_INDICATOR + // Sets a property on this layer indicating that its visible region should be considered + // when computing TrustedPresentation Thresholds. + eCanOccludePresentation = 0x1000, }; enum { diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index e72fd59147..32d60be612 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -246,6 +246,10 @@ struct WindowInfo : public Parcelable { // any other window. sp<IBinder> focusTransferTarget; + // Sets a property on this window indicating that its visible region should be considered when + // computing TrustedPresentation Thresholds. + bool canOccludePresentation = false; + void setInputConfig(ftl::Flags<InputConfig> config, bool value); void addTouchableRegion(const Rect& region); diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index b9ab803101..a9d6e8d3bf 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -63,8 +63,7 @@ namespace android::test { using Transaction = SurfaceComposerClient::Transaction; sp<IInputFlinger> getInputFlinger() { - sp<IBinder> input(defaultServiceManager()->getService( - String16("inputflinger"))); + sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger"))); if (input == nullptr) { ALOGE("Failed to link to input service"); } else { ALOGE("Linked to input"); } @@ -104,8 +103,13 @@ public: if (noInputChannel) { mInputInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true); } else { - mClientChannel = std::make_shared<InputChannel>(); - mInputFlinger->createInputChannel("testchannels", mClientChannel.get()); + android::os::InputChannelCore tempChannel; + android::binder::Status result = + mInputFlinger->createInputChannel("testchannels", &tempChannel); + if (!result.isOk()) { + ADD_FAILURE() << "binder call to createInputChannel failed"; + } + mClientChannel = InputChannel::create(std::move(tempChannel)); mInputInfo.token = mClientChannel->getConnectionToken(); mInputConsumer = new InputConsumer(mClientChannel); } diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 3d3bf1355f..e5fb2c5b7a 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -175,6 +175,7 @@ cc_library { ], srcs: [ "android/os/IInputFlinger.aidl", + "android/os/InputChannelCore.aidl", "AccelerationCurve.cpp", "Input.cpp", "InputDevice.cpp", diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 8eaff00cbd..0d29b4db96 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -374,7 +374,7 @@ std::ostream& operator<<(std::ostream& out, const KeyEvent& event) { out << ", deviceId=" << event.getDeviceId(); out << ", source=" << inputEventSourceToString(event.getSource()); out << ", displayId=" << event.getDisplayId(); - out << ", eventId=" << event.getId(); + out << ", eventId=0x" << std::hex << event.getId() << std::dec; out << "}"; return out; } @@ -1051,7 +1051,7 @@ std::ostream& operator<<(std::ostream& out, const MotionEvent& event) { out << ", deviceId=" << event.getDeviceId(); out << ", source=" << inputEventSourceToString(event.getSource()); out << ", displayId=" << event.getDisplayId(); - out << ", eventId=" << event.getId(); + out << ", eventId=0x" << std::hex << event.getId() << std::dec; out << "}"; return out; } diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 9c7c0c19ed..d4dbc45090 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -190,14 +190,16 @@ InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) mHasSensor(other.mHasSensor), mMotionRanges(other.mMotionRanges), mSensors(other.mSensors), - mLights(other.mLights) {} + mLights(other.mLights), + mViewBehavior(other.mViewBehavior) {} InputDeviceInfo::~InputDeviceInfo() { } void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber, const InputDeviceIdentifier& identifier, const std::string& alias, - bool isExternal, bool hasMic, int32_t associatedDisplayId) { + bool isExternal, bool hasMic, int32_t associatedDisplayId, + InputDeviceViewBehavior viewBehavior) { mId = id; mGeneration = generation; mControllerNumber = controllerNumber; @@ -212,6 +214,7 @@ void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t control mHasBattery = false; mHasButtonUnderPad = false; mHasSensor = false; + mViewBehavior = viewBehavior; mUsiVersion.reset(); mMotionRanges.clear(); mSensors.clear(); diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp index 0e627e56fd..8db0ca588b 100644 --- a/libs/input/InputEventLabels.cpp +++ b/libs/input/InputEventLabels.cpp @@ -348,7 +348,9 @@ namespace android { DEFINE_KEYCODE(MACRO_1), \ DEFINE_KEYCODE(MACRO_2), \ DEFINE_KEYCODE(MACRO_3), \ - DEFINE_KEYCODE(MACRO_4) + DEFINE_KEYCODE(MACRO_4), \ + DEFINE_KEYCODE(EMOJI_PICKER), \ + DEFINE_KEYCODE(SCREENSHOT) // NOTE: If you add a new axis here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 37de00cf35..0e0e80dc79 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -95,6 +95,21 @@ bool debugResampling() { return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO); } +android::base::unique_fd dupChannelFd(int fd) { + android::base::unique_fd newFd(::dup(fd)); + if (!newFd.ok()) { + ALOGE("Could not duplicate fd %i : %s", fd, strerror(errno)); + const bool hitFdLimit = errno == EMFILE || errno == ENFILE; + // If this process is out of file descriptors, then throwing that might end up exploding + // on the other side of a binder call, which isn't really helpful. + // Better to just crash here and hope that the FD leak is slow. + // Other failures could be client errors, so we still propagate those back to the caller. + LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel"); + return {}; + } + return newFd; +} + } // namespace using android::base::Result; @@ -395,15 +410,23 @@ std::unique_ptr<InputChannel> InputChannel::create(const std::string& name, return std::unique_ptr<InputChannel>(new InputChannel(name, std::move(fd), token)); } -InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token) - : mName(std::move(name)), mFd(std::move(fd)), mToken(std::move(token)) { +std::unique_ptr<InputChannel> InputChannel::create( + android::os::InputChannelCore&& parceledChannel) { + return InputChannel::create(parceledChannel.name, parceledChannel.fd.release(), + parceledChannel.token); +} + +InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token) { + this->name = std::move(name); + this->fd.reset(std::move(fd)); + this->token = std::move(token); ALOGD_IF(DEBUG_CHANNEL_LIFECYCLE, "Input channel constructed: name='%s', fd=%d", - getName().c_str(), getFd().get()); + getName().c_str(), getFd()); } InputChannel::~InputChannel() { ALOGD_IF(DEBUG_CHANNEL_LIFECYCLE, "Input channel destroyed: name='%s', fd=%d", - getName().c_str(), getFd().get()); + getName().c_str(), getFd()); } status_t InputChannel::openInputChannelPair(const std::string& name, @@ -441,19 +464,19 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { ATRACE_NAME_IF(ATRACE_ENABLED(), StringPrintf("sendMessage(inputChannel=%s, seq=0x%" PRIx32 ", type=0x%" PRIx32 ")", - mName.c_str(), msg->header.seq, msg->header.type)); + name.c_str(), msg->header.seq, msg->header.type)); const size_t msgLength = msg->size(); InputMessage cleanMsg; msg->getSanitizedCopy(&cleanMsg); ssize_t nWrite; do { - nWrite = ::send(getFd().get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); + nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); if (nWrite < 0) { int error = errno; ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ error sending message of type %s, %s", - mName.c_str(), ftl::enum_string(msg->header.type).c_str(), strerror(error)); + name.c_str(), ftl::enum_string(msg->header.type).c_str(), strerror(error)); if (error == EAGAIN || error == EWOULDBLOCK) { return WOULD_BLOCK; } @@ -465,12 +488,12 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { if (size_t(nWrite) != msgLength) { ALOGD_IF(DEBUG_CHANNEL_MESSAGES, - "channel '%s' ~ error sending message type %s, send was incomplete", mName.c_str(), + "channel '%s' ~ error sending message type %s, send was incomplete", name.c_str(), ftl::enum_string(msg->header.type).c_str()); return DEAD_OBJECT; } - ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ sent message of type %s", mName.c_str(), + ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ sent message of type %s", name.c_str(), ftl::enum_string(msg->header.type).c_str()); return OK; @@ -479,13 +502,13 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { status_t InputChannel::receiveMessage(InputMessage* msg) { ssize_t nRead; do { - nRead = ::recv(getFd().get(), msg, sizeof(InputMessage), MSG_DONTWAIT); + nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT); } while (nRead == -1 && errno == EINTR); if (nRead < 0) { int error = errno; ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ receive message failed, errno=%d", - mName.c_str(), errno); + name.c_str(), errno); if (error == EAGAIN || error == EWOULDBLOCK) { return WOULD_BLOCK; } @@ -497,29 +520,29 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { if (nRead == 0) { // check for EOF ALOGD_IF(DEBUG_CHANNEL_MESSAGES, - "channel '%s' ~ receive message failed because peer was closed", mName.c_str()); + "channel '%s' ~ receive message failed because peer was closed", name.c_str()); return DEAD_OBJECT; } if (!msg->isValid(nRead)) { - ALOGE("channel '%s' ~ received invalid message of size %zd", mName.c_str(), nRead); + ALOGE("channel '%s' ~ received invalid message of size %zd", name.c_str(), nRead); return BAD_VALUE; } - ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ received message of type %s", mName.c_str(), + ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ received message of type %s", name.c_str(), ftl::enum_string(msg->header.type).c_str()); if (ATRACE_ENABLED()) { // Add an additional trace point to include data about the received message. std::string message = StringPrintf("receiveMessage(inputChannel=%s, seq=0x%" PRIx32 ", type=0x%" PRIx32 ")", - mName.c_str(), msg->header.seq, msg->header.type); + name.c_str(), msg->header.seq, msg->header.type); ATRACE_NAME(message.c_str()); } return OK; } bool InputChannel::probablyHasInput() const { - struct pollfd pfds = {.fd = mFd, .events = POLLIN}; + struct pollfd pfds = {.fd = fd.get(), .events = POLLIN}; if (::poll(&pfds, /*nfds=*/1, /*timeout=*/0) <= 0) { // This can be a false negative because EINTR and ENOMEM are not handled. The latter should // be extremely rare. The EINTR is also unlikely because it happens only when the signal @@ -538,7 +561,7 @@ void InputChannel::waitForMessage(std::chrono::milliseconds timeout) const { if (timeout < 0ms) { LOG(FATAL) << "Timeout cannot be negative, received " << timeout.count(); } - struct pollfd pfds = {.fd = mFd, .events = POLLIN}; + struct pollfd pfds = {.fd = fd.get(), .events = POLLIN}; int ret; std::chrono::time_point<std::chrono::steady_clock> stopTime = std::chrono::steady_clock::now() + timeout; @@ -551,53 +574,18 @@ void InputChannel::waitForMessage(std::chrono::milliseconds timeout) const { } std::unique_ptr<InputChannel> InputChannel::dup() const { - base::unique_fd newFd(dupFd()); + base::unique_fd newFd(dupChannelFd(fd.get())); return InputChannel::create(getName(), std::move(newFd), getConnectionToken()); } -void InputChannel::copyTo(InputChannel& outChannel) const { - outChannel.mName = getName(); - outChannel.mFd = dupFd(); - outChannel.mToken = getConnectionToken(); -} - -status_t InputChannel::writeToParcel(android::Parcel* parcel) const { - if (parcel == nullptr) { - ALOGE("%s: Null parcel", __func__); - return BAD_VALUE; - } - return parcel->writeStrongBinder(mToken) - ?: parcel->writeUtf8AsUtf16(mName) ?: parcel->writeUniqueFileDescriptor(mFd); -} - -status_t InputChannel::readFromParcel(const android::Parcel* parcel) { - if (parcel == nullptr) { - ALOGE("%s: Null parcel", __func__); - return BAD_VALUE; - } - mToken = parcel->readStrongBinder(); - return parcel->readUtf8FromUtf16(&mName) ?: parcel->readUniqueFileDescriptor(&mFd); +void InputChannel::copyTo(android::os::InputChannelCore& outChannel) const { + outChannel.name = getName(); + outChannel.fd.reset(dupChannelFd(fd.get())); + outChannel.token = getConnectionToken(); } sp<IBinder> InputChannel::getConnectionToken() const { - return mToken; -} - -base::unique_fd InputChannel::dupFd() const { - base::unique_fd newFd(::dup(getFd().get())); - if (!newFd.ok()) { - ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(), - strerror(errno)); - const bool hitFdLimit = errno == EMFILE || errno == ENFILE; - // If this process is out of file descriptors, then throwing that might end up exploding - // on the other side of a binder call, which isn't really helpful. - // Better to just crash here and hope that the FD leak is slow. - // Other failures could be client errors, so we still propagate those back to the caller. - LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s", - getName().c_str()); - return {}; - } - return newFd; + return token; } // --- InputPublisher --- diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl index 00ebd4d34f..c1aacfbc53 100644 --- a/libs/input/android/os/IInputFlinger.aidl +++ b/libs/input/android/os/IInputFlinger.aidl @@ -16,14 +16,13 @@ package android.os; -import android.InputChannel; +import android.os.InputChannelCore; import android.gui.FocusRequest; -import android.gui.WindowInfo; /** @hide */ interface IInputFlinger { - InputChannel createInputChannel(in @utf8InCpp String name); + InputChannelCore createInputChannel(in @utf8InCpp String name); void removeInputChannel(in IBinder connectionToken); /** * Sets focus to the window identified by the token. This must be called diff --git a/libs/input/android/InputChannel.aidl b/libs/input/android/os/InputChannelCore.aidl index c2d1112dd3..888a553268 100644 --- a/libs/input/android/InputChannel.aidl +++ b/libs/input/android/os/InputChannelCore.aidl @@ -15,6 +15,16 @@ ** limitations under the License. */ -package android; +package android.os; -parcelable InputChannel cpp_header "input/InputTransport.h"; +import android.os.ParcelFileDescriptor; + +/** + * Input channel struct for sending InputChannel between processes. + * @hide + */ +parcelable InputChannelCore { + @utf8InCpp String name; + ParcelFileDescriptor fd; + IBinder token; +} diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 0c850fe0ee..ec7a2849ea 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -26,6 +26,13 @@ flag { namespace: "input" description: "Set to true to enable timer support for the touchpad Gestures library" bug: "297192727" + } + + flag { + name: "enable_input_event_tracing" + namespace: "input" + description: "Set to true to enable input event tracing, including always-on tracing on non-user builds" + bug: "210460522" } flag { @@ -104,3 +111,10 @@ flag { description: "Move user-activity poke rate-limiting from PowerManagerService to InputDispatcher." bug: "320499729" } + +flag { + name: "input_device_view_behavior_api" + namespace: "input" + description: "Controls the API to provide InputDevice view behavior." + bug: "246946631" +} diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs index 867af793bc..b1d7760682 100644 --- a/libs/input/rust/input_verifier.rs +++ b/libs/input/rust/input_verifier.rs @@ -79,7 +79,7 @@ impl InputVerifier { logger::init( logger::Config::default() .with_tag_on_device("InputVerifier") - .with_min_level(log::Level::Trace), + .with_max_level(log::LevelFilter::Trace), ); Self { name: name.to_owned(), diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp index 650c93034c..60feb53dcc 100644 --- a/libs/input/tests/InputChannel_test.cpp +++ b/libs/input/tests/InputChannel_test.cpp @@ -32,37 +32,31 @@ namespace android { +namespace { +bool operator==(const InputChannel& left, const InputChannel& right) { + struct stat lhs, rhs; + if (fstat(left.getFd(), &lhs) != 0) { + return false; + } + if (fstat(right.getFd(), &rhs) != 0) { + return false; + } + // If file descriptors are pointing to same inode they are duplicated fds. + return left.getName() == right.getName() && + left.getConnectionToken() == right.getConnectionToken() && lhs.st_ino == rhs.st_ino; +} +} // namespace + class InputChannelTest : public testing::Test { }; +TEST_F(InputChannelTest, ClientAndServerTokensMatch) { + std::unique_ptr<InputChannel> serverChannel, clientChannel; -TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) { - // Our purpose here is to verify that the input channel destructor closes the - // file descriptor provided to it. One easy way is to provide it with one end - // of a pipe and to check for EPIPE on the other end after the channel is destroyed. - Pipe pipe; - - android::base::unique_fd sendFd(pipe.sendFd); - - std::unique_ptr<InputChannel> inputChannel = - InputChannel::create("channel name", std::move(sendFd), new BBinder()); - - EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created"; - EXPECT_STREQ("channel name", inputChannel->getName().c_str()) - << "channel should have provided name"; - EXPECT_NE(-1, inputChannel->getFd()) << "channel should have valid fd"; - - // InputChannel should be the owner of the file descriptor now - ASSERT_FALSE(sendFd.ok()); -} - -TEST_F(InputChannelTest, SetAndGetToken) { - Pipe pipe; - sp<IBinder> token = new BBinder(); - std::unique_ptr<InputChannel> channel = - InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd), token); - - EXPECT_EQ(token, channel->getConnectionToken()); + status_t result = + InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); + ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; + EXPECT_EQ(serverChannel->getConnectionToken(), clientChannel->getConnectionToken()); } TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { @@ -71,8 +65,7 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); - ASSERT_EQ(OK, result) - << "should have successfully opened a channel pair"; + ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; // Name EXPECT_STREQ("channel name (server)", serverChannel->getName().c_str()) @@ -235,25 +228,6 @@ TEST_F(InputChannelTest, SendAndReceive_MotionClassification) { } } -TEST_F(InputChannelTest, InputChannelParcelAndUnparcel) { - std::unique_ptr<InputChannel> serverChannel, clientChannel; - - status_t result = - InputChannel::openInputChannelPair("channel parceling", serverChannel, clientChannel); - - ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; - - InputChannel chan; - Parcel parcel; - ASSERT_EQ(OK, serverChannel->writeToParcel(&parcel)); - parcel.setDataPosition(0); - chan.readFromParcel(&parcel); - - EXPECT_EQ(chan == *serverChannel, true) - << "inputchannel should be equal after parceling and unparceling.\n" - << "name " << chan.getName() << " name " << serverChannel->getName(); -} - TEST_F(InputChannelTest, DuplicateChannelAndAssertEqual) { std::unique_ptr<InputChannel> serverChannel, clientChannel; diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index fc84bbf283..330cc6650a 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -1016,7 +1016,7 @@ void SkiaRenderEngine::drawLayersInternal( .fakeOutputDataspace = fakeDataspace})); // Turn on dithering when dimming beyond this (arbitrary) threshold... - static constexpr float kDimmingThreshold = 0.2f; + static constexpr float kDimmingThreshold = 0.9f; // ...or we're rendering an HDR layer down to an 8-bit target // Most HDR standards require at least 10-bits of color depth for source content, so we // can just extract the transfer function rather than dig into precise gralloc layout. diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index 3af85c064e..bff12ce7ff 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -34,6 +34,8 @@ #include <cstdint> #include <memory> +#include <sstream> +#include <string> #include <vector> #include <vulkan/vulkan.h> @@ -67,6 +69,13 @@ struct DestroySemaphoreInfo { DestroySemaphoreInfo(VkSemaphore semaphore) : mSemaphore(semaphore) {} }; +namespace { +void onVkDeviceFault(void* callbackContext, const std::string& description, + const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos, + const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos, + const std::vector<std::byte>& vendorBinaryData); +} // anonymous namespace + struct VulkanInterface { bool initialized = false; VkInstance instance; @@ -79,6 +88,7 @@ struct VulkanInterface { VkPhysicalDeviceFeatures2* physicalDeviceFeatures2 = nullptr; VkPhysicalDeviceSamplerYcbcrConversionFeatures* samplerYcbcrConversionFeatures = nullptr; VkPhysicalDeviceProtectedMemoryFeatures* protectedMemoryFeatures = nullptr; + VkPhysicalDeviceFaultFeaturesEXT* deviceFaultFeatures = nullptr; GrVkGetProc grGetProc; bool isProtected; bool isRealtimePriority; @@ -100,6 +110,8 @@ struct VulkanInterface { backendContext.fDeviceFeatures2 = physicalDeviceFeatures2; backendContext.fGetProc = grGetProc; backendContext.fProtectedContext = isProtected ? GrProtected::kYes : GrProtected::kNo; + backendContext.fDeviceLostContext = this; // VulkanInterface is long-lived + backendContext.fDeviceLostProc = onVkDeviceFault; return backendContext; }; @@ -178,6 +190,68 @@ struct VulkanInterface { } }; +namespace { +void onVkDeviceFault(void* callbackContext, const std::string& description, + const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos, + const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos, + const std::vector<std::byte>& vendorBinaryData) { + VulkanInterface* interface = static_cast<VulkanInterface*>(callbackContext); + const std::string protectedStr = interface->isProtected ? "protected" : "non-protected"; + // The final crash string should contain as much differentiating info as possible, up to 1024 + // bytes. As this final message is constructed, the same information is also dumped to the logs + // but in a more verbose format. Building the crash string is unsightly, so the clearer logging + // statement is always placed first to give context. + ALOGE("VK_ERROR_DEVICE_LOST (%s context): %s", protectedStr.c_str(), description.c_str()); + std::stringstream crashMsg; + crashMsg << "VK_ERROR_DEVICE_LOST (" << protectedStr; + + if (!addressInfos.empty()) { + ALOGE("%zu VkDeviceFaultAddressInfoEXT:", addressInfos.size()); + crashMsg << ", " << addressInfos.size() << " address info ("; + for (VkDeviceFaultAddressInfoEXT addressInfo : addressInfos) { + ALOGE(" addressType: %d", (int)addressInfo.addressType); + ALOGE(" reportedAddress: %" PRIu64, addressInfo.reportedAddress); + ALOGE(" addressPrecision: %" PRIu64, addressInfo.addressPrecision); + crashMsg << addressInfo.addressType << ":" + << addressInfo.reportedAddress << ":" + << addressInfo.addressPrecision << ", "; + } + crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", " + crashMsg << ")"; + } + + if (!vendorInfos.empty()) { + ALOGE("%zu VkDeviceFaultVendorInfoEXT:", vendorInfos.size()); + crashMsg << ", " << vendorInfos.size() << " vendor info ("; + for (VkDeviceFaultVendorInfoEXT vendorInfo : vendorInfos) { + ALOGE(" description: %s", vendorInfo.description); + ALOGE(" vendorFaultCode: %" PRIu64, vendorInfo.vendorFaultCode); + ALOGE(" vendorFaultData: %" PRIu64, vendorInfo.vendorFaultData); + // Omit descriptions for individual vendor info structs in the crash string, as the + // fault code and fault data fields should be enough for clustering, and the verbosity + // isn't worth it. Additionally, vendors may just set the general description field of + // the overall fault to the description of the first element in this list, and that + // overall description will be placed at the end of the crash string. + crashMsg << vendorInfo.vendorFaultCode << ":" + << vendorInfo.vendorFaultData << ", "; + } + crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", " + crashMsg << ")"; + } + + if (!vendorBinaryData.empty()) { + // TODO: b/322830575 - Log in base64, or dump directly to a file that gets put in bugreports + ALOGE("%zu bytes of vendor-specific binary data (please notify Android's Core Graphics" + " Stack team if you observe this message).", + vendorBinaryData.size()); + crashMsg << ", " << vendorBinaryData.size() << " bytes binary"; + } + + crashMsg << "): " << description; + LOG_ALWAYS_FATAL("%s", crashMsg.str().c_str()); +}; +} // anonymous namespace + static GrVkGetProc sGetProc = [](const char* proc_name, VkInstance instance, VkDevice device) { if (device != VK_NULL_HANDLE) { return vkGetDeviceProcAddr(device, proc_name); @@ -429,6 +503,14 @@ VulkanInterface initVulkanInterface(bool protectedContent = false) { tailPnext = &interface.protectedMemoryFeatures->pNext; } + if (interface.grExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) { + interface.deviceFaultFeatures = new VkPhysicalDeviceFaultFeaturesEXT; + interface.deviceFaultFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT; + interface.deviceFaultFeatures->pNext = nullptr; + *tailPnext = interface.deviceFaultFeatures; + tailPnext = &interface.deviceFaultFeatures->pNext; + } + vkGetPhysicalDeviceFeatures2(physicalDevice, interface.physicalDeviceFeatures2); // Looks like this would slow things down and we can't depend on it on all platforms interface.physicalDeviceFeatures2->features.robustBufferAccess = VK_FALSE; @@ -545,6 +627,10 @@ void teardownVulkanInterface(VulkanInterface* interface) { delete interface->physicalDeviceFeatures2; } + if (interface->deviceFaultFeatures) { + delete interface->deviceFaultFeatures; + } + interface->samplerYcbcrConversionFeatures = nullptr; interface->physicalDeviceFeatures2 = nullptr; interface->protectedMemoryFeatures = nullptr; diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp index d992aa5105..cc92bc30a0 100644 --- a/libs/sensor/Android.bp +++ b/libs/sensor/Android.bp @@ -21,6 +21,18 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +aconfig_declarations { + name: "libsensor_flags", + package: "com.android.hardware.libsensor.flags", + srcs: ["libsensor_flags.aconfig"], +} + +cc_aconfig_library { + name: "libsensor_flags_c_lib", + host_supported: true, + aconfig_declarations: "libsensor_flags", +} + cc_library { name: "libsensor", @@ -52,6 +64,10 @@ cc_library { "android.companion.virtual.virtualdevice_aidl-cpp", ], + static_libs: [ + "libsensor_flags_c_lib", + ], + export_include_dirs: ["include"], export_shared_lib_headers: [ diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp index b82a79f501..9411e204e9 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -37,6 +37,8 @@ #include <sensor/Sensor.h> #include <sensor/SensorEventQueue.h> +#include <com_android_hardware_libsensor_flags.h> + // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- @@ -192,6 +194,9 @@ void SensorManager::sensorManagerDied() { } status_t SensorManager::assertStateLocked() { +#if COM_ANDROID_HARDWARE_LIBSENSOR_FLAGS(SENSORMANAGER_PING_BINDER) + if (mSensorServer == nullptr) { +#else bool initSensorManager = false; if (mSensorServer == nullptr) { initSensorManager = true; @@ -203,6 +208,7 @@ status_t SensorManager::assertStateLocked() { } } if (initSensorManager) { +#endif waitForSensorService(&mSensorServer); LOG_ALWAYS_FATAL_IF(mSensorServer == nullptr, "getService(SensorService) NULL"); diff --git a/libs/sensor/libsensor_flags.aconfig b/libs/sensor/libsensor_flags.aconfig new file mode 100644 index 0000000000..ef4d7370b5 --- /dev/null +++ b/libs/sensor/libsensor_flags.aconfig @@ -0,0 +1,9 @@ +package: "com.android.hardware.libsensor.flags" + +flag { + name: "sensormanager_ping_binder" + namespace: "sensors" + description: "Whether to pingBinder on SensorManager init" + bug: "322228259" + is_fixed_read_only: true +}
\ No newline at end of file diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen index 7fd9c3a735..f73fa5d63c 100755 --- a/opengl/tools/glgen/gen +++ b/opengl/tools/glgen/gen @@ -22,8 +22,10 @@ mkdir out mkdir -p out/javax/microedition/khronos/opengles mkdir -p out/com/google/android/gles_jni +mkdir -p out/android/annotation mkdir -p out/android/app mkdir -p out/android/graphics +mkdir -p out/android/hardware mkdir -p out/android/view mkdir -p out/android/opengl mkdir -p out/android/content @@ -34,18 +36,20 @@ mkdir -p out/android/util echo "package android.graphics;" > out/android/graphics/Canvas.java echo "public interface Canvas {}" >> out/android/graphics/Canvas.java +echo "package android.annotation; public @interface NonNull {}" > out/android/annotation/NonNull.java echo "package android.app; import android.content.pm.IPackageManager; public class AppGlobals { public static IPackageManager getPackageManager() { return null;} }" > out/android/app/AppGlobals.java # echo "package android.content; import android.content.pm.PackageManager; public interface Context { public PackageManager getPackageManager(); }" > out/android/content/Context.java echo "package android.content.pm; public class ApplicationInfo {public int targetSdkVersion;}" > out/android/content/pm/ApplicationInfo.java echo "package android.content.pm; public interface IPackageManager {ApplicationInfo getApplicationInfo(java.lang.String packageName, int flags, java.lang.String userId) throws android.os.RemoteException;}" > out/android/content/pm/IPackageManager.java -echo "package android.os; public class Build {public static class VERSION_CODES { public static final int CUPCAKE = 3;}; }" > out/android/os/Build.java +echo "package android.hardware; import android.os.ParcelFileDescriptor; public class SyncFence { public static SyncFence create(ParcelFileDescriptor w) { return null; } public static SyncFence createEmpty() { return null; } }" > out/android/hardware/SyncFence.java +echo "package android.os; public class Build {public static class VERSION_CODES { public static final int CUPCAKE = 0; public static final int R = 0; }; }" > out/android/os/Build.java +echo "package android.os; public class ParcelFileDescriptor { public static ParcelFileDescriptor adoptFd(int fd) { return null; } }" > out/android/os/ParcelFileDescriptor.java echo "package android.os; public class UserHandle {public static String myUserId() { return \"\"; } }" > out/android/os/UserHandle.java echo "package android.os; public class RemoteException extends Exception {}" > out/android/os/RemoteException.java -echo "package android.util; public class Log {public static void w(String a, String b) {} public static void e(String a, String b) {}}" > out/android/util/Log.java +echo "package android.util; public class Log {public static void d(String a, String b) {} public static void w(String a, String b) {} public static void e(String a, String b) {}}" > out/android/util/Log.java echo "package android.opengl; public abstract class EGLObjectHandle { public int getHandle() { return 0; } }" > out/android/opengl/EGLObjectHandle.java - echo "package android.graphics;" > out/android/graphics/SurfaceTexture.java echo "public interface SurfaceTexture {}" >> out/android/graphics/SurfaceTexture.java echo "package android.view;" > out/android/view/SurfaceView.java diff --git a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if index 951ecffc32..695b571a92 100644 --- a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if +++ b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if @@ -20,6 +20,7 @@ package android.opengl; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.SurfaceTexture; +import android.os.Build; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; diff --git a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp index b2ea041cd5..ea55179bbd 100644 --- a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp +++ b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp @@ -17,6 +17,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include "jni.h" diff --git a/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp index 6dffac5945..8e452fb9cd 100644 --- a/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp +++ b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp @@ -17,6 +17,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include "jni.h" diff --git a/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if b/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if index 523bc574be..75e17044c6 100644 --- a/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if +++ b/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if @@ -18,6 +18,11 @@ package android.opengl; +import android.annotation.NonNull; +import android.hardware.SyncFence; +import android.os.ParcelFileDescriptor; +import android.util.Log; + /** * EGL Extensions */ @@ -30,8 +35,44 @@ public class EGLExt { public static final int EGL_OPENGL_ES3_BIT_KHR = 0x0040; public static final int EGL_RECORDABLE_ANDROID = 0x3142; + // EGL_ANDROID_native_fence_sync + public static final int EGL_SYNC_NATIVE_FENCE_ANDROID = 0x3144; + public static final int EGL_SYNC_NATIVE_FENCE_FD_ANDROID = 0x3145; + public static final int EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID = 0x3146; + public static final int EGL_NO_NATIVE_FENCE_FD_ANDROID = -1; + native private static void _nativeClassInit(); static { _nativeClassInit(); } + /** + * Retrieves the SyncFence for an EGLSync created with EGL_SYNC_NATIVE_FENCE_ANDROID + * + * See <a href="https://www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_native_fence_sync.txt"> + * EGL_ANDROID_native_fence_sync</a> extension for more details + * @param display The EGLDisplay connection + * @param sync The EGLSync to fetch the SyncFence from + * @return A SyncFence representing the native fence. + * * If <sync> is not a valid sync object for <display>, + * an {@link SyncFence#isValid() invalid} SyncFence is returned and an EGL_BAD_PARAMETER + * error is generated. + * * If the EGL_SYNC_NATIVE_FENCE_FD_ANDROID attribute of <sync> is + * EGL_NO_NATIVE_FENCE_FD_ANDROID, an {@link SyncFence#isValid() invalid} SyncFence is + * returned and an EGL_BAD_PARAMETER error is generated. + * * If <display> does not match the display passed to eglCreateSync + * when <sync> was created, the behaviour is undefined. + */ + public static @NonNull SyncFence eglDupNativeFenceFDANDROID(@NonNull EGLDisplay display, + @NonNull EGLSync sync) { + int fd = eglDupNativeFenceFDANDROIDImpl(display, sync); + Log.d("EGL", "eglDupNativeFence returned " + fd); + if (fd >= 0) { + return SyncFence.create(ParcelFileDescriptor.adoptFd(fd)); + } else { + return SyncFence.createEmpty(); + } + } + + private static native int eglDupNativeFenceFDANDROIDImpl(EGLDisplay display, EGLSync sync); + diff --git a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp index be8b3e3977..f1f0ac574b 100644 --- a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp +++ b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp @@ -17,6 +17,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include "jni.h" @@ -37,25 +38,12 @@ #include <ui/ANativeObjectBase.h> static jclass egldisplayClass; -static jclass eglcontextClass; static jclass eglsurfaceClass; -static jclass eglconfigClass; +static jclass eglsyncClass; static jmethodID egldisplayGetHandleID; -static jmethodID eglcontextGetHandleID; static jmethodID eglsurfaceGetHandleID; -static jmethodID eglconfigGetHandleID; - -static jmethodID egldisplayConstructor; -static jmethodID eglcontextConstructor; -static jmethodID eglsurfaceConstructor; -static jmethodID eglconfigConstructor; - -static jobject eglNoContextObject; -static jobject eglNoDisplayObject; -static jobject eglNoSurfaceObject; - - +static jmethodID eglsyncGetHandleID; /* Cache method IDs each time the class is loaded. */ @@ -64,37 +52,14 @@ nativeClassInit(JNIEnv *_env, jclass glImplClass) { jclass egldisplayClassLocal = _env->FindClass("android/opengl/EGLDisplay"); egldisplayClass = (jclass) _env->NewGlobalRef(egldisplayClassLocal); - jclass eglcontextClassLocal = _env->FindClass("android/opengl/EGLContext"); - eglcontextClass = (jclass) _env->NewGlobalRef(eglcontextClassLocal); jclass eglsurfaceClassLocal = _env->FindClass("android/opengl/EGLSurface"); eglsurfaceClass = (jclass) _env->NewGlobalRef(eglsurfaceClassLocal); - jclass eglconfigClassLocal = _env->FindClass("android/opengl/EGLConfig"); - eglconfigClass = (jclass) _env->NewGlobalRef(eglconfigClassLocal); + jclass eglsyncClassLocal = _env->FindClass("android/opengl/EGLSync"); + eglsyncClass = (jclass) _env->NewGlobalRef(eglsyncClassLocal); egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J"); - eglcontextGetHandleID = _env->GetMethodID(eglcontextClass, "getNativeHandle", "()J"); eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J"); - eglconfigGetHandleID = _env->GetMethodID(eglconfigClass, "getNativeHandle", "()J"); - - - egldisplayConstructor = _env->GetMethodID(egldisplayClass, "<init>", "(J)V"); - eglcontextConstructor = _env->GetMethodID(eglcontextClass, "<init>", "(J)V"); - eglsurfaceConstructor = _env->GetMethodID(eglsurfaceClass, "<init>", "(J)V"); - eglconfigConstructor = _env->GetMethodID(eglconfigClass, "<init>", "(J)V"); - - - jclass eglClass = _env->FindClass("android/opengl/EGL14"); - jfieldID noContextFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;"); - jobject localeglNoContextObject = _env->GetStaticObjectField(eglClass, noContextFieldID); - eglNoContextObject = _env->NewGlobalRef(localeglNoContextObject); - - jfieldID noDisplayFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_DISPLAY", "Landroid/opengl/EGLDisplay;"); - jobject localeglNoDisplayObject = _env->GetStaticObjectField(eglClass, noDisplayFieldID); - eglNoDisplayObject = _env->NewGlobalRef(localeglNoDisplayObject); - - jfieldID noSurfaceFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_SURFACE", "Landroid/opengl/EGLSurface;"); - jobject localeglNoSurfaceObject = _env->GetStaticObjectField(eglClass, noSurfaceFieldID); - eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject); + eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J"); } static void * @@ -108,24 +73,12 @@ fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) { return reinterpret_cast<void*>(_env->CallLongMethod(obj, mid)); } -static jobject -toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void * handle) { - if (cls == eglcontextClass && - (EGLContext)handle == EGL_NO_CONTEXT) { - return eglNoContextObject; - } - - if (cls == egldisplayClass && - (EGLDisplay)handle == EGL_NO_DISPLAY) { - return eglNoDisplayObject; - } - - if (cls == eglsurfaceClass && - (EGLSurface)handle == EGL_NO_SURFACE) { - return eglNoSurfaceObject; - } +// TODO: this should be generated from the .spec file, but needs to be renamed and made private +static jint android_eglDupNativeFenceFDANDROID(JNIEnv *env, jobject, jobject dpy, jobject sync) { + EGLDisplay dpy_native = (EGLDisplay)fromEGLHandle(env, egldisplayGetHandleID, dpy); + EGLSync sync_native = (EGLSync)fromEGLHandle(env, eglsyncGetHandleID, sync); - return _env->NewObject(cls, con, reinterpret_cast<jlong>(handle)); + return eglDupNativeFenceFDANDROID(dpy_native, sync_native); } // -------------------------------------------------------------------------- diff --git a/opengl/tools/glgen/stubs/egl/eglGetDisplay.java b/opengl/tools/glgen/stubs/egl/eglGetDisplay.java index 85f743d815..78b0819374 100755 --- a/opengl/tools/glgen/stubs/egl/eglGetDisplay.java +++ b/opengl/tools/glgen/stubs/egl/eglGetDisplay.java @@ -7,7 +7,7 @@ /** * {@hide} */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static native EGLDisplay eglGetDisplay( long display_id ); diff --git a/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp index dd17ca458a..1fa92758ad 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp @@ -18,6 +18,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <GLES/gl.h> diff --git a/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp index dd17ca458a..1fa92758ad 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp @@ -18,6 +18,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <GLES/gl.h> diff --git a/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp index dd17ca458a..1fa92758ad 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp @@ -18,6 +18,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <GLES/gl.h> diff --git a/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp index dd17ca458a..1fa92758ad 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp @@ -18,6 +18,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <GLES/gl.h> diff --git a/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp index b2bbdf6fe9..4004a7dfcf 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp @@ -18,6 +18,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <GLES2/gl2.h> diff --git a/opengl/tools/glgen/stubs/gles11/GLES30cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES30cHeader.cpp index b039bc9a83..c5bdf323fc 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES30cHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES30cHeader.cpp @@ -18,6 +18,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <GLES3/gl3.h> diff --git a/opengl/tools/glgen/stubs/gles11/GLES31ExtcHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES31ExtcHeader.cpp index dd00e9205c..2260a80bb4 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES31ExtcHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES31ExtcHeader.cpp @@ -17,6 +17,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <GLES3/gl31.h> diff --git a/opengl/tools/glgen/stubs/gles11/GLES31cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES31cHeader.cpp index 88e00bef6e..130612d7cc 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES31cHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES31cHeader.cpp @@ -17,6 +17,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <stdint.h> diff --git a/opengl/tools/glgen/stubs/gles11/GLES32cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES32cHeader.cpp index 3e7ec8b2de..5446fc2fc3 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES32cHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES32cHeader.cpp @@ -17,6 +17,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <stdint.h> diff --git a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp index 9cab1d6a59..c3534bff31 100644 --- a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp +++ b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp @@ -17,6 +17,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include "jni.h" diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index a7955cff84..d244b1abc4 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -14,6 +14,7 @@ // Default flags to be used throughout all libraries in inputflinger. package { + default_team: "trendy_team_input_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" @@ -110,6 +111,7 @@ cc_defaults { ], static_libs: [ "libattestation", + "libperfetto_client_experimental", "libpalmrejection", "libui-types", ], diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 4863513eb5..823df67d16 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -260,13 +260,16 @@ void InputManager::dump(std::string& dump) { } // Used by tests only. -binder::Status InputManager::createInputChannel(const std::string& name, InputChannel* outChannel) { +binder::Status InputManager::createInputChannel(const std::string& name, + android::os::InputChannelCore* outChannel) { IPCThreadState* ipc = IPCThreadState::self(); - const int uid = ipc->getCallingUid(); + const uid_t uid = ipc->getCallingUid(); if (uid != AID_SHELL && uid != AID_ROOT) { - ALOGE("Invalid attempt to register input channel over IPC" - "from non shell/root entity (PID: %d)", ipc->getCallingPid()); - return binder::Status::ok(); + LOG(ERROR) << __func__ << " can only be called by SHELL or ROOT users, " + << "but was called from UID " << uid; + return binder::Status:: + fromExceptionCode(EX_SECURITY, + "This uid is not allowed to call createInputChannel"); } base::Result<std::unique_ptr<InputChannel>> channel = mDispatcher->createInputChannel(name); diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index df944ef807..c479aaf9b6 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -136,7 +136,8 @@ public: void dump(std::string& dump) override; status_t dump(int fd, const Vector<String16>& args) override; - binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override; + binder::Status createInputChannel(const std::string& name, + android::os::InputChannelCore* outChannel) override; binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override; binder::Status setFocusedWindow(const gui::FocusRequest&) override; diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp index e200f8b303..2d1257478d 100644 --- a/services/inputflinger/benchmarks/Android.bp +++ b/services/inputflinger/benchmarks/Android.bp @@ -1,4 +1,5 @@ package { + default_team: "trendy_team_input_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index c7baceea70..6d71acc775 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_input_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" @@ -49,6 +50,7 @@ filegroup { "Monitor.cpp", "TouchedWindow.cpp", "TouchState.cpp", + "trace/*.cpp", ], } @@ -72,6 +74,7 @@ cc_defaults { static_libs: [ "libattestation", "libgui_window_info_static", + "libperfetto_client_experimental", ], target: { android: { diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp index f304712e8c..c7963c0512 100644 --- a/services/inputflinger/dispatcher/Connection.cpp +++ b/services/inputflinger/dispatcher/Connection.cpp @@ -23,19 +23,12 @@ namespace android::inputdispatcher { Connection::Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor, const IdGenerator& idGenerator) : status(Status::NORMAL), - inputChannel(inputChannel), monitor(monitor), inputPublisher(inputChannel), inputState(idGenerator) {} -const std::string Connection::getWindowName() const { - if (inputChannel != nullptr) { - return inputChannel->getName(); - } - if (monitor) { - return "monitor"; - } - return "?"; -} +sp<IBinder> Connection::getToken() const { + return inputPublisher.getChannel()->getConnectionToken(); +}; } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h index c17baea0e5..8d7f182a7d 100644 --- a/services/inputflinger/dispatcher/Connection.h +++ b/services/inputflinger/dispatcher/Connection.h @@ -42,7 +42,6 @@ public: }; Status status; - std::shared_ptr<InputChannel> inputChannel; // never null bool monitor; InputPublisher inputPublisher; InputState inputState; @@ -62,9 +61,11 @@ public: Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor, const IdGenerator& idGenerator); - inline const std::string getInputChannelName() const { return inputChannel->getName(); } + inline const std::string getInputChannelName() const { + return inputPublisher.getChannel()->getName(); + } - const std::string getWindowName() const; + sp<IBinder> getToken() const; }; } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 2153d8a3f5..264dc03e70 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -282,9 +282,10 @@ std::string SensorEntry::getDescription() const { volatile int32_t DispatchEntry::sNextSeqAtomic; DispatchEntry::DispatchEntry(std::shared_ptr<const EventEntry> eventEntry, - ftl::Flags<InputTarget::Flags> targetFlags, + ftl::Flags<InputTargetFlags> targetFlags, const ui::Transform& transform, const ui::Transform& rawTransform, - float globalScaleFactor) + float globalScaleFactor, gui::Uid targetUid, int64_t vsyncId, + std::optional<int32_t> windowId) : seq(nextSeq()), eventEntry(std::move(eventEntry)), targetFlags(targetFlags), @@ -292,7 +293,10 @@ DispatchEntry::DispatchEntry(std::shared_ptr<const EventEntry> eventEntry, rawTransform(rawTransform), globalScaleFactor(globalScaleFactor), deliveryTime(0), - resolvedFlags(0) { + resolvedFlags(0), + targetUid(targetUid), + vsyncId(vsyncId), + windowId(windowId) { switch (this->eventEntry->type) { case EventEntry::Type::KEY: { const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*this->eventEntry); diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index a915805182..1298b5d511 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -17,7 +17,7 @@ #pragma once #include "InjectionState.h" -#include "InputTarget.h" +#include "InputTargetFlags.h" #include "trace/EventTrackerInterface.h" #include <gui/InputApplication.h> @@ -215,7 +215,7 @@ struct DispatchEntry { const uint32_t seq; // unique sequence number, never 0 std::shared_ptr<const EventEntry> eventEntry; // the event to dispatch - const ftl::Flags<InputTarget::Flags> targetFlags; + const ftl::Flags<InputTargetFlags> targetFlags; ui::Transform transform; ui::Transform rawTransform; float globalScaleFactor; @@ -227,17 +227,27 @@ struct DispatchEntry { int32_t resolvedFlags; + // Information about the dispatch window used for tracing. We avoid holding a window handle + // here because information in a window handle may be dynamically updated within the lifespan + // of this dispatch entry. + gui::Uid targetUid; + int64_t vsyncId; + // The window that this event is targeting. The only case when this windowId is not populated + // is when dispatching an event to a global monitor. + std::optional<int32_t> windowId; + DispatchEntry(std::shared_ptr<const EventEntry> eventEntry, - ftl::Flags<InputTarget::Flags> targetFlags, const ui::Transform& transform, - const ui::Transform& rawTransform, float globalScaleFactor); + ftl::Flags<InputTargetFlags> targetFlags, const ui::Transform& transform, + const ui::Transform& rawTransform, float globalScaleFactor, gui::Uid targetUid, + int64_t vsyncId, std::optional<int32_t> windowId); DispatchEntry(const DispatchEntry&) = delete; DispatchEntry& operator=(const DispatchEntry&) = delete; inline bool hasForegroundTarget() const { - return targetFlags.test(InputTarget::Flags::FOREGROUND); + return targetFlags.test(InputTargetFlags::FOREGROUND); } - inline bool isSplit() const { return targetFlags.test(InputTarget::Flags::SPLIT); } + inline bool isSplit() const { return targetFlags.test(InputTargetFlags::SPLIT); } private: static volatile int32_t sNextSeqAtomic; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index c349a5857f..a62e23a930 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -35,6 +35,7 @@ #include <input/PrintTools.h> #include <input/TraceTools.h> #include <openssl/mem.h> +#include <private/android_filesystem_config.h> #include <unistd.h> #include <utils/Trace.h> @@ -51,6 +52,8 @@ #include "Connection.h" #include "DebugConfig.h" #include "InputDispatcher.h" +#include "trace/InputTracer.h" +#include "trace/InputTracingPerfettoBackend.h" #define INDENT " " #define INDENT2 " " @@ -75,6 +78,14 @@ namespace android::inputdispatcher { namespace { +// Input tracing is only available on debuggable builds (userdebug and eng) when the feature +// flag is enabled. When the flag is changed, tracing will only be available after reboot. +bool isInputTracingEnabled() { + static const std::string buildType = base::GetProperty("ro.build.type", "user"); + static const bool isUserdebugOrEng = buildType == "userdebug" || buildType == "eng"; + return input_flags::enable_input_event_tracing() && isUserdebugOrEng; +} + template <class Entry> void ensureEventTraced(const Entry& entry) { if (!entry.traceTracker) { @@ -359,14 +370,22 @@ size_t firstMarkedBit(T set) { return i; } -std::unique_ptr<DispatchEntry> createDispatchEntry( - const InputTarget& inputTarget, std::shared_ptr<const EventEntry> eventEntry, - ftl::Flags<InputTarget::Flags> inputTargetFlags) { +std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget, + std::shared_ptr<const EventEntry> eventEntry, + ftl::Flags<InputTarget::Flags> inputTargetFlags, + int64_t vsyncId) { + const sp<WindowInfoHandle> win = inputTarget.windowHandle; + const std::optional<int32_t> windowId = + win ? std::make_optional(win->getInfo()->id) : std::nullopt; + // Assume the only targets that are not associated with a window are global monitors, and use + // the system UID for global monitors for tracing purposes. + const gui::Uid uid = win ? win->getInfo()->ownerUid : gui::Uid(AID_SYSTEM); if (inputTarget.useDefaultPointerTransform()) { const ui::Transform& transform = inputTarget.getDefaultPointerTransform(); return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform, inputTarget.displayTransform, - inputTarget.globalScaleFactor); + inputTarget.globalScaleFactor, uid, vsyncId, + windowId); } ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); @@ -413,7 +432,7 @@ std::unique_ptr<DispatchEntry> createDispatchEntry( std::unique_ptr<DispatchEntry> dispatchEntry = std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags, firstPointerTransform, inputTarget.displayTransform, - inputTarget.globalScaleFactor); + inputTarget.globalScaleFactor, uid, vsyncId, windowId); return dispatchEntry; } @@ -709,7 +728,7 @@ void filterUntrustedTargets(TouchState& touchState, std::vector<InputTarget>& ta // TODO(b/282025641): simplify this code once InputTargets are being identified // separately from TouchedWindows. std::erase_if(targets, [&](const InputTarget& target) { - return target.inputChannel->getConnectionToken() == window.windowHandle->getToken(); + return target.connection->getToken() == window.windowHandle->getToken(); }); return true; } @@ -804,7 +823,9 @@ int32_t getUserActivityEventType(const EventEntry& eventEntry) { // --- InputDispatcher --- InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy) - : InputDispatcher(policy, nullptr) {} + : InputDispatcher(policy, + isInputTracingEnabled() ? std::make_unique<trace::impl::PerfettoBackend>() + : nullptr) {} InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, std::unique_ptr<trace::InputTracingBackendInterface> traceBackend) @@ -833,7 +854,7 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, mKeyRepeatState.lastKeyEntry = nullptr; if (traceBackend) { - // TODO: Create input tracer instance. + mTracer = std::make_unique<trace::impl::InputTracer>(std::move(traceBackend)); } mLastUserActivityTimes.fill(0); @@ -849,7 +870,7 @@ InputDispatcher::~InputDispatcher() { while (!mConnectionsByToken.empty()) { std::shared_ptr<Connection> connection = mConnectionsByToken.begin()->second; - removeInputChannelLocked(connection->inputChannel->getConnectionToken(), /*notify=*/false); + removeInputChannelLocked(connection->getToken(), /*notify=*/false); } } @@ -969,7 +990,7 @@ nsecs_t InputDispatcher::processAnrsLocked() { } connection->responsive = false; // Stop waking up for this unresponsive connection - mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); + mAnrTracker.eraseToken(connection->getToken()); onAnrLocked(connection); return LLONG_MIN; } @@ -979,8 +1000,7 @@ std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked( if (connection->monitor) { return mMonitorDispatchingTimeout; } - const sp<WindowInfoHandle> window = - getWindowHandleLocked(connection->inputChannel->getConnectionToken()); + const sp<WindowInfoHandle> window = getWindowHandleLocked(connection->getToken()); if (window != nullptr) { return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); } @@ -1602,15 +1622,15 @@ void InputDispatcher::enqueueFocusEventLocked(const sp<IBinder>& windowToken, bo void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<const FocusEntry> entry) { - std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken); - if (channel == nullptr) { - return; // Window has gone away + std::shared_ptr<Connection> connection = getConnectionLocked(entry->connectionToken); + if (connection == nullptr) { + return; // Connection has gone away } InputTarget target; - target.inputChannel = channel; + target.connection = connection; entry->dispatchInProgress = true; std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") + - channel->getName(); + connection->getInputChannelName(); std::string reason = std::string("reason=").append(entry->reason); android_log_event_list(LOGTAG_INPUT_FOCUS) << message << reason << LOG_ID_EVENTS; dispatchEventLocked(currentTime, entry, {target}); @@ -1670,8 +1690,8 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( } } - auto channel = getInputChannelLocked(token); - if (channel == nullptr) { + auto connection = getConnectionLocked(token); + if (connection == nullptr) { // Window has gone away, clean up Pointer Capture state. mWindowTokenWithPointerCapture = nullptr; if (mCurrentPointerCaptureRequest.enable) { @@ -1680,7 +1700,7 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( return; } InputTarget target; - target.inputChannel = channel; + target.connection = connection; entry->dispatchInProgress = true; dispatchEventLocked(currentTime, entry, {target}); @@ -1711,12 +1731,12 @@ std::vector<InputTarget> InputDispatcher::getInputTargetsFromWindowHandlesLocked if (token == nullptr) { continue; } - std::shared_ptr<InputChannel> channel = getInputChannelLocked(token); - if (channel == nullptr) { - continue; // Window has gone away + std::shared_ptr<Connection> connection = getConnectionLocked(token); + if (connection == nullptr) { + continue; // Connection has gone away } InputTarget target; - target.inputChannel = channel; + target.connection = connection; inputTargets.push_back(target); } return inputTargets; @@ -1961,6 +1981,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, isPointerEvent ? CancelationOptions::Mode::CANCEL_POINTER_EVENTS : CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS); CancelationOptions options(mode, "input event injection failed"); + options.displayId = entry->displayId; synthesizeCancelationEventsForMonitorsLocked(options); return true; } @@ -1993,12 +2014,12 @@ void InputDispatcher::enqueueDragEventLocked(const sp<WindowInfoHandle>& windowH void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<const DragEntry> entry) { - std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken); - if (channel == nullptr) { - return; // Window has gone away + std::shared_ptr<Connection> connection = getConnectionLocked(entry->connectionToken); + if (connection == nullptr) { + return; // Connection has gone away } InputTarget target; - target.inputChannel = channel; + target.connection = connection; entry->dispatchInProgress = true; dispatchEventLocked(currentTime, entry, {target}); } @@ -2051,17 +2072,8 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, pokeUserActivityLocked(*eventEntry); for (const InputTarget& inputTarget : inputTargets) { - std::shared_ptr<Connection> connection = - getConnectionLocked(inputTarget.inputChannel->getConnectionToken()); - if (connection != nullptr) { - prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget); - } else { - if (DEBUG_DROPPED_EVENTS_VERBOSE) { - LOG(INFO) << "Dropping event delivery to target with channel " - << inputTarget.inputChannel->getName() - << " because it is no longer registered with the input dispatcher."; - } - } + std::shared_ptr<Connection> connection = inputTarget.connection; + prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget); } } @@ -2072,7 +2084,7 @@ void InputDispatcher::cancelEventsForAnrLocked(const std::shared_ptr<Connection> // sending new pointers to the connection when it blocked, but focused events will continue to // pile up. ALOGW("Canceling events for %s because it is unresponsive", - connection->inputChannel->getName().c_str()); + connection->getInputChannelName().c_str()); if (connection->status == Connection::Status::NORMAL) { CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, "application not responding"); @@ -2255,17 +2267,11 @@ std::vector<Monitor> InputDispatcher::selectResponsiveMonitorsLocked( const std::vector<Monitor>& monitors) const { std::vector<Monitor> responsiveMonitors; std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors), - [this](const Monitor& monitor) REQUIRES(mLock) { - std::shared_ptr<Connection> connection = - getConnectionLocked(monitor.inputChannel->getConnectionToken()); - if (connection == nullptr) { - ALOGE("Could not find connection for monitor %s", - monitor.inputChannel->getName().c_str()); - return false; - } + [](const Monitor& monitor) REQUIRES(mLock) { + std::shared_ptr<Connection> connection = monitor.connection; if (!connection->responsive) { ALOGW("Unresponsive monitor %s will not get the new gesture", - connection->inputChannel->getName().c_str()); + connection->getInputChannelName().c_str()); return false; } return true; @@ -2642,7 +2648,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( for (InputTarget& target : targets) { if (target.dispatchMode == InputTarget::DispatchMode::OUTSIDE) { sp<WindowInfoHandle> targetWindow = - getWindowHandleLocked(target.inputChannel->getConnectionToken()); + getWindowHandleLocked(target.connection->getToken()); if (targetWindow->getInfo()->ownerUid != foregroundWindowUid) { target.flags |= InputTarget::Flags::ZERO_COORDS; } @@ -2840,13 +2846,13 @@ std::optional<InputTarget> InputDispatcher::createInputTargetLocked( const sp<android::gui::WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags, std::optional<nsecs_t> firstDownTimeInTarget) const { - std::shared_ptr<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken()); - if (inputChannel == nullptr) { + std::shared_ptr<Connection> connection = getConnectionLocked(windowHandle->getToken()); + if (connection == nullptr) { ALOGW("Not creating InputTarget for %s, no input channel", windowHandle->getName().c_str()); return {}; } InputTarget inputTarget; - inputTarget.inputChannel = inputChannel; + inputTarget.connection = connection; inputTarget.windowHandle = windowHandle; inputTarget.dispatchMode = dispatchMode; inputTarget.flags = targetFlags; @@ -2870,8 +2876,7 @@ void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHa std::vector<InputTarget>::iterator it = std::find_if(inputTargets.begin(), inputTargets.end(), [&windowHandle](const InputTarget& inputTarget) { - return inputTarget.inputChannel->getConnectionToken() == - windowHandle->getToken(); + return inputTarget.connection->getToken() == windowHandle->getToken(); }); const WindowInfo* windowInfo = windowHandle->getInfo(); @@ -2911,8 +2916,7 @@ void InputDispatcher::addPointerWindowTargetLocked( std::vector<InputTarget>::iterator it = std::find_if(inputTargets.begin(), inputTargets.end(), [&windowHandle](const InputTarget& inputTarget) { - return inputTarget.inputChannel->getConnectionToken() == - windowHandle->getToken(); + return inputTarget.connection->getToken() == windowHandle->getToken(); }); // This is a hack, because the actual entry could potentially be an ACTION_DOWN event that @@ -2962,7 +2966,7 @@ void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& for (const Monitor& monitor : selectResponsiveMonitorsLocked(monitorsIt->second)) { InputTarget target; - target.inputChannel = monitor.inputChannel; + target.connection = monitor.connection; // target.firstDownTimeInTarget is not set for global monitors. It is only required in split // touch and global monitoring works as intended even without setting firstDownTimeInTarget if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) { @@ -3333,10 +3337,11 @@ void InputDispatcher::enqueueDispatchEntryAndStartDispatchCycleLocked( void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connection>& connection, std::shared_ptr<const EventEntry> eventEntry, const InputTarget& inputTarget) { + // TODO(b/210460522): Verify all targets excluding global monitors are associated with a window. // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. std::unique_ptr<DispatchEntry> dispatchEntry = - createDispatchEntry(inputTarget, eventEntry, inputTarget.flags); + createDispatchEntry(inputTarget, eventEntry, inputTarget.flags, mWindowInfosVsyncId); // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a // different EventEntry than what was passed in. @@ -3455,7 +3460,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio << cancelEvent->getDescription(); std::unique_ptr<DispatchEntry> cancelDispatchEntry = createDispatchEntry(inputTarget, std::move(cancelEvent), - ftl::Flags<InputTarget::Flags>()); + ftl::Flags<InputTarget::Flags>(), mWindowInfosVsyncId); // Send these cancel events to the queue before sending the event from the new // device. @@ -3476,7 +3481,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio } dispatchPointerDownOutsideFocus(resolvedMotion->source, resolvedMotion->action, - inputTarget.inputChannel->getConnectionToken()); + inputTarget.connection->getToken()); break; } @@ -3559,13 +3564,9 @@ void InputDispatcher::processInteractionsLocked(const EventEntry& entry, continue; // Skip windows that receive ACTION_OUTSIDE } - sp<IBinder> token = target.inputChannel->getConnectionToken(); - std::shared_ptr<Connection> connection = getConnectionLocked(token); - if (connection == nullptr) { - continue; - } + sp<IBinder> token = target.connection->getToken(); newConnectionTokens.insert(std::move(token)); - newConnections.emplace_back(connection); + newConnections.emplace_back(target.connection); if (target.windowHandle) { interactionUids.emplace(target.windowHandle->getInfo()->ownerUid); } @@ -3585,7 +3586,7 @@ void InputDispatcher::processInteractionsLocked(const EventEntry& entry, std::string targetList; for (const std::shared_ptr<Connection>& connection : newConnections) { - targetList += connection->getWindowName() + ", "; + targetList += connection->getInputChannelName() + ", "; } std::string message = "Interaction with: " + targetList; if (targetList.empty()) { @@ -3799,7 +3800,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, connection->outboundQueue.erase(connection->outboundQueue.begin()); traceOutboundQueueLength(*connection); if (connection->responsive) { - mAnrTracker.insert(timeoutTime, connection->inputChannel->getConnectionToken()); + mAnrTracker.insert(timeoutTime, connection->getToken()); } traceWaitQueueLength(*connection); } @@ -3889,7 +3890,7 @@ void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, auto command = [this, connection]() REQUIRES(mLock) { scoped_unlock unlock(mLock); - mPolicy.notifyInputChannelBroken(connection->inputChannel->getConnectionToken()); + mPolicy.notifyInputChannelBroken(connection->getToken()); }; postCommandLocked(std::move(command)); } @@ -3947,10 +3948,9 @@ int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionTok if (shouldReportMetricsForConnection(*connection)) { const InputPublisher::Timeline& timeline = std::get<InputPublisher::Timeline>(*result); - mLatencyTracker - .trackGraphicsLatency(timeline.inputEventId, - connection->inputChannel->getConnectionToken(), - std::move(timeline.graphicsTimeline)); + mLatencyTracker.trackGraphicsLatency(timeline.inputEventId, + connection->getToken(), + std::move(timeline.graphicsTimeline)); } } gotOne = true; @@ -3971,8 +3971,7 @@ int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionTok } else { // Monitor channels are never explicitly unregistered. // We do it automatically when the remote endpoint is closed so don't warn about them. - const bool stillHaveWindowHandle = - getWindowHandleLocked(connection->inputChannel->getConnectionToken()) != nullptr; + const bool stillHaveWindowHandle = getWindowHandleLocked(connection->getToken()) != nullptr; notify = !connection->monitor && stillHaveWindowHandle; if (notify) { ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. events=0x%x", @@ -3981,7 +3980,7 @@ int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionTok } // Remove the channel. - removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify); + removeInputChannelLocked(connection->getToken(), notify); return 0; // remove the callback } @@ -3996,21 +3995,11 @@ void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( const CancelationOptions& options) { for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) { for (const Monitor& monitor : monitors) { - synthesizeCancelationEventsForInputChannelLocked(monitor.inputChannel, options); + synthesizeCancelationEventsForConnectionLocked(monitor.connection, options); } } } -void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( - const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options) { - std::shared_ptr<Connection> connection = getConnectionLocked(channel->getConnectionToken()); - if (connection == nullptr) { - return; - } - - synthesizeCancelationEventsForConnectionLocked(connection, options); -} - void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( const std::shared_ptr<Connection>& connection, const CancelationOptions& options) { if (connection->status != Connection::Status::NORMAL) { @@ -4039,8 +4028,8 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( const bool wasEmpty = connection->outboundQueue.empty(); // The target to use if we don't find a window associated with the channel. - const InputTarget fallbackTarget{.inputChannel = connection->inputChannel}; - const auto& token = connection->inputChannel->getConnectionToken(); + const InputTarget fallbackTarget{.connection = connection}; + const auto& token = connection->getToken(); for (size_t i = 0; i < cancelationEvents.size(); i++) { std::unique_ptr<EventEntry> cancelationEventEntry = std::move(cancelationEvents[i]); @@ -4141,8 +4130,7 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( connection->getInputChannelName().c_str(), downEvents.size()); } - sp<WindowInfoHandle> windowHandle = - getWindowHandleLocked(connection->inputChannel->getConnectionToken()); + sp<WindowInfoHandle> windowHandle = getWindowHandleLocked(connection->getToken()); const bool wasEmpty = connection->outboundQueue.empty(); for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) { @@ -4160,8 +4148,8 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( targetFlags, pointerIds, motionEntry.downTime, targets); } else { - targets.emplace_back(InputTarget{.inputChannel = connection->inputChannel, - .flags = targetFlags}); + targets.emplace_back( + InputTarget{.connection = connection, .flags = targetFlags}); const auto it = mDisplayInfos.find(motionEntry.displayId); if (it != mDisplayInfos.end()) { targets.back().displayTransform = it->second.transform; @@ -5118,15 +5106,6 @@ bool InputDispatcher::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& w return true; } -std::shared_ptr<InputChannel> InputDispatcher::getInputChannelLocked( - const sp<IBinder>& token) const { - auto connectionIt = mConnectionsByToken.find(token); - if (connectionIt == mConnectionsByToken.end()) { - return nullptr; - } - return connectionIt->second->inputChannel; -} - void InputDispatcher::updateWindowHandlesForDisplayLocked( const std::vector<sp<WindowInfoHandle>>& windowInfoHandles, int32_t displayId) { if (windowInfoHandles.empty()) { @@ -5146,7 +5125,7 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( std::vector<sp<WindowInfoHandle>> newHandles; for (const sp<WindowInfoHandle>& handle : windowInfoHandles) { const WindowInfo* info = handle->getInfo(); - if (getInputChannelLocked(handle->getToken()) == nullptr) { + if (getConnectionLocked(handle->getToken()) == nullptr) { const bool noInputChannel = info->inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL); const bool canReceiveInput = @@ -5245,12 +5224,12 @@ void InputDispatcher::setInputWindowsLocked( if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) { LOG(INFO) << "Touched window was removed: " << touchedWindow.windowHandle->getName() << " in display %" << displayId; - std::shared_ptr<InputChannel> touchedInputChannel = - getInputChannelLocked(touchedWindow.windowHandle->getToken()); - if (touchedInputChannel != nullptr) { + std::shared_ptr<Connection> touchedConnection = + getConnectionLocked(touchedWindow.windowHandle->getToken()); + if (touchedConnection != nullptr) { CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "touched window was removed"); - synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options); + synthesizeCancelationEventsForConnectionLocked(touchedConnection, options); // Since we are about to drop the touch, cancel the events for the wallpaper as // well. if (touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND) && @@ -5355,14 +5334,13 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { sp<IBinder> oldFocusedWindowToken = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId); if (oldFocusedWindowToken != nullptr) { - std::shared_ptr<InputChannel> inputChannel = - getInputChannelLocked(oldFocusedWindowToken); - if (inputChannel != 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; - synthesizeCancelationEventsForInputChannelLocked(inputChannel, options); + synthesizeCancelationEventsForConnectionLocked(connection, options); } } mFocusedDisplayId = displayId; @@ -5825,11 +5803,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const { if (!mConnectionsByToken.empty()) { dump += INDENT "Connections:\n"; for (const auto& [token, connection] : mConnectionsByToken) { - dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', " + dump += StringPrintf(INDENT2 "%i: channelName='%s', " "status=%s, monitor=%s, responsive=%s\n", - connection->inputChannel->getFd().get(), + connection->inputPublisher.getChannel()->getFd(), connection->getInputChannelName().c_str(), - connection->getWindowName().c_str(), ftl::enum_string(connection->status).c_str(), toString(connection->monitor), toString(connection->responsive)); @@ -5883,8 +5860,8 @@ void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor> const size_t numMonitors = monitors.size(); for (size_t i = 0; i < numMonitors; i++) { const Monitor& monitor = monitors[i]; - const std::shared_ptr<InputChannel>& channel = monitor.inputChannel; - dump += StringPrintf(INDENT2 "%zu: '%s', ", i, channel->getName().c_str()); + const std::shared_ptr<Connection>& connection = monitor.connection; + dump += StringPrintf(INDENT2 "%zu: '%s', ", i, connection->getInputChannelName().c_str()); dump += "\n"; } } @@ -5914,20 +5891,20 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const { // acquire lock std::scoped_lock _l(mLock); const sp<IBinder>& token = serverChannel->getConnectionToken(); - auto&& fd = serverChannel->getFd(); + const int fd = serverChannel->getFd(); std::shared_ptr<Connection> connection = std::make_shared<Connection>(std::move(serverChannel), /*monitor=*/false, mIdGenerator); - if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) { + auto [_, inserted] = mConnectionsByToken.try_emplace(token, connection); + if (!inserted) { ALOGE("Created a new connection, but the token %p is already known", token.get()); } - mConnectionsByToken.emplace(token, connection); std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback, this, std::placeholders::_1, token); - mLooper->addFd(fd.get(), 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), + mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), nullptr); } // release lock @@ -5957,18 +5934,19 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_ std::shared_ptr<Connection> connection = std::make_shared<Connection>(serverChannel, /*monitor=*/true, mIdGenerator); const sp<IBinder>& token = serverChannel->getConnectionToken(); - auto&& fd = serverChannel->getFd(); + const int fd = serverChannel->getFd(); - if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) { + auto [_, inserted] = mConnectionsByToken.emplace(token, connection); + if (!inserted) { ALOGE("Created a new connection, but the token %p is already known", token.get()); } - mConnectionsByToken.emplace(token, connection); + std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback, this, std::placeholders::_1, token); - mGlobalMonitorsByDisplay[displayId].emplace_back(serverChannel, pid); + mGlobalMonitorsByDisplay[displayId].emplace_back(connection, pid); - mLooper->addFd(fd.get(), 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), + mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), nullptr); } @@ -6007,7 +5985,7 @@ status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connection removeMonitorChannelLocked(connectionToken); } - mLooper->removeFd(connection->inputChannel->getFd().get()); + mLooper->removeFd(connection->inputPublisher.getChannel()->getFd()); nsecs_t currentTime = now(); abortBrokenDispatchCycleLocked(currentTime, connection, notify); @@ -6020,7 +5998,7 @@ void InputDispatcher::removeMonitorChannelLocked(const sp<IBinder>& connectionTo for (auto it = mGlobalMonitorsByDisplay.begin(); it != mGlobalMonitorsByDisplay.end();) { auto& [displayId, monitors] = *it; std::erase_if(monitors, [connectionToken](const Monitor& monitor) { - return monitor.inputChannel->getConnectionToken() == connectionToken; + return monitor.connection->getToken() == connectionToken; }); if (monitors.empty()) { @@ -6037,8 +6015,8 @@ status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) { } status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { - const std::shared_ptr<InputChannel> requestingChannel = getInputChannelLocked(token); - if (!requestingChannel) { + const std::shared_ptr<Connection> requestingConnection = getConnectionLocked(token); + if (!requestingConnection) { LOG(WARNING) << "Attempted to pilfer pointers from an un-registered channel or invalid token"; return BAD_VALUE; @@ -6071,16 +6049,16 @@ status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { std::string canceledWindows; for (const TouchedWindow& w : state.windows) { - const std::shared_ptr<InputChannel> channel = - getInputChannelLocked(w.windowHandle->getToken()); - if (channel != nullptr && channel->getConnectionToken() != token) { - synthesizeCancelationEventsForInputChannelLocked(channel, options); + const std::shared_ptr<Connection> connection = + getConnectionLocked(w.windowHandle->getToken()); + if (connection != nullptr && connection->getToken() != token) { + synthesizeCancelationEventsForConnectionLocked(connection, options); canceledWindows += canceledWindows.empty() ? "[" : ", "; - canceledWindows += channel->getName(); + canceledWindows += connection->getInputChannelName(); } } canceledWindows += canceledWindows.empty() ? "[]" : "]"; - LOG(INFO) << "Channel " << requestingChannel->getName() + LOG(INFO) << "Channel " << requestingConnection->getInputChannelName() << " is stealing input gesture for device " << deviceId << " from " << canceledWindows; @@ -6146,7 +6124,7 @@ void InputDispatcher::setDisplayEligibilityForPointerCapture(int32_t displayId, std::optional<gui::Pid> InputDispatcher::findMonitorPidByTokenLocked(const sp<IBinder>& token) { for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) { for (const Monitor& monitor : monitors) { - if (monitor.inputChannel->getConnectionToken() == token) { + if (monitor.connection->getToken() == token) { return monitor.pid; } } @@ -6178,8 +6156,8 @@ std::string InputDispatcher::getConnectionNameLocked(const sp<IBinder>& connecti } void InputDispatcher::removeConnectionLocked(const std::shared_ptr<Connection>& connection) { - mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); - mConnectionsByToken.erase(connection->inputChannel->getConnectionToken()); + mAnrTracker.eraseToken(connection->getToken()); + mConnectionsByToken.erase(connection->getToken()); } void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, @@ -6201,12 +6179,11 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, const nsecs_t eventDuration = finishTime - dispatchEntry.deliveryTime; if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { - ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(), + ALOGI("%s spent %" PRId64 "ms processing %s", connection->getInputChannelName().c_str(), ns2ms(eventDuration), dispatchEntry.eventEntry->getDescription().c_str()); } if (shouldReportFinishedEvent(dispatchEntry, *connection)) { - mLatencyTracker.trackFinishedEvent(dispatchEntry.eventEntry->id, - connection->inputChannel->getConnectionToken(), + mLatencyTracker.trackFinishedEvent(dispatchEntry.eventEntry->id, connection->getToken(), dispatchEntry.deliveryTime, consumeTime, finishTime); } @@ -6227,7 +6204,7 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, std::unique_ptr<DispatchEntry> dispatchEntry = std::move(*entryIt); connection->waitQueue.erase(entryIt); - const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken(); + const sp<IBinder>& connectionToken = connection->getToken(); mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken); if (!connection->responsive) { connection->responsive = isConnectionResponsive(*connection); @@ -6238,8 +6215,7 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, } traceWaitQueueLength(*connection); if (fallbackKeyEntry && connection->status == Connection::Status::NORMAL) { - const InputTarget target{.inputChannel = connection->inputChannel, - .flags = dispatchEntry->targetFlags}; + const InputTarget target{.connection = connection, .flags = dispatchEntry->targetFlags}; enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry), target); } releaseDispatchEntry(std::move(dispatchEntry)); @@ -6274,7 +6250,7 @@ void InputDispatcher::onAnrLocked(const std::shared_ptr<Connection>& connection) // is already healthy again. Don't raise ANR in this situation if (connection->waitQueue.empty()) { ALOGI("Not raising ANR because the connection %s has recovered", - connection->inputChannel->getName().c_str()); + connection->getInputChannelName().c_str()); return; } /** @@ -6289,10 +6265,10 @@ void InputDispatcher::onAnrLocked(const std::shared_ptr<Connection>& connection) const nsecs_t currentWait = now() - oldestEntry.deliveryTime; std::string reason = android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s", - connection->inputChannel->getName().c_str(), + connection->getInputChannelName().c_str(), ns2ms(currentWait), oldestEntry.eventEntry->getDescription().c_str()); - sp<IBinder> connectionToken = connection->inputChannel->getConnectionToken(); + sp<IBinder> connectionToken = connection->getToken(); updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason); processConnectionUnresponsiveLocked(*connection, std::move(reason)); @@ -6391,15 +6367,15 @@ void InputDispatcher::sendWindowResponsiveCommandLocked(const sp<IBinder>& token */ void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& connection, std::string reason) { - const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken(); + const sp<IBinder>& connectionToken = connection.getToken(); std::optional<gui::Pid> pid; if (connection.monitor) { - ALOGW("Monitor %s is unresponsive: %s", connection.inputChannel->getName().c_str(), + ALOGW("Monitor %s is unresponsive: %s", connection.getInputChannelName().c_str(), reason.c_str()); pid = findMonitorPidByTokenLocked(connectionToken); } else { // The connection is a window - ALOGW("Window %s is unresponsive: %s", connection.inputChannel->getName().c_str(), + ALOGW("Window %s is unresponsive: %s", connection.getInputChannelName().c_str(), reason.c_str()); const sp<WindowInfoHandle> handle = getWindowHandleLocked(connectionToken); if (handle != nullptr) { @@ -6413,7 +6389,7 @@ void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& conn * Tell the policy that a connection has become responsive so that it can stop ANR. */ void InputDispatcher::processConnectionResponsiveLocked(const Connection& connection) { - const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken(); + const sp<IBinder>& connectionToken = connection.getToken(); std::optional<gui::Pid> pid; if (connection.monitor) { pid = findMonitorPidByTokenLocked(connectionToken); @@ -6464,8 +6440,8 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl mLock.unlock(); if (const auto unhandledKeyFallback = - mPolicy.dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), - event, keyEntry.policyFlags); + mPolicy.dispatchUnhandledKey(connection->getToken(), event, + keyEntry.policyFlags); unhandledKeyFallback) { event = *unhandledKeyFallback; } @@ -6509,8 +6485,8 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl mLock.unlock(); bool fallback = false; - if (auto fb = mPolicy.dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), - event, keyEntry.policyFlags); + if (auto fb = mPolicy.dispatchUnhandledKey(connection->getToken(), event, + keyEntry.policyFlags); fb) { fallback = true; event = *fb; @@ -6618,7 +6594,8 @@ void InputDispatcher::traceInboundQueueLengthLocked() { void InputDispatcher::traceOutboundQueueLength(const Connection& connection) { if (ATRACE_ENABLED()) { char counterName[40]; - snprintf(counterName, sizeof(counterName), "oq:%s", connection.getWindowName().c_str()); + snprintf(counterName, sizeof(counterName), "oq:%s", + connection.getInputChannelName().c_str()); ATRACE_INT(counterName, connection.outboundQueue.size()); } } @@ -6626,7 +6603,8 @@ void InputDispatcher::traceOutboundQueueLength(const Connection& connection) { void InputDispatcher::traceWaitQueueLength(const Connection& connection) { if (ATRACE_ENABLED()) { char counterName[40]; - snprintf(counterName, sizeof(counterName), "wq:%s", connection.getWindowName().c_str()); + snprintf(counterName, sizeof(counterName), "wq:%s", + connection.getInputChannelName().c_str()); ATRACE_INT(counterName, connection.waitQueue.size()); } } @@ -6697,11 +6675,11 @@ void InputDispatcher::setFocusedWindow(const FocusRequest& request) { void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& changes) { if (changes.oldFocus) { - std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(changes.oldFocus); - if (focusedInputChannel) { + std::shared_ptr<Connection> focusedConnection = getConnectionLocked(changes.oldFocus); + if (focusedConnection) { CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, "focus left window"); - synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); + synthesizeCancelationEventsForConnectionLocked(focusedConnection, options); enqueueFocusEventLocked(changes.oldFocus, /*hasFocus=*/false, changes.reason); } } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index e635852662..155d485ae4 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -369,8 +369,6 @@ private: REQUIRES(mLock); sp<android::gui::WindowInfoHandle> getWindowHandleLocked( const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock); - std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const - REQUIRES(mLock); sp<android::gui::WindowInfoHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock); bool canWindowReceiveMotionLocked(const sp<android::gui::WindowInfoHandle>& window, @@ -617,9 +615,6 @@ private: REQUIRES(mLock); void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options) REQUIRES(mLock); - void synthesizeCancelationEventsForInputChannelLocked( - const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options) - REQUIRES(mLock); void synthesizeCancelationEventsForConnectionLocked( const std::shared_ptr<Connection>& connection, const CancelationOptions& options) REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp index c02c5d67a2..28e3c6d69a 100644 --- a/services/inputflinger/dispatcher/InputTarget.cpp +++ b/services/inputflinger/dispatcher/InputTarget.cpp @@ -83,9 +83,9 @@ std::string InputTarget::getPointerInfoString() const { } std::ostream& operator<<(std::ostream& out, const InputTarget& target) { - out << "{inputChannel="; - if (target.inputChannel != nullptr) { - out << target.inputChannel->getName(); + out << "{connection="; + if (target.connection != nullptr) { + out << target.connection->getInputChannelName(); } else { out << "<null>"; } diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index aef866baaf..5728bdf1e8 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -19,10 +19,11 @@ #include <ftl/flags.h> #include <gui/WindowInfo.h> #include <gui/constants.h> -#include <input/InputTransport.h> #include <ui/Transform.h> #include <utils/BitSet.h> #include <bitset> +#include "Connection.h" +#include "InputTargetFlags.h" namespace android::inputdispatcher { @@ -33,29 +34,7 @@ namespace android::inputdispatcher { * window area. */ struct InputTarget { - enum class Flags : uint32_t { - /* This flag indicates that the event is being delivered to a foreground application. */ - FOREGROUND = 1 << 0, - - /* This flag indicates that the MotionEvent falls within the area of the target - * obscured by another visible window above it. The motion event should be - * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ - WINDOW_IS_OBSCURED = 1 << 1, - - /* This flag indicates that a motion event is being split across multiple windows. */ - SPLIT = 1 << 2, - - /* This flag indicates that the pointer coordinates dispatched to the application - * will be zeroed out to avoid revealing information to an application. This is - * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing - * the same UID from watching all touches. */ - ZERO_COORDS = 1 << 3, - - /* This flag indicates that the target of a MotionEvent is partly or wholly - * obscured by another visible window above it. The motion event should be - * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */ - WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14, - }; + using Flags = InputTargetFlags; enum class DispatchMode { /* This flag indicates that the event should be sent as is. @@ -85,8 +64,8 @@ struct InputTarget { ftl_last = SLIPPERY_ENTER, }; - // The input channel to be targeted. - std::shared_ptr<InputChannel> inputChannel; + // The input connection to be targeted. + std::shared_ptr<Connection> connection; // Flags for the input target. ftl::Flags<Flags> flags; diff --git a/services/inputflinger/dispatcher/InputTargetFlags.h b/services/inputflinger/dispatcher/InputTargetFlags.h new file mode 100644 index 0000000000..7497543bd4 --- /dev/null +++ b/services/inputflinger/dispatcher/InputTargetFlags.h @@ -0,0 +1,47 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <ftl/flags.h> + +namespace android::inputdispatcher { + +enum class InputTargetFlags : uint32_t { + /* This flag indicates that the event is being delivered to a foreground application. */ + FOREGROUND = 1 << 0, + + /* This flag indicates that the MotionEvent falls within the area of the target + * obscured by another visible window above it. The motion event should be + * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ + WINDOW_IS_OBSCURED = 1 << 1, + + /* This flag indicates that a motion event is being split across multiple windows. */ + SPLIT = 1 << 2, + + /* This flag indicates that the pointer coordinates dispatched to the application + * will be zeroed out to avoid revealing information to an application. This is + * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing + * the same UID from watching all touches. */ + ZERO_COORDS = 1 << 3, + + /* This flag indicates that the target of a MotionEvent is partly or wholly + * obscured by another visible window above it. The motion event should be + * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */ + WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14, +}; + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp index 204791eb03..4e77d905aa 100644 --- a/services/inputflinger/dispatcher/Monitor.cpp +++ b/services/inputflinger/dispatcher/Monitor.cpp @@ -19,7 +19,7 @@ namespace android::inputdispatcher { // --- Monitor --- -Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel, gui::Pid pid) - : inputChannel(inputChannel), pid(pid) {} +Monitor::Monitor(const std::shared_ptr<Connection>& connection, gui::Pid pid) + : connection(connection), pid(pid) {} } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h index 1b1eb3a593..d15a222752 100644 --- a/services/inputflinger/dispatcher/Monitor.h +++ b/services/inputflinger/dispatcher/Monitor.h @@ -17,16 +17,16 @@ #pragma once #include <gui/PidUid.h> -#include <input/InputTransport.h> +#include "Connection.h" namespace android::inputdispatcher { struct Monitor { - std::shared_ptr<InputChannel> inputChannel; // never null + std::shared_ptr<Connection> connection; // never null gui::Pid pid; - explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel, gui::Pid pid); + explicit Monitor(const std::shared_ptr<Connection>& connection, gui::Pid pid); }; } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp new file mode 100644 index 0000000000..a61fa85d6e --- /dev/null +++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp @@ -0,0 +1,109 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AndroidInputEventProtoConverter.h" + +#include <android-base/logging.h> +#include <perfetto/trace/android/android_input_event.pbzero.h> + +namespace android::inputdispatcher::trace { + +void AndroidInputEventProtoConverter::toProtoMotionEvent(const TracedMotionEvent& event, + proto::AndroidMotionEvent& outProto) { + outProto.set_event_id(event.id); + outProto.set_event_time_nanos(event.eventTime); + outProto.set_down_time_nanos(event.downTime); + outProto.set_source(event.source); + outProto.set_action(event.action); + outProto.set_device_id(event.deviceId); + outProto.set_display_id(event.displayId); + outProto.set_classification(static_cast<int32_t>(event.classification)); + outProto.set_cursor_position_x(event.xCursorPosition); + outProto.set_cursor_position_y(event.yCursorPosition); + outProto.set_flags(event.flags); + outProto.set_policy_flags(event.policyFlags); + + for (uint32_t i = 0; i < event.pointerProperties.size(); i++) { + auto* pointer = outProto.add_pointer(); + + const auto& props = event.pointerProperties[i]; + pointer->set_pointer_id(props.id); + pointer->set_tool_type(static_cast<int32_t>(props.toolType)); + + const auto& coords = event.pointerCoords[i]; + auto bits = BitSet64(coords.bits); + for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) { + const auto axis = bits.clearFirstMarkedBit(); + auto axisEntry = pointer->add_axis_value(); + axisEntry->set_axis(axis); + axisEntry->set_value(coords.values[axisIndex]); + } + } +} + +void AndroidInputEventProtoConverter::toProtoKeyEvent(const TracedKeyEvent& event, + proto::AndroidKeyEvent& outProto) { + outProto.set_event_id(event.id); + outProto.set_event_time_nanos(event.eventTime); + outProto.set_down_time_nanos(event.downTime); + outProto.set_source(event.source); + outProto.set_action(event.action); + outProto.set_device_id(event.deviceId); + outProto.set_display_id(event.displayId); + outProto.set_key_code(event.keyCode); + outProto.set_scan_code(event.scanCode); + outProto.set_meta_state(event.metaState); + outProto.set_repeat_count(event.repeatCount); + outProto.set_flags(event.flags); + outProto.set_policy_flags(event.policyFlags); +} + +void AndroidInputEventProtoConverter::toProtoWindowDispatchEvent( + const InputTracingBackendInterface::WindowDispatchArgs& args, + proto::AndroidWindowInputDispatchEvent& outProto) { + std::visit([&](auto entry) { outProto.set_event_id(entry.id); }, args.eventEntry); + outProto.set_vsync_id(args.vsyncId); + outProto.set_window_id(args.windowId); + outProto.set_resolved_flags(args.resolvedFlags); + + if (auto* motion = std::get_if<TracedMotionEvent>(&args.eventEntry); motion != nullptr) { + for (size_t i = 0; i < motion->pointerProperties.size(); i++) { + auto* pointerProto = outProto.add_dispatched_pointer(); + pointerProto->set_pointer_id(motion->pointerProperties[i].id); + const auto rawXY = + MotionEvent::calculateTransformedXY(motion->source, args.rawTransform, + motion->pointerCoords[i].getXYValue()); + pointerProto->set_x_in_display(rawXY.x); + pointerProto->set_y_in_display(rawXY.y); + + const auto& coords = motion->pointerCoords[i]; + const auto coordsInWindow = + MotionEvent::calculateTransformedCoords(motion->source, args.transform, coords); + auto bits = BitSet64(coords.bits); + for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) { + const uint32_t axis = bits.clearFirstMarkedBit(); + const float axisValueInWindow = coordsInWindow.values[axisIndex]; + if (coords.values[axisIndex] != axisValueInWindow) { + auto* axisEntry = pointerProto->add_axis_value_in_window(); + axisEntry->set_axis(axis); + axisEntry->set_value(axisValueInWindow); + } + } + } + } +} + +} // namespace android::inputdispatcher::trace diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h new file mode 100644 index 0000000000..8a46f1518b --- /dev/null +++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h @@ -0,0 +1,39 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <perfetto/trace/android/android_input_event.pbzero.h> + +#include "InputTracingBackendInterface.h" + +namespace proto = perfetto::protos::pbzero; + +namespace android::inputdispatcher::trace { + +/** + * Write traced events into Perfetto protos. + */ +class AndroidInputEventProtoConverter { +public: + static void toProtoMotionEvent(const TracedMotionEvent& event, + proto::AndroidMotionEvent& outProto); + static void toProtoKeyEvent(const TracedKeyEvent& event, proto::AndroidKeyEvent& outProto); + static void toProtoWindowDispatchEvent(const InputTracingBackendInterface::WindowDispatchArgs&, + proto::AndroidWindowInputDispatchEvent& outProto); +}; + +} // namespace android::inputdispatcher::trace diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp new file mode 100644 index 0000000000..b065729873 --- /dev/null +++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp @@ -0,0 +1,222 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InputTracer" + +#include "InputTracer.h" + +#include <android-base/logging.h> +#include <utils/AndroidThreads.h> + +namespace android::inputdispatcher::trace::impl { + +namespace { + +TracedEvent createTracedEvent(const MotionEntry& e) { + return TracedMotionEvent{e.id, + e.eventTime, + e.policyFlags, + e.deviceId, + e.source, + e.displayId, + e.action, + e.actionButton, + e.flags, + e.metaState, + e.buttonState, + e.classification, + e.edgeFlags, + e.xPrecision, + e.yPrecision, + e.xCursorPosition, + e.yCursorPosition, + e.downTime, + e.pointerProperties, + e.pointerCoords}; +} + +TracedEvent createTracedEvent(const KeyEntry& e) { + return TracedKeyEvent{e.id, e.eventTime, e.policyFlags, e.deviceId, e.source, + e.displayId, e.action, e.keyCode, e.scanCode, e.metaState, + e.downTime, e.flags, e.repeatCount}; +} + +} // namespace + +// --- InputTracer --- + +InputTracer::InputTracer(std::unique_ptr<InputTracingBackendInterface> backend) + : mTracerThread(&InputTracer::threadLoop, this), mBackend(std::move(backend)) {} + +InputTracer::~InputTracer() { + { + std::scoped_lock lock(mLock); + mThreadExit = true; + } + mThreadWakeCondition.notify_all(); + mTracerThread.join(); +} + +std::unique_ptr<EventTrackerInterface> InputTracer::traceInboundEvent(const EventEntry& entry) { + std::scoped_lock lock(mLock); + TracedEvent traced; + + if (entry.type == EventEntry::Type::MOTION) { + const auto& motion = static_cast<const MotionEntry&>(entry); + traced = createTracedEvent(motion); + } else if (entry.type == EventEntry::Type::KEY) { + const auto& key = static_cast<const KeyEntry&>(entry); + traced = createTracedEvent(key); + } else { + LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type); + } + + return std::make_unique<EventTrackerImpl>(*this, std::move(traced)); +} + +void InputTracer::dispatchToTargetHint(const EventTrackerInterface& cookie, + const InputTarget& target) { + std::scoped_lock lock(mLock); + auto& cookieState = getState(cookie); + if (!cookieState) { + LOG(FATAL) << "dispatchToTargetHint() should not be called after eventProcessingComplete()"; + } + // TODO(b/210460522): Determine if the event is sensitive based on the target. +} + +void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie) { + { + std::scoped_lock lock(mLock); + auto& cookieState = getState(cookie); + if (!cookieState) { + LOG(FATAL) << "Traced event was already logged. " + "eventProcessingComplete() was likely called more than once."; + } + mTraceQueue.emplace_back(std::move(*cookieState)); + cookieState.reset(); + } // release lock + + mThreadWakeCondition.notify_all(); +} + +void InputTracer::traceEventDispatch(const DispatchEntry& dispatchEntry, + const EventTrackerInterface* cookie) { + { + std::scoped_lock lock(mLock); + const EventEntry& entry = *dispatchEntry.eventEntry; + + TracedEvent traced; + if (entry.type == EventEntry::Type::MOTION) { + const auto& motion = static_cast<const MotionEntry&>(entry); + traced = createTracedEvent(motion); + } else if (entry.type == EventEntry::Type::KEY) { + const auto& key = static_cast<const KeyEntry&>(entry); + traced = createTracedEvent(key); + } else { + LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type); + } + + if (!cookie) { + // This event was not tracked as an inbound event, so trace it now. + mTraceQueue.emplace_back(traced); + } + + // The vsyncId only has meaning if the event is targeting a window. + const int32_t windowId = dispatchEntry.windowId.value_or(0); + const int32_t vsyncId = dispatchEntry.windowId.has_value() ? dispatchEntry.vsyncId : 0; + + mDispatchTraceQueue.emplace_back(std::move(traced), dispatchEntry.deliveryTime, + dispatchEntry.resolvedFlags, dispatchEntry.targetUid, + vsyncId, windowId, dispatchEntry.transform, + dispatchEntry.rawTransform); + } // release lock + + mThreadWakeCondition.notify_all(); +} + +std::optional<InputTracer::EventState>& InputTracer::getState(const EventTrackerInterface& cookie) { + return static_cast<const EventTrackerImpl&>(cookie).mLockedState; +} + +void InputTracer::threadLoop() { + androidSetThreadName("InputTracer"); + + while (true) { + std::vector<const EventState> eventsToTrace; + std::vector<const WindowDispatchArgs> dispatchEventsToTrace; + { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + if (mThreadExit) { + return; + } + if (mTraceQueue.empty() && mDispatchTraceQueue.empty()) { + // Wait indefinitely until the thread is awoken. + mThreadWakeCondition.wait(lock); + } + + mTraceQueue.swap(eventsToTrace); + mDispatchTraceQueue.swap(dispatchEventsToTrace); + } // release lock + + // Trace the events into the backend without holding the lock to reduce the amount of + // work performed in the critical section. + writeEventsToBackend(eventsToTrace, dispatchEventsToTrace); + eventsToTrace.clear(); + dispatchEventsToTrace.clear(); + } +} + +void InputTracer::writeEventsToBackend( + const std::vector<const EventState>& events, + const std::vector<const WindowDispatchArgs>& dispatchEvents) { + for (const auto& event : events) { + if (auto* motion = std::get_if<TracedMotionEvent>(&event.event); motion != nullptr) { + mBackend->traceMotionEvent(*motion); + } else { + mBackend->traceKeyEvent(std::get<TracedKeyEvent>(event.event)); + } + } + + for (const auto& dispatchArgs : dispatchEvents) { + mBackend->traceWindowDispatch(dispatchArgs); + } +} + +// --- InputTracer::EventTrackerImpl --- + +InputTracer::EventTrackerImpl::EventTrackerImpl(InputTracer& tracer, TracedEvent&& event) + : mTracer(tracer), mLockedState(event) {} + +InputTracer::EventTrackerImpl::~EventTrackerImpl() { + { + std::scoped_lock lock(mTracer.mLock); + if (!mLockedState) { + // This event has already been written to the trace as expected. + return; + } + // We're still holding on to the state, which means it hasn't yet been written to the trace. + // Write it to the trace now. + // TODO(b/210460522): Determine why/where the event is being destroyed before + // eventProcessingComplete() is called. + mTracer.mTraceQueue.emplace_back(std::move(*mLockedState)); + mLockedState.reset(); + } // release lock + + mTracer.mThreadWakeCondition.notify_all(); +} + +} // namespace android::inputdispatcher::trace::impl diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h new file mode 100644 index 0000000000..9fe395d397 --- /dev/null +++ b/services/inputflinger/dispatcher/trace/InputTracer.h @@ -0,0 +1,99 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "InputTracerInterface.h" + +#include <android-base/thread_annotations.h> +#include <gui/WindowInfo.h> + +#include <memory> +#include <mutex> +#include <thread> +#include <unordered_set> +#include <vector> + +#include "../Entry.h" +#include "InputTracingBackendInterface.h" + +namespace android::inputdispatcher::trace::impl { + +/** + * The tracer implementation for InputDispatcher. + * + * InputTracer is thread-safe, so it can be called from any thread. Upon construction, InputTracer + * will start its own thread that it uses for write events into the tracing backend. That is the + * one and only thread that will interact with the tracing backend, since the Perfetto backend + * uses thread-local storage. + * + * See the documentation in InputTracerInterface for the API surface. + */ +class InputTracer : public InputTracerInterface { +public: + explicit InputTracer(std::unique_ptr<InputTracingBackendInterface>); + ~InputTracer() override; + InputTracer(const InputTracer&) = delete; + InputTracer& operator=(const InputTracer&) = delete; + + std::unique_ptr<EventTrackerInterface> traceInboundEvent(const EventEntry&) override; + void dispatchToTargetHint(const EventTrackerInterface&, const InputTarget&) override; + void eventProcessingComplete(const EventTrackerInterface&) override; + void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface*) override; + +private: + std::mutex mLock; + std::thread mTracerThread; + bool mThreadExit GUARDED_BY(mLock){false}; + std::condition_variable mThreadWakeCondition; + std::unique_ptr<InputTracingBackendInterface> mBackend; + + // The state of a tracked event. + struct EventState { + const TracedEvent event; + // TODO(b/210460522): Add additional args for tracking event sensitivity and + // dispatch target UIDs. + }; + std::vector<const EventState> mTraceQueue GUARDED_BY(mLock); + using WindowDispatchArgs = InputTracingBackendInterface::WindowDispatchArgs; + std::vector<const WindowDispatchArgs> mDispatchTraceQueue GUARDED_BY(mLock); + + // Provides thread-safe access to the state from an event tracker cookie. + std::optional<EventState>& getState(const EventTrackerInterface&) REQUIRES(mLock); + + // Implementation of the event tracker cookie. + class EventTrackerImpl : public EventTrackerInterface { + public: + explicit EventTrackerImpl(InputTracer&, TracedEvent&& entry); + virtual ~EventTrackerImpl() override; + + private: + InputTracer& mTracer; + // This event tracker cookie will only hold the state as long as it has not been written + // to the trace. The state is released when the event is written to the trace. + mutable std::optional<EventState> mLockedState; + + // Only allow InputTracer access to the locked state through getTrackerState() to ensure + // that the InputTracer lock is held when this is accessed. + friend std::optional<EventState>& InputTracer::getState(const EventTrackerInterface&); + }; + + void threadLoop(); + void writeEventsToBackend(const std::vector<const EventState>& events, + const std::vector<const WindowDispatchArgs>& dispatchEvents); +}; + +} // namespace android::inputdispatcher::trace::impl diff --git a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h index b430a5bb28..bc47817817 100644 --- a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h +++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h @@ -16,11 +16,65 @@ #pragma once -#include "../Entry.h" +#include <gui/PidUid.h> +#include <input/Input.h> +#include <ui/Transform.h> + +#include <array> +#include <variant> +#include <vector> namespace android::inputdispatcher::trace { /** + * A representation of an Android KeyEvent used by the tracing backend. + */ +struct TracedKeyEvent { + int32_t id; + nsecs_t eventTime; + uint32_t policyFlags; + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t action; + int32_t keyCode; + int32_t scanCode; + int32_t metaState; + nsecs_t downTime; + int32_t flags; + int32_t repeatCount; +}; + +/** + * A representation of an Android MotionEvent used by the tracing backend. + */ +struct TracedMotionEvent { + int32_t id; + nsecs_t eventTime; + uint32_t policyFlags; + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t action; + int32_t actionButton; + int32_t flags; + int32_t metaState; + int32_t buttonState; + MotionClassification classification; + int32_t edgeFlags; + float xPrecision; + float yPrecision; + float xCursorPosition; + float yCursorPosition; + nsecs_t downTime; + std::vector<PointerProperties> pointerProperties; + std::vector<PointerCoords> pointerCoords; +}; + +/** A representation of a traced input event. */ +using TracedEvent = std::variant<TracedKeyEvent, TracedMotionEvent>; + +/** * An interface for the tracing backend, used for setting a custom backend for testing. */ class InputTracingBackendInterface { @@ -28,14 +82,14 @@ public: virtual ~InputTracingBackendInterface() = default; /** Trace a KeyEvent. */ - virtual void traceKeyEvent(const KeyEntry&) const = 0; + virtual void traceKeyEvent(const TracedKeyEvent&) const = 0; /** Trace a MotionEvent. */ - virtual void traceMotionEvent(const MotionEntry&) const = 0; + virtual void traceMotionEvent(const TracedMotionEvent&) const = 0; /** Trace an event being sent to a window. */ struct WindowDispatchArgs { - std::variant<MotionEntry, KeyEntry> eventEntry; + TracedEvent eventEntry; nsecs_t deliveryTime; int32_t resolvedFlags; gui::Uid targetUid; diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp new file mode 100644 index 0000000000..4442ad8586 --- /dev/null +++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp @@ -0,0 +1,94 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InputTracer" + +#include "InputTracingPerfettoBackend.h" + +#include "AndroidInputEventProtoConverter.h" + +#include <android-base/logging.h> +#include <perfetto/trace/android/android_input_event.pbzero.h> + +namespace android::inputdispatcher::trace::impl { + +namespace { + +constexpr auto INPUT_EVENT_TRACE_DATA_SOURCE_NAME = "android.input.inputevent"; + +} // namespace + +// --- PerfettoBackend::InputEventDataSource --- + +void PerfettoBackend::InputEventDataSource::OnStart(const perfetto::DataSourceBase::StartArgs&) { + LOG(INFO) << "Starting perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME; +} + +void PerfettoBackend::InputEventDataSource::OnStop(const perfetto::DataSourceBase::StopArgs&) { + LOG(INFO) << "Stopping perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME; + InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { ctx.Flush(); }); +} + +// --- PerfettoBackend --- + +std::once_flag PerfettoBackend::sDataSourceRegistrationFlag{}; + +PerfettoBackend::PerfettoBackend() { + // Use a once-flag to ensure that the data source is only registered once per boot, since + // we never unregister the InputEventDataSource. + std::call_once(sDataSourceRegistrationFlag, []() { + perfetto::TracingInitArgs args; + args.backends = perfetto::kSystemBackend; + perfetto::Tracing::Initialize(args); + + // Register our custom data source for input event tracing. + perfetto::DataSourceDescriptor dsd; + dsd.set_name(INPUT_EVENT_TRACE_DATA_SOURCE_NAME); + InputEventDataSource::Register(dsd); + LOG(INFO) << "InputTracer initialized for data source: " + << INPUT_EVENT_TRACE_DATA_SOURCE_NAME; + }); +} + +void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event) const { + InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { + auto tracePacket = ctx.NewTracePacket(); + auto* inputEvent = tracePacket->set_android_input_event(); + auto* dispatchMotion = inputEvent->set_dispatcher_motion_event(); + AndroidInputEventProtoConverter::toProtoMotionEvent(event, *dispatchMotion); + }); +} + +void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event) const { + InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { + auto tracePacket = ctx.NewTracePacket(); + auto* inputEvent = tracePacket->set_android_input_event(); + auto* dispatchKey = inputEvent->set_dispatcher_key_event(); + AndroidInputEventProtoConverter::toProtoKeyEvent(event, *dispatchKey); + }); +} + +void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs) const { + InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { + auto tracePacket = ctx.NewTracePacket(); + auto* inputEventProto = tracePacket->set_android_input_event(); + auto* dispatchEventProto = inputEventProto->set_dispatcher_window_dispatch_event(); + AndroidInputEventProtoConverter::toProtoWindowDispatchEvent(dispatchArgs, + *dispatchEventProto); + }); +} + +} // namespace android::inputdispatcher::trace::impl diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h new file mode 100644 index 0000000000..2777cfe9fe --- /dev/null +++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h @@ -0,0 +1,66 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "InputTracingBackendInterface.h" + +#include <perfetto/tracing.h> +#include <mutex> + +namespace android::inputdispatcher::trace::impl { + +/** + * The tracing backend that writes events into ongoing Perfetto traces. + * + * Example shell command to take an input trace from Perfetto: + * + * adb shell perfetto \ + * -c - --txt \ + * -o /data/misc/perfetto-traces/trace.input-trace \ + * <<END + * buffers: { + * size_kb: 5000 + * fill_policy: RING_BUFFER + * } + * data_sources: { + * config { + * name: "android.input.inputevent" + * } + * } + * END + */ +class PerfettoBackend : public InputTracingBackendInterface { +public: + PerfettoBackend(); + ~PerfettoBackend() override = default; + + void traceKeyEvent(const TracedKeyEvent&) const override; + void traceMotionEvent(const TracedMotionEvent&) const override; + void traceWindowDispatch(const WindowDispatchArgs&) const override; + + class InputEventDataSource : public perfetto::DataSource<InputEventDataSource> { + public: + void OnSetup(const SetupArgs&) override {} + void OnStart(const StartArgs&) override; + void OnStop(const StopArgs&) override; + }; + +private: + static std::once_flag sDataSourceRegistrationFlag; +}; + +} // namespace android::inputdispatcher::trace::impl diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 9abfef14dc..79c8a4ba76 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -226,6 +226,9 @@ struct InputReaderConfiguration { // True to enable tap-to-click on touchpads. bool touchpadTapToClickEnabled; + // True to enable tap dragging on touchpads. + bool touchpadTapDraggingEnabled; + // True to enable a zone on the right-hand side of touchpads where clicks will be turned into // context (a.k.a. "right") clicks. bool touchpadRightClickZoneEnabled; @@ -266,6 +269,7 @@ struct InputReaderConfiguration { touchpadPointerSpeed(0), touchpadNaturalScrollingEnabled(true), touchpadTapToClickEnabled(true), + touchpadTapDraggingEnabled(false), touchpadRightClickZoneEnabled(false), stylusButtonMotionEventsEnabled(true), stylusPointerIconEnabled(false) {} diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index f954370928..ba586d7a18 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_input_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index a41064b361..f3f15df151 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -251,6 +251,7 @@ std::list<NotifyArgs> InputDevice::configureInternal(nsecs_t when, mAssociatedDeviceType = getValueByKey(readerConfig.deviceTypeAssociations, mIdentifier.location); mIsWaking = mConfiguration.getBool("device.wake").value_or(false); + mShouldSmoothScroll = mConfiguration.getBool("device.viewBehavior_smoothScroll"); } if (!changes.any() || changes.test(Change::DEVICE_ALIAS)) { @@ -401,7 +402,8 @@ std::list<NotifyArgs> InputDevice::updateExternalStylusState(const StylusState& InputDeviceInfo InputDevice::getDeviceInfo() { InputDeviceInfo outDeviceInfo; outDeviceInfo.initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal, - mHasMic, getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE)); + mHasMic, getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE), + {mShouldSmoothScroll}); for_each_mapper( [&outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(outDeviceInfo); }); diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index ba7234b3e1..0719b0ce3c 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -199,6 +199,7 @@ private: std::optional<DisplayViewport> mAssociatedViewport; bool mHasMic; bool mDropUntilNextSync; + std::optional<bool> mShouldSmoothScroll; typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code); int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc); diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index d207ed1655..65f69c5155 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -164,8 +164,9 @@ std::list<NotifyArgs> CursorInputMapper::reconfigure(nsecs_t when, configureOnChangeDisplayInfo(readerConfig); } + // Pointer speed settings depend on display settings. if (!changes.any() || changes.test(InputReaderConfiguration::Change::POINTER_SPEED) || - configurePointerCapture) { + changes.test(InputReaderConfiguration::Change::DISPLAY_INFO) || configurePointerCapture) { configureOnChangePointerSpeed(readerConfig); } return out; @@ -515,7 +516,11 @@ void CursorInputMapper::configureOnChangePointerSpeed(const InputReaderConfigura mNewPointerVelocityControl.setCurve( createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed)); } else { - mOldPointerVelocityControl.setParameters(config.pointerVelocityControlParameters); + mOldPointerVelocityControl.setParameters( + (config.displaysWithMousePointerAccelerationDisabled.count( + mDisplayId.value_or(ADISPLAY_ID_NONE)) == 0) + ? config.pointerVelocityControlParameters + : FLAT_VELOCITY_CONTROL_PARAMS); } mWheelXVelocityControl.setParameters(config.wheelVelocityControlParameters); mWheelYVelocityControl.setParameters(config.wheelVelocityControlParameters); diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index bdc164029c..b990dd5e79 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -397,6 +397,8 @@ std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when, .setBoolValues({config.touchpadNaturalScrollingEnabled}); mPropertyProvider.getProperty("Tap Enable") .setBoolValues({config.touchpadTapToClickEnabled}); + mPropertyProvider.getProperty("Tap Drag Enable") + .setBoolValues({config.touchpadTapDraggingEnabled}); mPropertyProvider.getProperty("Button Right Click Zone Enable") .setBoolValues({config.touchpadRightClickZoneEnabled}); } diff --git a/services/inputflinger/reporter/Android.bp b/services/inputflinger/reporter/Android.bp index b1e1aee02a..e85a10428d 100644 --- a/services/inputflinger/reporter/Android.bp +++ b/services/inputflinger/reporter/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_input_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" diff --git a/services/inputflinger/rust/lib.rs b/services/inputflinger/rust/lib.rs index da72134065..25dfb03cb8 100644 --- a/services/inputflinger/rust/lib.rs +++ b/services/inputflinger/rust/lib.rs @@ -70,7 +70,9 @@ mod ffi { /// incremented using `AIBinder_incStrong`. See `binder::unstable_api::new_spibinder`. unsafe fn create_inputflinger_rust(callback: *mut ffi::IInputFlingerRustBootstrapCallbackAIBinder) { logger::init( - logger::Config::default().with_tag_on_device(LOG_TAG).with_min_level(log::Level::Trace), + logger::Config::default() + .with_tag_on_device(LOG_TAG) + .with_max_level(log::LevelFilter::Trace), ); let callback = callback as *mut AIBinder; diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 55aa22685e..0544757d99 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_input_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index f1f4a61e73..163eab078e 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -665,7 +665,7 @@ protected: void SetUp() override { mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>(); - mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy); + mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy, nullptr); mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false); // Start InputDispatcher thread @@ -1113,7 +1113,7 @@ public: sp<IBinder> getToken() { return mConsumer.getChannel()->getConnectionToken(); } - int getChannelFd() { return mConsumer.getChannel()->getFd().get(); } + int getChannelFd() { return mConsumer.getChannel()->getFd(); } private: InputConsumer mConsumer; @@ -6151,6 +6151,219 @@ TEST_F(InputDispatcherMonitorTest, InjectionFailsWithNoWindow) { monitor.assertNoEvents(); } +/** + * Two displays + * The first monitor has a foreground window, a monitor + * The second window has only one monitor. + * We first inject a Down event into the first display, this injection should succeed and both + * the foreground window and monitor should receive a down event, then inject a Down event into + * the second display as well, this injection should fail, at this point, the first display + * window and monitor should not receive a cancel or any other event. + * Continue to inject Move and UP events to the first display, the events should be received + * normally by the foreground window and monitor. + */ +TEST_F(InputDispatcherMonitorTest, MonitorTouchIsNotCanceledWhenAnotherEmptyDisplayReceiveEvents) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + + FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); + FakeMonitorReceiver secondMonitor = FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID); + + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "The down event injected into the first display should succeed"; + + window->consumeMotionDown(); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, + {100, 200})) + << "The down event injected into the second display should fail since there's no " + "touchable window"; + + // Continue to inject event to first display. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 220})) + << "The move event injected into the first display should succeed"; + + window->consumeMotionMove(); + monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {110, 220})) + << "The up event injected into the first display should succeed"; + + window->consumeMotionUp(); + monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + + window->assertNoEvents(); + monitor.assertNoEvents(); + secondMonitor.assertNoEvents(); +} + +/** + * Two displays + * There is a monitor and foreground window on each display. + * First, we inject down events into each of the two displays, at this point, the foreground windows + * and monitors on both displays should receive down events. + * At this point, the foreground window of the second display goes away, the gone window should + * receive the cancel event, and the other windows and monitors should not receive any events. + * Inject a move event into the second display. At this point, the injection should fail because + * the second display no longer has a foreground window. At this point, the monitor on the second + * display should receive a cancel event, and any windows or monitors on the first display should + * not receive any events, and any subsequent injection of events into the second display should + * also fail. + * Continue to inject events into the first display, and the events should all be injected + * successfully and received normally. + */ +TEST_F(InputDispatcherMonitorTest, MonitorTouchIsNotCancelWhenAnotherDisplayMonitorTouchCanceled) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> secondWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "SecondForeground", + SECOND_DISPLAY_ID); + + FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); + FakeMonitorReceiver secondMonitor = FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID); + + // There is a foreground window on both displays. + mDispatcher->onWindowInfosChanged({{*window->getInfo(), *secondWindow->getInfo()}, {}, 0, 0}); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "The down event injected into the first display should succeed"; + + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, + {100, 200})) + << "The down event injected into the second display should succeed"; + + secondWindow->consumeMotionDown(SECOND_DISPLAY_ID); + secondMonitor.consumeMotionDown(SECOND_DISPLAY_ID); + + // Now second window is gone away. + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + + // The gone window should receive a cancel, and the monitor on the second display should not + // receive any events. + secondWindow->consumeMotionCancel(SECOND_DISPLAY_ID); + secondMonitor.assertNoEvents(); + + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + SECOND_DISPLAY_ID, {110, 220})) + << "The move event injected into the second display should fail because there's no " + "touchable window"; + // Now the monitor on the second display should receive a cancel event. + secondMonitor.consumeMotionCancel(SECOND_DISPLAY_ID); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 200})) + << "The move event injected into the first display should succeed"; + + window->consumeMotionMove(); + monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT); + + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, + {110, 220})) + << "The up event injected into the second display should fail because there's no " + "touchable window"; + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {110, 220})) + << "The up event injected into the first display should succeed"; + + window->consumeMotionUp(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + + window->assertNoEvents(); + monitor.assertNoEvents(); + secondWindow->assertNoEvents(); + secondMonitor.assertNoEvents(); +} + +/** + * One display with transform + * There is a foreground window and a monitor on the display + * Inject down event and move event sequentially, the foreground window and monitor can receive down + * event and move event, then let the foreground window go away, the foreground window receives + * cancel event, inject move event again, the monitor receives cancel event, all the events received + * by the monitor should be with the same transform as the display + */ +TEST_F(InputDispatcherMonitorTest, MonitorTouchCancelEventWithDisplayTransform) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); + + ui::Transform transform; + transform.set({1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0, 0, 1}); + + gui::DisplayInfo displayInfo; + displayInfo.displayId = ADISPLAY_ID_DEFAULT; + displayInfo.transform = transform; + + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {displayInfo}, 0, 0}); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "The down event injected should succeed"; + + window->consumeMotionDown(); + std::unique_ptr<MotionEvent> downMotionEvent = monitor.consumeMotion(); + EXPECT_EQ(transform, downMotionEvent->getTransform()); + EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, downMotionEvent->getAction()); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 220})) + << "The move event injected should succeed"; + + window->consumeMotionMove(); + std::unique_ptr<MotionEvent> moveMotionEvent = monitor.consumeMotion(); + EXPECT_EQ(transform, moveMotionEvent->getTransform()); + EXPECT_EQ(AMOTION_EVENT_ACTION_MOVE, moveMotionEvent->getAction()); + + // Let foreground window gone + mDispatcher->onWindowInfosChanged({{}, {displayInfo}, 0, 0}); + + // Foreground window should receive a cancel event, but not the monitor. + window->consumeMotionCancel(); + + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 220})) + << "The move event injected should failed"; + // Now foreground should not receive any events, but monitor should receive a cancel event + // with transform that same as display's display. + std::unique_ptr<MotionEvent> cancelMotionEvent = monitor.consumeMotion(); + EXPECT_EQ(transform, cancelMotionEvent->getTransform()); + EXPECT_EQ(ADISPLAY_ID_DEFAULT, cancelMotionEvent->getDisplayId()); + EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, cancelMotionEvent->getAction()); + + // Other event inject to this display should fail. + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 220})) + << "The up event injected should fail because the touched window was removed"; + window->assertNoEvents(); + monitor.assertNoEvents(); +} + TEST_F(InputDispatcherTest, TestMoveEvent) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, @@ -7090,12 +7303,9 @@ protected: sp<FakeWindowHandle> mWindow; virtual void SetUp() override { - mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>(); - mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy); - mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false); - mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY); - ASSERT_EQ(OK, mDispatcher->start()); + InputDispatcherTest::SetUp(); + mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY); setUpWindow(); } diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 460a7b185d..c1dc7ffcfb 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -2706,6 +2706,31 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe ASSERT_NO_FATAL_FAILURE(mapper2.assertProcessWasCalled()); } +TEST_F(InputDeviceTest, Configure_SmoothScrollViewBehaviorNotSet) { + // Set some behavior to force the configuration to be update. + mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "1"); + mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(), + AINPUT_SOURCE_KEYBOARD); + + std::list<NotifyArgs> unused = + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + /*changes=*/{}); + + ASSERT_FALSE(mDevice->getDeviceInfo().getViewBehavior().shouldSmoothScroll.has_value()); +} + +TEST_F(InputDeviceTest, Configure_SmoothScrollViewBehaviorEnabled) { + mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.viewBehavior_smoothScroll", "1"); + mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(), + AINPUT_SOURCE_KEYBOARD); + + std::list<NotifyArgs> unused = + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + /*changes=*/{}); + + ASSERT_TRUE(mDevice->getDeviceInfo().getViewBehavior().shouldSmoothScroll.value_or(false)); +} + TEST_F(InputDeviceTest, WakeDevice_AddsWakeFlagToProcessNotifyArgs) { mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "1"); FakeInputMapper& mapper = diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp index 8a4f6f0acc..81c335326a 100644 --- a/services/inputflinger/tests/fuzzers/Android.bp +++ b/services/inputflinger/tests/fuzzers/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_input_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" diff --git a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp index be765cc364..c2bf275611 100644 --- a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp @@ -123,6 +123,7 @@ void setTouchpadSettings(ThreadSafeFuzzedDataProvider& fdp, InputReaderConfigura config.touchpadPointerSpeed = fdp.ConsumeIntegralInRange(-7, 7); config.touchpadNaturalScrollingEnabled = fdp.ConsumeBool(); config.touchpadTapToClickEnabled = fdp.ConsumeBool(); + config.touchpadTapDraggingEnabled = fdp.ConsumeBool(); config.touchpadRightClickZoneEnabled = fdp.ConsumeBool(); } diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 9e35717c95..575a30e522 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -97,13 +97,13 @@ public: MOCK_CONST_METHOD1(isConnected, bool(PhysicalDisplayId)); MOCK_CONST_METHOD2(getModes, std::vector<HWComposer::HWCDisplayMode>(PhysicalDisplayId, int32_t)); - MOCK_CONST_METHOD1(getActiveMode, std::optional<hal::HWConfigId>(PhysicalDisplayId)); + MOCK_CONST_METHOD1(getActiveMode, ftl::Expected<hal::HWConfigId, status_t>(PhysicalDisplayId)); MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(PhysicalDisplayId)); MOCK_METHOD3(setActiveColorMode, status_t(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent)); MOCK_CONST_METHOD0(isUsingVrComposer, bool()); MOCK_CONST_METHOD1(getDisplayConnectionType, ui::DisplayConnectionType(PhysicalDisplayId)); MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(PhysicalDisplayId)); - MOCK_CONST_METHOD2(getDisplayVsyncPeriod, status_t(PhysicalDisplayId, nsecs_t*)); + MOCK_CONST_METHOD1(getDisplayVsyncPeriod, ftl::Expected<nsecs_t, status_t>(PhysicalDisplayId)); MOCK_METHOD4(setActiveModeWithConstraints, status_t(PhysicalDisplayId, hal::HWConfigId, const hal::VsyncPeriodChangeConstraints&, diff --git a/services/surfaceflinger/Display/DisplayModeRequest.h b/services/surfaceflinger/Display/DisplayModeRequest.h index d07cdf55d2..c0e77bb5fc 100644 --- a/services/surfaceflinger/Display/DisplayModeRequest.h +++ b/services/surfaceflinger/Display/DisplayModeRequest.h @@ -27,6 +27,9 @@ struct DisplayModeRequest { // Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE. bool emitEvent = false; + + // Whether to force the request to be applied, even if the mode is unchanged. + bool force = false; }; inline bool operator==(const DisplayModeRequest& lhs, const DisplayModeRequest& rhs) { diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 4f81482814..45f08a4521 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -24,6 +24,7 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <common/FlagManager.h> #include <compositionengine/CompositionEngine.h> #include <compositionengine/Display.h> #include <compositionengine/DisplayColorProfile.h> @@ -214,6 +215,17 @@ void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps vsyncRate, Fps rende bool DisplayDevice::initiateModeChange(display::DisplayModeRequest&& desiredMode, const hal::VsyncPeriodChangeConstraints& constraints, hal::VsyncPeriodChangeTimeline& outTimeline) { + // TODO(b/255635711): Flow the DisplayModeRequest through the desired/pending/active states. For + // now, `desiredMode` and `mDesiredModeOpt` are one and the same, but the latter is not cleared + // until the next `SF::initiateDisplayModeChanges`. However, the desired mode has been consumed + // at this point, so clear the `force` flag to prevent an endless loop of `initiateModeChange`. + if (FlagManager::getInstance().connected_display()) { + std::scoped_lock lock(mDesiredModeLock); + if (mDesiredModeOpt) { + mDesiredModeOpt->force = false; + } + } + mPendingModeOpt = std::move(desiredMode); mIsModeSetPending = true; @@ -239,10 +251,8 @@ nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const { return 0; } - nsecs_t vsyncPeriod; - const auto status = mHwComposer.getDisplayVsyncPeriod(physicalId, &vsyncPeriod); - if (status == NO_ERROR) { - return vsyncPeriod; + if (const auto vsyncPeriodOpt = mHwComposer.getDisplayVsyncPeriod(physicalId).value_opt()) { + return *vsyncPeriodOpt; } return refreshRateSelector().getActiveMode().modePtr->getVsyncRate().getPeriodNsecs(); @@ -519,8 +529,7 @@ void DisplayDevice::animateOverlay() { } } -auto DisplayDevice::setDesiredMode(display::DisplayModeRequest&& desiredMode, bool force) - -> DesiredModeAction { +auto DisplayDevice::setDesiredMode(display::DisplayModeRequest&& desiredMode) -> DesiredModeAction { ATRACE_CALL(); const auto& desiredModePtr = desiredMode.mode.modePtr; @@ -528,20 +537,26 @@ auto DisplayDevice::setDesiredMode(display::DisplayModeRequest&& desiredMode, bo LOG_ALWAYS_FATAL_IF(getPhysicalId() != desiredModePtr->getPhysicalDisplayId(), "DisplayId mismatch"); - ALOGV("%s(%s)", __func__, to_string(*desiredModePtr).c_str()); + // TODO (b/318533819): Stringize DisplayModeRequest. + ALOGD("%s(%s, force=%s)", __func__, to_string(*desiredModePtr).c_str(), + desiredMode.force ? "true" : "false"); std::scoped_lock lock(mDesiredModeLock); if (mDesiredModeOpt) { // A mode transition was already scheduled, so just override the desired mode. const bool emitEvent = mDesiredModeOpt->emitEvent; + const bool force = mDesiredModeOpt->force; mDesiredModeOpt = std::move(desiredMode); mDesiredModeOpt->emitEvent |= emitEvent; + if (FlagManager::getInstance().connected_display()) { + mDesiredModeOpt->force |= force; + } return DesiredModeAction::None; } // If the desired mode is already active... const auto activeMode = refreshRateSelector().getActiveMode(); - if (!force && activeMode.modePtr->getId() == desiredModePtr->getId()) { + if (!desiredMode.force && activeMode.modePtr->getId() == desiredModePtr->getId()) { if (activeMode == desiredMode.mode) { return DesiredModeAction::None; } diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 4ab6321064..edd57cce91 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -189,8 +189,7 @@ public: enum class DesiredModeAction { None, InitiateDisplayModeSwitch, InitiateRenderRateSwitch }; - DesiredModeAction setDesiredMode(display::DisplayModeRequest&&, bool force = false) - EXCLUDES(mDesiredModeLock); + DesiredModeAction setDesiredMode(display::DisplayModeRequest&&) EXCLUDES(mDesiredModeLock); using DisplayModeRequestOpt = ftl::Optional<display::DisplayModeRequest>; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 17f6f31441..362ab9c39e 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -343,7 +343,9 @@ void AidlComposer::registerCallback(HWC2::ComposerCallback& callback) { } mAidlComposerCallback = ndk::SharedRefBase::make<AidlIComposerCallbackWrapper>(callback); - AIBinder_setMinSchedulerPolicy(mAidlComposerCallback->asBinder().get(), SCHED_FIFO, 2); + + ndk::SpAIBinder binder = mAidlComposerCallback->asBinder(); + AIBinder_setMinSchedulerPolicy(binder.get(), SCHED_FIFO, 2); const auto status = mAidlComposerClient->registerCallback(mAidlComposerCallback); if (!status.isOk()) { diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 704ece516d..db66f5b549 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -27,6 +27,7 @@ #include "HWC2.h" #include <android/configuration.h> +#include <common/FlagManager.h> #include <ui/Fence.h> #include <ui/FloatRect.h> #include <ui/GraphicBuffer.h> @@ -416,7 +417,19 @@ Error Display::setActiveConfigWithConstraints(hal::HWConfigId configId, VsyncPeriodChangeTimeline* outTimeline) { ALOGV("[%" PRIu64 "] setActiveConfigWithConstraints", mId); - if (isVsyncPeriodSwitchSupported()) { + // FIXME (b/319505580): At least the first config set on an external display must be + // `setActiveConfig`, so skip over the block that calls `setActiveConfigWithConstraints` + // for simplicity. + ui::DisplayConnectionType type = ui::DisplayConnectionType::Internal; + const bool connected_display = FlagManager::getInstance().connected_display(); + if (connected_display) { + if (auto err = getConnectionType(&type); err != Error::NONE) { + return err; + } + } + + if (isVsyncPeriodSwitchSupported() && + (!connected_display || type != ui::DisplayConnectionType::External)) { Hwc2::IComposerClient::VsyncPeriodChangeConstraints hwc2Constraints; hwc2Constraints.desiredTimeNanos = constraints.desiredTimeNanos; hwc2Constraints.seamlessRequired = constraints.seamlessRequired; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index cf1c3c10b7..bac24c701e 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -343,14 +343,18 @@ std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromLegacyDisplayCon return modes; } -std::optional<hal::HWConfigId> HWComposer::getActiveMode(PhysicalDisplayId displayId) const { - RETURN_IF_INVALID_DISPLAY(displayId, std::nullopt); +ftl::Expected<hal::HWConfigId, status_t> HWComposer::getActiveMode( + PhysicalDisplayId displayId) const { + RETURN_IF_INVALID_DISPLAY(displayId, ftl::Unexpected(BAD_INDEX)); const auto hwcId = *fromPhysicalDisplayId(displayId); hal::HWConfigId configId; const auto error = static_cast<hal::Error>(mComposer->getActiveConfig(hwcId, &configId)); + if (error == hal::Error::BAD_CONFIG) { + return ftl::Unexpected(NO_INIT); + } - RETURN_IF_HWC_ERROR_FOR("getActiveConfig", error, displayId, std::nullopt); + RETURN_IF_HWC_ERROR_FOR("getActiveConfig", error, displayId, ftl::Unexpected(UNKNOWN_ERROR)); return configId; } @@ -376,20 +380,20 @@ bool HWComposer::isVsyncPeriodSwitchSupported(PhysicalDisplayId displayId) const return mDisplayData.at(displayId).hwcDisplay->isVsyncPeriodSwitchSupported(); } -status_t HWComposer::getDisplayVsyncPeriod(PhysicalDisplayId displayId, - nsecs_t* outVsyncPeriod) const { - RETURN_IF_INVALID_DISPLAY(displayId, 0); +ftl::Expected<nsecs_t, status_t> HWComposer::getDisplayVsyncPeriod( + PhysicalDisplayId displayId) const { + RETURN_IF_INVALID_DISPLAY(displayId, ftl::Unexpected(BAD_INDEX)); if (!isVsyncPeriodSwitchSupported(displayId)) { - return INVALID_OPERATION; + return ftl::Unexpected(INVALID_OPERATION); } + const auto hwcId = *fromPhysicalDisplayId(displayId); Hwc2::VsyncPeriodNanos vsyncPeriodNanos = 0; - auto error = + const auto error = static_cast<hal::Error>(mComposer->getDisplayVsyncPeriod(hwcId, &vsyncPeriodNanos)); - RETURN_IF_HWC_ERROR(error, displayId, 0); - *outVsyncPeriod = static_cast<nsecs_t>(vsyncPeriodNanos); - return NO_ERROR; + RETURN_IF_HWC_ERROR(error, displayId, ftl::Unexpected(UNKNOWN_ERROR)); + return static_cast<nsecs_t>(vsyncPeriodNanos); } std::vector<ui::ColorMode> HWComposer::getColorModes(PhysicalDisplayId displayId) const { diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index fb32ff45e1..7fbbb1a672 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -25,6 +25,7 @@ #include <vector> #include <android-base/thread_annotations.h> +#include <ftl/expected.h> #include <ftl/future.h> #include <ui/DisplayIdentification.h> #include <ui/FenceTime.h> @@ -237,7 +238,7 @@ public: virtual std::vector<HWCDisplayMode> getModes(PhysicalDisplayId, int32_t maxFrameIntervalNs) const = 0; - virtual std::optional<hal::HWConfigId> getActiveMode(PhysicalDisplayId) const = 0; + virtual ftl::Expected<hal::HWConfigId, status_t> getActiveMode(PhysicalDisplayId) const = 0; virtual std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const = 0; @@ -247,8 +248,7 @@ public: // Composer 2.4 virtual ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const = 0; virtual bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const = 0; - virtual status_t getDisplayVsyncPeriod(PhysicalDisplayId displayId, - nsecs_t* outVsyncPeriod) const = 0; + virtual ftl::Expected<nsecs_t, status_t> getDisplayVsyncPeriod(PhysicalDisplayId) const = 0; virtual status_t setActiveModeWithConstraints(PhysicalDisplayId, hal::HWConfigId, const hal::VsyncPeriodChangeConstraints&, hal::VsyncPeriodChangeTimeline* outTimeline) = 0; @@ -424,7 +424,7 @@ public: std::vector<HWCDisplayMode> getModes(PhysicalDisplayId, int32_t maxFrameIntervalNs) const override; - std::optional<hal::HWConfigId> getActiveMode(PhysicalDisplayId) const override; + ftl::Expected<hal::HWConfigId, status_t> getActiveMode(PhysicalDisplayId) const override; std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const override; @@ -435,8 +435,7 @@ public: // Composer 2.4 ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override; bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override; - status_t getDisplayVsyncPeriod(PhysicalDisplayId displayId, - nsecs_t* outVsyncPeriod) const override; + ftl::Expected<nsecs_t, status_t> getDisplayVsyncPeriod(PhysicalDisplayId) const override; status_t setActiveModeWithConstraints(PhysicalDisplayId, hal::HWConfigId, const hal::VsyncPeriodChangeConstraints&, hal::VsyncPeriodChangeTimeline* outTimeline) override; diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp index a0c943ba72..ee6d37bc9c 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp @@ -48,6 +48,7 @@ namespace impl { using aidl::android::hardware::power::Boost; using aidl::android::hardware::power::Mode; using aidl::android::hardware::power::SessionHint; +using aidl::android::hardware::power::SessionTag; using aidl::android::hardware::power::WorkDuration; PowerAdvisor::~PowerAdvisor() = default; @@ -206,9 +207,12 @@ bool PowerAdvisor::supportsPowerHintSession() { bool PowerAdvisor::ensurePowerHintSessionRunning() { if (mHintSession == nullptr && !mHintSessionThreadIds.empty() && usePowerHintSession()) { - auto ret = getPowerHal().createHintSession(getpid(), static_cast<int32_t>(getuid()), - mHintSessionThreadIds, mTargetDuration.ns()); - + auto ret = + getPowerHal().createHintSessionWithConfig(getpid(), static_cast<int32_t>(getuid()), + mHintSessionThreadIds, + mTargetDuration.ns(), + SessionTag::SURFACEFLINGER, + &mSessionConfig); if (ret.isOk()) { mHintSession = ret.value(); } diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h index bbe51cc09d..d6ffb2af1f 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h @@ -292,6 +292,9 @@ private: // Whether we should send reportActualWorkDuration calls static const bool sUseReportActualDuration; + // Metadata about the session returned from PowerHAL + aidl::android::hardware::power::SessionConfig mSessionConfig; + // How long we expect hwc to run after the present call until it waits for the fence static constexpr const Duration kFenceWaitStartDelayValidated{150us}; static constexpr const Duration kFenceWaitStartDelaySkippedValidate{250us}; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp index 38974a2f58..3ef9e69620 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp @@ -315,6 +315,7 @@ std::ostream& operator<<(std::ostream& out, const LayerSnapshot& obj) { if (obj.hasInputInfo()) { out << "\n input{" << "(" << obj.inputInfo.inputConfig.string() << ")"; + if (obj.inputInfo.canOccludePresentation) out << " canOccludePresentation"; if (obj.touchCropId != UNASSIGNED_LAYER_ID) out << " touchCropId=" << obj.touchCropId; if (obj.inputInfo.replaceTouchableRegionWithCrop) out << " replaceTouchableRegionWithCrop"; auto touchableRegion = obj.inputInfo.touchableRegion.getBounds(); diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index 8df5d8c679..0966fe0496 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -1044,6 +1044,8 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, snapshot.inputInfo.touchOcclusionMode = requested.hasInputInfo() ? requested.windowInfoHandle->getInfo()->touchOcclusionMode : parentSnapshot.inputInfo.touchOcclusionMode; + snapshot.inputInfo.canOccludePresentation = parentSnapshot.inputInfo.canOccludePresentation || + (requested.flags & layer_state_t::eCanOccludePresentation); if (requested.dropInputMode == gui::DropInputMode::ALL || parentSnapshot.dropInputMode == gui::DropInputMode::ALL) { snapshot.dropInputMode = gui::DropInputMode::ALL; diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index 209df79f8e..2cf4c1b7dd 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -170,6 +170,9 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta if ((oldFlags ^ flags) & layer_state_t::eIgnoreDestinationFrame) { changes |= RequestedLayerState::Changes::Geometry; } + if ((oldFlags ^ flags) & layer_state_t::eCanOccludePresentation) { + changes |= RequestedLayerState::Changes::Input; + } } if (clientState.what & layer_state_t::eBufferChanged) { diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index c8b1059b94..219a8e20a4 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -78,11 +78,13 @@ #include "SurfaceFlinger.h" #include "TimeStats/TimeStats.h" #include "TunnelModeEnabledReporter.h" +#include "Utils/FenceUtils.h" #define DEBUG_RESIZE 0 #define EARLY_RELEASE_ENABLED false namespace android { +using namespace std::chrono_literals; namespace { constexpr int kDumpTableRowLength = 159; @@ -2911,7 +2913,8 @@ void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& l } void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult, - ui::LayerStack layerStack) { + ui::LayerStack layerStack, + std::function<FenceResult(FenceResult)>&& continuation) { // If we are displayed on multiple displays in a single composition cycle then we would // need to do careful tracking to enable the use of the mLastClientCompositionFence. // For example we can only use it if all the displays are client comp, and we need @@ -2946,11 +2949,41 @@ void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult, break; } } + + if (!FlagManager::getInstance().screenshot_fence_preservation() && continuation) { + futureFenceResult = ftl::Future(futureFenceResult).then(std::move(continuation)).share(); + } + if (ch != nullptr) { ch->previousReleaseCallbackId = mPreviousReleaseCallbackId; ch->previousReleaseFences.emplace_back(std::move(futureFenceResult)); ch->name = mName; + } else if (FlagManager::getInstance().screenshot_fence_preservation()) { + // If we didn't get a release callback yet, e.g. some scenarios when capturing screenshots + // asynchronously, then make sure we don't drop the fence. + mAdditionalPreviousReleaseFences.emplace_back(std::move(futureFenceResult), + std::move(continuation)); + std::vector<FenceAndContinuation> mergedFences; + sp<Fence> prevFence = nullptr; + // For a layer that's frequently screenshotted, try to merge fences to make sure we don't + // grow unbounded. + for (const auto& futureAndContinution : mAdditionalPreviousReleaseFences) { + auto result = futureAndContinution.future.wait_for(0s); + if (result != std::future_status::ready) { + mergedFences.emplace_back(futureAndContinution); + continue; + } + + mergeFence(getDebugName(), futureAndContinution.chain().get().value_or(Fence::NO_FENCE), + prevFence); + } + if (prevFence != nullptr) { + mergedFences.emplace_back(ftl::yield(FenceResult(std::move(prevFence))).share()); + } + + mAdditionalPreviousReleaseFences.swap(mergedFences); } + if (mBufferInfo.mBuffer) { mPreviouslyPresentedLayerStacks.push_back(layerStack); } @@ -3440,6 +3473,15 @@ bool Layer::setTransactionCompletedListeners(const std::vector<sp<CallbackHandle handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence; handle->frameNumber = mDrawingState.frameNumber; handle->previousFrameNumber = mDrawingState.previousFrameNumber; + if (FlagManager::getInstance().screenshot_fence_preservation() && + mPreviousReleaseBufferEndpoint == handle->listener) { + // Add fences from previous screenshots now so that they can be dispatched to the + // client. + for (const auto& futureAndContinution : mAdditionalPreviousReleaseFences) { + handle->previousReleaseFences.emplace_back(futureAndContinution.chain()); + } + mAdditionalPreviousReleaseFences.clear(); + } // Store so latched time and release fence can be set mDrawingState.callbackHandles.push_back(handle); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index c772e0ea1b..dfd57c65e3 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -555,7 +555,8 @@ public: const compositionengine::LayerFECompositionState* getCompositionState() const; bool fenceHasSignaled() const; void onPreComposition(nsecs_t refreshStartTime); - void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack); + void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack, + std::function<FenceResult(FenceResult)>&& continuation = nullptr); void setWasClientComposed(const sp<Fence>& fence) { mLastClientCompositionFence = fence; @@ -932,6 +933,19 @@ public: // the release fences from the correct displays when we release the last buffer // from the layer. std::vector<ui::LayerStack> mPreviouslyPresentedLayerStacks; + struct FenceAndContinuation { + ftl::SharedFuture<FenceResult> future; + std::function<FenceResult(FenceResult)> continuation; + + ftl::SharedFuture<FenceResult> chain() const { + if (continuation) { + return ftl::Future(future).then(continuation).share(); + } else { + return future; + } + } + }; + std::vector<FenceAndContinuation> mAdditionalPreviousReleaseFences; // Exposed so SurfaceFlinger can assert that it's held const sp<SurfaceFlinger> mFlinger; diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index c80c8fd237..ccd1c0f306 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -404,6 +404,9 @@ VsyncEventData EventThread::getLatestVsyncEventData( }(); generateFrameTimeline(vsyncEventData, frameInterval.ns(), systemTime(SYSTEM_TIME_MONOTONIC), presentTime, deadline); + if (FlagManager::getInstance().vrr_config()) { + mCallback.onExpectedPresentTimePosted(TimePoint::fromNs(presentTime)); + } return vsyncEventData; } @@ -721,6 +724,11 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, removeDisplayEventConnectionLocked(consumer); } } + if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC && + FlagManager::getInstance().vrr_config()) { + mCallback.onExpectedPresentTimePosted( + TimePoint::fromNs(event.vsync.vsyncData.preferredExpectedPresentationTime())); + } } void EventThread::dump(std::string& result) const { diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index 8970103a7c..90e61a984b 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -142,6 +142,7 @@ struct IEventThreadCallback { virtual bool throttleVsync(TimePoint, uid_t) = 0; virtual Period getVsyncPeriod(uid_t) = 0; virtual void resync() = 0; + virtual void onExpectedPresentTimePosted(TimePoint) = 0; }; namespace impl { diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h index 3b61de7e83..9f4f5b6d39 100644 --- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h +++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h @@ -30,6 +30,8 @@ struct ISchedulerCallback { virtual void kernelTimerChanged(bool expired) = 0; virtual void triggerOnFrameRateOverridesChanged() = 0; virtual void onChoreographerAttached() = 0; + virtual void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, + Fps renderRate) = 0; protected: ~ISchedulerCallback() = default; diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index c3709e5cc2..7614453c00 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -843,9 +843,13 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending); using fps_approx_ops::operator<; + // A method for UI Toolkit to send the touch signal via "HighHint" category vote, + // which will touch boost when there are no ExplicitDefault layer votes. This is an + // incomplete solution but accounts for cases such as games that use `setFrameRate` with default + // compatibility to limit the frame rate, which should not have touch boost. const bool hasInteraction = signals.touch || interactiveLayers > 0; - if (hasInteraction && explicitDefaultVoteLayers == 0 && explicitCategoryVoteLayers == 0 && - touchBoostForExplicitExact && + + if (hasInteraction && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact && scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) { ALOGV("Touch Boost"); ATRACE_FORMAT_INSTANT("%s (Touch Boost [late])", diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index c76d4bd982..6979f0399b 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -71,15 +71,13 @@ namespace android::scheduler { Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features, - surfaceflinger::Factory& factory, Fps activeRefreshRate, TimeStats& timeStats, - IVsyncTrackerCallback& vsyncTrackerCallback) + surfaceflinger::Factory& factory, Fps activeRefreshRate, TimeStats& timeStats) : android::impl::MessageQueue(compositor), mFeatures(features), mVsyncConfiguration(factory.createVsyncConfiguration(activeRefreshRate)), mVsyncModulator(sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs())), mRefreshRateStats(std::make_unique<RefreshRateStats>(timeStats, activeRefreshRate)), - mSchedulerCallback(callback), - mVsyncTrackerCallback(vsyncTrackerCallback) {} + mSchedulerCallback(callback) {} Scheduler::~Scheduler() { // MessageQueue depends on VsyncSchedule, so first destroy it. @@ -134,10 +132,11 @@ void Scheduler::setPacesetterDisplay(std::optional<PhysicalDisplayId> pacesetter } void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) { - auto schedulePtr = std::make_shared<VsyncSchedule>( - selectorPtr->getActiveMode().modePtr, mFeatures, - [this](PhysicalDisplayId id, bool enable) { onHardwareVsyncRequest(id, enable); }, - mVsyncTrackerCallback); + auto schedulePtr = + std::make_shared<VsyncSchedule>(selectorPtr->getActiveMode().modePtr, mFeatures, + [this](PhysicalDisplayId id, bool enable) { + onHardwareVsyncRequest(id, enable); + }); registerDisplayInternal(displayId, std::move(selectorPtr), std::move(schedulePtr)); } @@ -222,7 +221,12 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, targets.try_emplace(id, &targeter.target()); } - if (!compositor.commit(pacesetterPtr->displayId, targets)) return; + if (!compositor.commit(pacesetterPtr->displayId, targets)) { + if (FlagManager::getInstance().vrr_config()) { + compositor.sendNotifyExpectedPresentHint(pacesetterPtr->displayId); + } + return; + } } // The pacesetter may have changed or been registered anew during commit. @@ -263,6 +267,9 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, } const auto resultsPerDisplay = compositor.composite(pacesetterPtr->displayId, targeters); + if (FlagManager::getInstance().vrr_config()) { + compositor.sendNotifyExpectedPresentHint(pacesetterPtr->displayId); + } compositor.sample(); for (const auto& [id, targeter] : targeters) { @@ -323,6 +330,19 @@ Period Scheduler::getVsyncPeriod(uid_t uid) { // behaviour. return Period::fromNs(currentPeriod.ns() * divisor); } +void Scheduler::onExpectedPresentTimePosted(TimePoint expectedPresentTime) { + const auto frameRateMode = [this] { + std::scoped_lock lock(mDisplayLock); + const auto pacesetterOpt = pacesetterDisplayLocked(); + const Display& pacesetter = *pacesetterOpt; + return pacesetter.selectorPtr->getActiveMode(); + }(); + + if (frameRateMode.modePtr->getVrrConfig()) { + mSchedulerCallback.onExpectedPresentTimePosted(expectedPresentTime, frameRateMode.modePtr, + frameRateMode.fps); + } +} ConnectionHandle Scheduler::createEventThread(Cycle cycle, frametimeline::TokenManager* tokenManager, diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 99126220b0..9f29e9f696 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -111,7 +111,7 @@ class Scheduler : public IEventThreadCallback, android::impl::MessageQueue { public: Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, surfaceflinger::Factory&, - Fps activeRefreshRate, TimeStats&, IVsyncTrackerCallback&); + Fps activeRefreshRate, TimeStats&); virtual ~Scheduler(); void startTimers(); @@ -458,6 +458,7 @@ private: bool throttleVsync(TimePoint, uid_t) override; Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock); void resync() override EXCLUDES(mDisplayLock); + void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock); // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. struct Connection { @@ -497,8 +498,6 @@ private: ISchedulerCallback& mSchedulerCallback; - IVsyncTrackerCallback& mVsyncTrackerCallback; - // mDisplayLock may be locked while under mPolicyLock. mutable std::mutex mPolicyLock; diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index 6e12b337ee..8697696915 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -48,14 +48,12 @@ static auto constexpr kMaxPercent = 100u; VSyncPredictor::~VSyncPredictor() = default; VSyncPredictor::VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize, - size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent, - IVsyncTrackerCallback& callback) + size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent) : mId(modePtr->getPhysicalDisplayId()), mTraceOn(property_get_bool("debug.sf.vsp_trace", false)), kHistorySize(historySize), kMinimumSamplesForPrediction(minimumSamplesForPrediction), kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)), - mVsyncTrackerCallback(callback), mDisplayModePtr(modePtr) { resetModel(); } @@ -312,13 +310,7 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, FlagManager::getInstance().vrr_config() && !lastFrameMissed && lastVsyncOpt ? std::max(timePoint, *lastVsyncOpt + minFramePeriod - threshold) : timePoint; - const auto vsyncTime = snapToVsyncAlignedWithRenderRate(baseTime); - if (FlagManager::getInstance().vrr_config() && mDisplayModePtr->getVrrConfig()) { - const auto vsyncTimePoint = TimePoint::fromNs(vsyncTime); - const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps(); - mVsyncTrackerCallback.onVsyncGenerated(vsyncTimePoint, mDisplayModePtr, renderRate); - } - return vsyncTime; + return snapToVsyncAlignedWithRenderRate(baseTime); } nsecs_t VSyncPredictor::snapToVsyncAlignedWithRenderRate(nsecs_t timePoint) const { diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index 919100363f..8fd7e6046d 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -37,11 +37,9 @@ public: * \param [in] minimumSamplesForPrediction The minimum number of samples to collect before * predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter * samples that fall outlierTolerancePercent from an anticipated vsync event. - * \param [in] IVsyncTrackerCallback The callback for the VSyncTracker. */ VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize, - size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent, - IVsyncTrackerCallback&); + size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent); ~VSyncPredictor(); bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex); @@ -106,7 +104,6 @@ private: size_t const kHistorySize; size_t const kMinimumSamplesForPrediction; size_t const kOutlierTolerancePercent; - IVsyncTrackerCallback& mVsyncTrackerCallback; std::mutex mutable mMutex; std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex); diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h index 417163f4e1..37bd4b4977 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -26,12 +26,6 @@ namespace android::scheduler { -struct IVsyncTrackerCallback { - virtual ~IVsyncTrackerCallback() = default; - virtual void onVsyncGenerated(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>, - Fps renderRate) = 0; -}; - /* * VSyncTracker is an interface for providing estimates on future Vsync signal times based on * historical vsync timing data. diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp index 3491600d8e..001938c756 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp @@ -57,11 +57,10 @@ private: }; VsyncSchedule::VsyncSchedule(ftl::NonNull<DisplayModePtr> modePtr, FeatureFlags features, - RequestHardwareVsync requestHardwareVsync, - IVsyncTrackerCallback& callback) + RequestHardwareVsync requestHardwareVsync) : mId(modePtr->getPhysicalDisplayId()), mRequestHardwareVsync(std::move(requestHardwareVsync)), - mTracker(createTracker(modePtr, callback)), + mTracker(createTracker(modePtr)), mDispatch(createDispatch(mTracker)), mController(createController(modePtr->getPhysicalDisplayId(), *mTracker, features)), mTracer(features.test(Feature::kTracePredictedVsync) @@ -115,15 +114,14 @@ void VsyncSchedule::dump(std::string& out) const { mDispatch->dump(out); } -VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(ftl::NonNull<DisplayModePtr> modePtr, - IVsyncTrackerCallback& callback) { +VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(ftl::NonNull<DisplayModePtr> modePtr) { // TODO(b/144707443): Tune constants. constexpr size_t kHistorySize = 20; constexpr size_t kMinSamplesForPrediction = 6; constexpr uint32_t kDiscardOutlierPercent = 20; return std::make_unique<VSyncPredictor>(modePtr, kHistorySize, kMinSamplesForPrediction, - kDiscardOutlierPercent, callback); + kDiscardOutlierPercent); } VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(TrackerPtr tracker) { diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h index 6f656d2029..85cd3e7c31 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.h +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h @@ -57,8 +57,7 @@ class VsyncSchedule final : public IVsyncSource { public: using RequestHardwareVsync = std::function<void(PhysicalDisplayId, bool enabled)>; - VsyncSchedule(ftl::NonNull<DisplayModePtr> modePtr, FeatureFlags, RequestHardwareVsync, - IVsyncTrackerCallback&); + VsyncSchedule(ftl::NonNull<DisplayModePtr> modePtr, FeatureFlags, RequestHardwareVsync); ~VsyncSchedule(); // IVsyncSource overrides: @@ -128,7 +127,7 @@ private: friend class android::VsyncScheduleTest; friend class android::fuzz::SchedulerFuzzer; - static TrackerPtr createTracker(ftl::NonNull<DisplayModePtr> modePtr, IVsyncTrackerCallback&); + static TrackerPtr createTracker(ftl::NonNull<DisplayModePtr> modePtr); static DispatchPtr createDispatch(TrackerPtr); static ControllerPtr createController(PhysicalDisplayId, VsyncTracker&, FeatureFlags); diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h index 12ee36e084..8673a2251c 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h @@ -47,6 +47,9 @@ struct ICompositor { virtual CompositeResultsPerDisplay composite(PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters&) = 0; + // Sends a hint about the expected present time + virtual void sendNotifyExpectedPresentHint(PhysicalDisplayId) = 0; + // Samples the composited frame via RegionSamplingThread. virtual void sample() = 0; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 47e7474cfb..89a341ff84 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1230,8 +1230,10 @@ status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& displayToken, return NO_ERROR; } -void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool force) { - const auto displayId = request.mode.modePtr->getPhysicalDisplayId(); +void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode) { + const auto mode = desiredMode.mode; + const auto displayId = mode.modePtr->getPhysicalDisplayId(); + ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str()); const auto display = getDisplayDeviceLocked(displayId); @@ -1240,10 +1242,9 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool return; } - const auto mode = request.mode; - const bool emitEvent = request.emitEvent; + const bool emitEvent = desiredMode.emitEvent; - switch (display->setDesiredMode(std::move(request), force)) { + switch (display->setDesiredMode(std::move(desiredMode))) { case DisplayDevice::DesiredModeAction::InitiateDisplayModeSwitch: // DisplayDevice::setDesiredMode updated the render rate, so inform Scheduler. mScheduler->setRenderRate(displayId, @@ -1429,7 +1430,8 @@ void SurfaceFlinger::initiateDisplayModeChanges() { to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(), to_string(display->getId()).c_str()); - if (display->getActiveMode() == desiredModeOpt->mode) { + if ((!FlagManager::getInstance().connected_display() || !desiredModeOpt->force) && + display->getActiveMode() == desiredModeOpt->mode) { applyActiveMode(display); continue; } @@ -2753,23 +2755,11 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay); } - const TimePoint expectedPresentTime = pacesetterTarget.expectedPresentTime(); // TODO(b/255601557) Update frameInterval per display - refreshArgs.frameInterval = mScheduler->getNextFrameInterval(pacesetterId, expectedPresentTime); + refreshArgs.frameInterval = + mScheduler->getNextFrameInterval(pacesetterId, pacesetterTarget.expectedPresentTime()); refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime(); refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0; - { - auto& notifyExpectedPresentData = mNotifyExpectedPresentMap[pacesetterId]; - auto lastExpectedPresentTimestamp = TimePoint::fromNs( - notifyExpectedPresentData.lastExpectedPresentTimestamp.load().ns()); - if (expectedPresentTime > lastExpectedPresentTimestamp) { - // If the values are not same, then hint is sent with newer value. - // And because composition always follows the notifyExpectedPresentIfRequired, we can - // skip updating the lastExpectedPresentTimestamp in this case. - notifyExpectedPresentData.lastExpectedPresentTimestamp - .compare_exchange_weak(lastExpectedPresentTimestamp, expectedPresentTime); - } - } // Store the present time just before calling to the composition engine so we could notify // the scheduler. const auto presentTime = systemTime(); @@ -2833,6 +2823,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( scheduleComposite(FrameHint::kNone); } + mNotifyExpectedPresentMap[pacesetterId].hintStatus = NotifyExpectedPresentHintStatus::Start; onCompositionPresented(pacesetterId, frameTargeters, presentTime); const bool hadGpuComposited = @@ -3293,7 +3284,11 @@ void SurfaceFlinger::commitTransactions() { std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( PhysicalDisplayId displayId) const { std::vector<HWComposer::HWCDisplayMode> hwcModes; - std::optional<hal::HWDisplayId> activeModeHwcId; + std::optional<hal::HWConfigId> activeModeHwcIdOpt; + + const bool isExternalDisplay = FlagManager::getInstance().connected_display() && + getHwComposer().getDisplayConnectionType(displayId) == + ui::DisplayConnectionType::External; int attempt = 0; constexpr int kMaxAttempts = 3; @@ -3301,10 +3296,81 @@ std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( hwcModes = getHwComposer().getModes(displayId, scheduler::RefreshRateSelector::kMinSupportedFrameRate .getPeriodNsecs()); - activeModeHwcId = getHwComposer().getActiveMode(displayId); + const auto activeModeHwcIdExp = getHwComposer().getActiveMode(displayId); + activeModeHwcIdOpt = activeModeHwcIdExp.value_opt(); + + if (isExternalDisplay && + activeModeHwcIdExp.has_error([](status_t error) { return error == NO_INIT; })) { + constexpr nsecs_t k59HzVsyncPeriod = 16949153; + constexpr nsecs_t k60HzVsyncPeriod = 16666667; + + // DM sets the initial mode for an external display to 1080p@60, but + // this comes after SF creates its own state (including the + // DisplayDevice). For now, pick the same mode in order to avoid + // inconsistent state and unnecessary mode switching. + // TODO (b/318534874): Let DM decide the initial mode. + // + // Try to find 1920x1080 @ 60 Hz + if (const auto iter = std::find_if(hwcModes.begin(), hwcModes.end(), + [](const auto& mode) { + return mode.width == 1920 && + mode.height == 1080 && + mode.vsyncPeriod == k60HzVsyncPeriod; + }); + iter != hwcModes.end()) { + activeModeHwcIdOpt = iter->hwcId; + break; + } + + // Try to find 1920x1080 @ 59-60 Hz + if (const auto iter = std::find_if(hwcModes.begin(), hwcModes.end(), + [](const auto& mode) { + return mode.width == 1920 && + mode.height == 1080 && + mode.vsyncPeriod >= k60HzVsyncPeriod && + mode.vsyncPeriod <= k59HzVsyncPeriod; + }); + iter != hwcModes.end()) { + activeModeHwcIdOpt = iter->hwcId; + break; + } + + // The display does not support 1080p@60, and this is the last attempt to pick a display + // mode. Prefer 60 Hz if available, with the closest resolution to 1080p. + if (attempt + 1 == kMaxAttempts) { + std::vector<HWComposer::HWCDisplayMode> hwcModeOpts; + + for (const auto& mode : hwcModes) { + if (mode.width <= 1920 && mode.height <= 1080 && + mode.vsyncPeriod >= k60HzVsyncPeriod && + mode.vsyncPeriod <= k59HzVsyncPeriod) { + hwcModeOpts.push_back(mode); + } + } + + if (const auto iter = std::max_element(hwcModeOpts.begin(), hwcModeOpts.end(), + [](const auto& a, const auto& b) { + const auto aSize = a.width * a.height; + const auto bSize = b.width * b.height; + if (aSize < bSize) + return true; + else if (aSize == bSize) + return a.vsyncPeriod > b.vsyncPeriod; + else + return false; + }); + iter != hwcModeOpts.end()) { + activeModeHwcIdOpt = iter->hwcId; + break; + } - const auto isActiveMode = [activeModeHwcId](const HWComposer::HWCDisplayMode& mode) { - return mode.hwcId == activeModeHwcId; + // hwcModeOpts was empty, use hwcModes[0] as the last resort + activeModeHwcIdOpt = hwcModes[0].hwcId; + } + } + + const auto isActiveMode = [activeModeHwcIdOpt](const HWComposer::HWCDisplayMode& mode) { + return mode.hwcId == activeModeHwcIdOpt; }; if (std::any_of(hwcModes.begin(), hwcModes.end(), isActiveMode)) { @@ -3314,7 +3380,7 @@ std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( if (attempt == kMaxAttempts) { const std::string activeMode = - activeModeHwcId ? std::to_string(*activeModeHwcId) : "unknown"s; + activeModeHwcIdOpt ? std::to_string(*activeModeHwcIdOpt) : "unknown"s; ALOGE("HWC failed to report an active mode that is supported: activeModeHwcId=%s, " "hwcModes={%s}", activeMode.c_str(), base::Join(hwcModes, ", ").c_str()); @@ -3358,10 +3424,14 @@ std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( // Keep IDs if modes have not changed. const auto& modes = sameModes ? oldModes : newModes; const DisplayModePtr activeMode = - std::find_if(modes.begin(), modes.end(), [activeModeHwcId](const auto& pair) { - return pair.second->getHwcId() == activeModeHwcId; + std::find_if(modes.begin(), modes.end(), [activeModeHwcIdOpt](const auto& pair) { + return pair.second->getHwcId() == activeModeHwcIdOpt; })->second; + if (isExternalDisplay) { + ALOGI("External display %s initial mode: {%s}", to_string(displayId).c_str(), + to_string(*activeMode).c_str()); + } return {modes, activeMode}; } @@ -3666,6 +3736,27 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, } mDisplays.try_emplace(displayToken, std::move(display)); + + // For an external display, loadDisplayModes already attempted to select the same mode + // as DM, but SF still needs to be updated to match. + // TODO (b/318534874): Let DM decide the initial mode. + if (const auto& physical = state.physical; + mScheduler && physical && FlagManager::getInstance().connected_display()) { + const bool isInternalDisplay = mPhysicalDisplays.get(physical->id) + .transform(&PhysicalDisplay::isInternal) + .value_or(false); + + if (!isInternalDisplay) { + auto activeModePtr = physical->activeMode; + const auto fps = activeModePtr->getPeakFps(); + + setDesiredMode( + {.mode = scheduler::FrameRateMode{fps, + ftl::as_non_null(std::move(activeModePtr))}, + .emitEvent = false, + .force = true}); + } + } } void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) { @@ -3775,6 +3866,9 @@ void SurfaceFlinger::processDisplayChangesLocked() { mVisibleRegionsDirty = true; mUpdateInputInfo = true; + // Apply the current color matrix to any added or changed display. + mCurrentState.colorMatrixChanged = true; + // find the displays that were removed // (ie: in drawing state but not in current state) // also handle displays that changed @@ -4111,8 +4205,9 @@ void SurfaceFlinger::onChoreographerAttached() { } } -void SurfaceFlinger::onVsyncGenerated(TimePoint expectedPresentTime, - ftl::NonNull<DisplayModePtr> modePtr, Fps renderRate) { +void SurfaceFlinger::onExpectedPresentTimePosted(TimePoint expectedPresentTime, + ftl::NonNull<DisplayModePtr> modePtr, + Fps renderRate) { const auto vsyncPeriod = modePtr->getVsyncRate().getPeriod(); const auto timeoutOpt = [&]() -> std::optional<Period> { const auto vrrConfig = modePtr->getVrrConfig(); @@ -4133,45 +4228,91 @@ void SurfaceFlinger::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId TimePoint expectedPresentTime, Fps frameInterval, std::optional<Period> timeoutOpt) { - { - auto& data = mNotifyExpectedPresentMap[displayId]; - const auto lastExpectedPresentTimestamp = data.lastExpectedPresentTimestamp.load(); - const auto lastFrameInterval = data.lastFrameInterval; - data.lastFrameInterval = frameInterval; - const auto threshold = Duration::fromNs(vsyncPeriod.ns() / 2); - - const constexpr nsecs_t kOneSecondNs = - std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); - const auto timeout = Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0 ? timeoutOpt->ns() - : kOneSecondNs); - const bool frameIntervalIsOnCadence = - isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp, - lastFrameInterval, timeout, threshold); - - const bool expectedPresentWithinTimeout = - isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp, - timeoutOpt, threshold); - - using fps_approx_ops::operator!=; - if (frameIntervalIsOnCadence && frameInterval != lastFrameInterval) { - data.lastExpectedPresentTimestamp = expectedPresentTime; - } - - if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) { + auto& data = mNotifyExpectedPresentMap[displayId]; + const auto lastExpectedPresentTimestamp = data.lastExpectedPresentTimestamp; + const auto lastFrameInterval = data.lastFrameInterval; + data.lastFrameInterval = frameInterval; + data.lastExpectedPresentTimestamp = expectedPresentTime; + const auto threshold = Duration::fromNs(vsyncPeriod.ns() / 2); + + const constexpr nsecs_t kOneSecondNs = + std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); + const auto timeout = + Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0 ? timeoutOpt->ns() : kOneSecondNs); + const bool frameIntervalIsOnCadence = + isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp, + lastFrameInterval, timeout, threshold); + + const bool expectedPresentWithinTimeout = + isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp, + timeoutOpt, threshold); + if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) { + return; + } + + auto hintStatus = data.hintStatus.load(); + if (!expectedPresentWithinTimeout) { + if (hintStatus != NotifyExpectedPresentHintStatus::Sent || + (timeoutOpt && timeoutOpt->ns() == 0)) { + // Send the hint immediately if timeout, as the hint gets + // delayed otherwise, as the frame is scheduled close + // to the actual present. + if (data.hintStatus + .compare_exchange_strong(hintStatus, + NotifyExpectedPresentHintStatus::ScheduleOnTx)) { + scheduleNotifyExpectedPresentHint(displayId); + } return; } - data.lastExpectedPresentTimestamp = expectedPresentTime; + } + + if (hintStatus != NotifyExpectedPresentHintStatus::Start) { + return; + } + data.hintStatus.store(NotifyExpectedPresentHintStatus::ScheduleOnPresent); + mScheduler->scheduleFrame(); +} + +void SurfaceFlinger::scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId) { + auto itr = mNotifyExpectedPresentMap.find(displayId); + if (itr == mNotifyExpectedPresentMap.end()) { + return; } const char* const whence = __func__; - static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) { - const auto status = getHwComposer().notifyExpectedPresent(displayId, expectedPresentTime, - frameInterval); + const auto sendHint = [=, this]() { + auto& data = mNotifyExpectedPresentMap.at(displayId); + data.hintStatus.store(NotifyExpectedPresentHintStatus::Sent); + const auto status = + getHwComposer().notifyExpectedPresent(displayId, data.lastExpectedPresentTimestamp, + data.lastFrameInterval); if (status != NO_ERROR) { ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, whence, displayId.value); } - })); + }; + + if (itr->second.hintStatus == NotifyExpectedPresentHintStatus::ScheduleOnTx) { + return static_cast<void>(mScheduler->schedule([=, + this]() FTL_FAKE_GUARD(kMainThreadContext) { + auto& data = mNotifyExpectedPresentMap.at(displayId); + auto scheduleHintOnTx = NotifyExpectedPresentHintStatus::ScheduleOnTx; + if (data.hintStatus.compare_exchange_strong(scheduleHintOnTx, + NotifyExpectedPresentHintStatus::Sent)) { + sendHint(); + } + })); + } + sendHint(); +} + +void SurfaceFlinger::sendNotifyExpectedPresentHint(PhysicalDisplayId displayId) { + if (auto itr = mNotifyExpectedPresentMap.find(displayId); + itr == mNotifyExpectedPresentMap.end() || + itr->second.hintStatus != NotifyExpectedPresentHintStatus::ScheduleOnPresent) { + return; + } + scheduleNotifyExpectedPresentHint(displayId); } void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { @@ -4213,8 +4354,7 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this), static_cast<ISchedulerCallback&>(*this), features, - getFactory(), activeRefreshRate, *mTimeStats, - static_cast<IVsyncTrackerCallback&>(*this)); + getFactory(), activeRefreshRate, *mTimeStats); mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector()); if (FlagManager::getInstance().vrr_config()) { mScheduler->setRenderRate(display->getPhysicalId(), activeMode.fps); @@ -4273,7 +4413,6 @@ void SurfaceFlinger::doCommitTransactions() { } mDrawingState = mCurrentState; - // clear the "changed" flags in current state mCurrentState.colorMatrixChanged = false; if (mVisibleRegionsDirty) { @@ -5733,7 +5872,7 @@ status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, gui::CreateSurface case ISurfaceComposerClient::eFXSurfaceContainer: case ISurfaceComposerClient::eFXSurfaceBufferState: args.flags |= ISurfaceComposerClient::eNoColorFill; - FMT_FALLTHROUGH; + [[fallthrough]]; case ISurfaceComposerClient::eFXSurfaceEffect: { result = createBufferStateLayer(args, &outResult.handle, &layer); std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter(); @@ -8159,14 +8298,23 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( : ftl::yield(present()).share(); for (auto& [layer, layerFE] : layers) { - layer->onLayerDisplayed(ftl::Future(presentFuture) - .then([layerFE = std::move(layerFE)](FenceResult) { - return layerFE->stealCompositionResult() - .releaseFences.back() - .first.get(); - }) - .share(), - ui::INVALID_LAYER_STACK); + layer->onLayerDisplayed(presentFuture, ui::INVALID_LAYER_STACK, + [layerFE = std::move(layerFE)](FenceResult) { + if (FlagManager::getInstance() + .screenshot_fence_preservation()) { + const auto compositionResult = + layerFE->stealCompositionResult(); + const auto& fences = compositionResult.releaseFences; + // CompositionEngine may choose to cull layers that + // aren't visible, so pass a non-fence. + return fences.empty() ? Fence::NO_FENCE + : fences.back().first.get(); + } else { + return layerFE->stealCompositionResult() + .releaseFences.back() + .first.get(); + } + }); } return presentFuture; @@ -8333,7 +8481,7 @@ status_t SurfaceFlinger::applyRefreshRateSelectorPolicy( return INVALID_OPERATION; } - setDesiredMode({std::move(preferredMode), .emitEvent = true}, force); + setDesiredMode({std::move(preferredMode), .emitEvent = true, .force = force}); // Update the frameRateOverride list as the display render rate might have changed if (mScheduler->updateFrameRateOverrides(scheduler::GlobalSignals{}, preferredFps)) { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index c8e2a4d70a..be057979f9 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -199,8 +199,7 @@ class SurfaceFlinger : public BnSurfaceComposer, private HWC2::ComposerCallback, private ICompositor, private scheduler::ISchedulerCallback, - private compositionengine::ICEPowerCallback, - private scheduler::IVsyncTrackerCallback { + private compositionengine::ICEPowerCallback { public: struct SkipInitializationTag {}; @@ -686,14 +685,12 @@ private: void kernelTimerChanged(bool expired) override; void triggerOnFrameRateOverridesChanged() override; void onChoreographerAttached() override; + void onExpectedPresentTimePosted(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>, + Fps renderRate) override; // ICEPowerCallback overrides: void notifyCpuLoadUp() override; - // IVsyncTrackerCallback overrides - void onVsyncGenerated(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>, - Fps renderRate) override; - // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates. void toggleKernelIdleTimer() REQUIRES(mStateLock); @@ -719,7 +716,7 @@ private: // Show hdr sdr ratio overlay bool mHdrSdrRatioOverlay = false; - void setDesiredMode(display::DisplayModeRequest&&, bool force = false) REQUIRES(mStateLock); + void setDesiredMode(display::DisplayModeRequest&&) REQUIRES(mStateLock); status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId, Fps minFps, Fps maxFps); @@ -1482,14 +1479,28 @@ private: ftl::SmallMap<int64_t, sp<SurfaceControl>, 3> mMirrorMapForDebug; // NotifyExpectedPresentHint + enum class NotifyExpectedPresentHintStatus { + // Represents that framework can start sending hint if required. + Start, + // Represents that the hint is already sent. + Sent, + // Represents that the hint will be scheduled with a new frame. + ScheduleOnPresent, + // Represents that a hint will be sent instantly by scheduling on the main thread. + ScheduleOnTx + }; struct NotifyExpectedPresentData { - // lastExpectedPresentTimestamp is read and write from multiple threads such as - // main thread, EventThread, MessageQueue. And is atomic for that reason. - std::atomic<TimePoint> lastExpectedPresentTimestamp{}; + TimePoint lastExpectedPresentTimestamp{}; Fps lastFrameInterval{}; + // hintStatus is read and write from multiple threads such as + // main thread, EventThread. And is atomic for that reason. + std::atomic<NotifyExpectedPresentHintStatus> hintStatus = + NotifyExpectedPresentHintStatus::Start; }; std::unordered_map<PhysicalDisplayId, NotifyExpectedPresentData> mNotifyExpectedPresentMap; - + void sendNotifyExpectedPresentHint(PhysicalDisplayId displayId) override + REQUIRES(kMainThreadContext); + void scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId); void notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod, TimePoint expectedPresentTime, Fps frameInterval, std::optional<Period> timeoutOpt); diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp index 6a155c17df..7b5298c82e 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.cpp +++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp @@ -25,6 +25,7 @@ #include "TransactionCallbackInvoker.h" #include "BackgroundExecutor.h" +#include "Utils/FenceUtils.h" #include <cinttypes> @@ -127,33 +128,8 @@ status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& sp<IBinder> surfaceControl = handle->surfaceControl.promote(); if (surfaceControl) { sp<Fence> prevFence = nullptr; - for (const auto& future : handle->previousReleaseFences) { - sp<Fence> currentFence = future.get().value_or(Fence::NO_FENCE); - if (prevFence == nullptr && currentFence->getStatus() != Fence::Status::Invalid) { - prevFence = std::move(currentFence); - } else if (prevFence != nullptr) { - // If both fences are signaled or both are unsignaled, we need to merge - // them to get an accurate timestamp. - if (prevFence->getStatus() != Fence::Status::Invalid && - prevFence->getStatus() == currentFence->getStatus()) { - char fenceName[32] = {}; - snprintf(fenceName, 32, "%.28s", handle->name.c_str()); - sp<Fence> mergedFence = Fence::merge(fenceName, prevFence, currentFence); - if (mergedFence->isValid()) { - prevFence = std::move(mergedFence); - } - } else if (currentFence->getStatus() == Fence::Status::Unsignaled) { - // If one fence has signaled and the other hasn't, the unsignaled - // fence will approximately correspond with the correct timestamp. - // There's a small race if both fences signal at about the same time - // and their statuses are retrieved with unfortunate timing. However, - // by this point, they will have both signaled and only the timestamp - // will be slightly off; any dependencies after this point will - // already have been met. - prevFence = std::move(currentFence); - } - } + mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence); } handle->previousReleaseFence = prevFence; handle->previousReleaseFences.clear(); diff --git a/services/surfaceflinger/Utils/FenceUtils.h b/services/surfaceflinger/Utils/FenceUtils.h new file mode 100644 index 0000000000..f6f70062df --- /dev/null +++ b/services/surfaceflinger/Utils/FenceUtils.h @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <ui/Fence.h> + +namespace android { + +// TODO: measure if Fence::merge is cheaper +inline void mergeFence(const char* debugName, sp<Fence>&& incomingFence, sp<Fence>& prevFence) { + if (prevFence == nullptr && incomingFence->getStatus() != Fence::Status::Invalid) { + prevFence = std::move(incomingFence); + } else if (prevFence != nullptr) { + // If both fences are signaled or both are unsignaled, we need to merge + // them to get an accurate timestamp. + if (prevFence->getStatus() != Fence::Status::Invalid && + prevFence->getStatus() == incomingFence->getStatus()) { + char fenceName[32] = {}; + snprintf(fenceName, 32, "%.28s", debugName); + sp<Fence> mergedFence = Fence::merge(fenceName, prevFence, incomingFence); + if (mergedFence->isValid()) { + prevFence = std::move(mergedFence); + } + } else if (incomingFence->getStatus() == Fence::Status::Unsignaled) { + // If one fence has signaled and the other hasn't, the unsignaled + // fence will approximately correspond with the correct timestamp. + // There's a small race if both fences signal at about the same time + // and their statuses are retrieved with unfortunate timing. However, + // by this point, they will have both signaled and only the timestamp + // will be slightly off; any dependencies after this point will + // already have been met. + prevFence = std::move(incomingFence); + } + } +} + +} // namespace android diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index 255b517e3e..b07e7ace06 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -128,6 +128,7 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(fp16_client_target); DUMP_READ_ONLY_FLAG(game_default_frame_rate); DUMP_READ_ONLY_FLAG(enable_layer_command_batching); + DUMP_READ_ONLY_FLAG(screenshot_fence_preservation); DUMP_READ_ONLY_FLAG(vulkan_renderengine); #undef DUMP_READ_ONLY_FLAG #undef DUMP_SERVER_FLAG @@ -203,6 +204,7 @@ FLAG_MANAGER_READ_ONLY_FLAG(display_protected, "") FLAG_MANAGER_READ_ONLY_FLAG(fp16_client_target, "debug.sf.fp16_client_target") 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") /// Trunk stable server flags /// diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index 15938c012c..2a30a40730 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -68,6 +68,7 @@ public: bool fp16_client_target() const; bool game_default_frame_rate() const; bool enable_layer_command_batching() const; + bool screenshot_fence_preservation() const; bool vulkan_renderengine() const; protected: diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp index 68237c8dd6..682079f979 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp @@ -143,7 +143,6 @@ private: void invokeComposerHal2_2(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); void invokeComposerHal2_3(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); void invokeComposerHal2_4(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); - void getDisplayVsyncPeriod(); void setActiveModeWithConstraints(); void getDisplayIdentificationData(); void dumpHwc(); @@ -202,11 +201,6 @@ Display DisplayHardwareFuzzer::createVirtualDisplay(Hwc2::AidlComposer* composer return display; } -void DisplayHardwareFuzzer::getDisplayVsyncPeriod() { - nsecs_t outVsyncPeriod; - mHwc.getDisplayVsyncPeriod(mPhysicalDisplayId, &outVsyncPeriod); -} - void DisplayHardwareFuzzer::setActiveModeWithConstraints() { hal::VsyncPeriodChangeTimeline outTimeline; mHwc.setActiveModeWithConstraints(mPhysicalDisplayId, kActiveConfig, {} /*constraints*/, @@ -617,8 +611,7 @@ void DisplayHardwareFuzzer::invokeComposer() { mHwc.getDisplayConnectionType(mPhysicalDisplayId); mHwc.isVsyncPeriodSwitchSupported(mPhysicalDisplayId); - - getDisplayVsyncPeriod(); + mHwc.getDisplayVsyncPeriod(mPhysicalDisplayId); setActiveModeWithConstraints(); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h index fa79956131..0d15f6264f 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h @@ -228,9 +228,9 @@ public: VsyncSchedule::TrackerPtr tracker, std::shared_ptr<RefreshRateSelector> selectorPtr, surfaceflinger::Factory& factory, TimeStats& timeStats, - ISchedulerCallback& callback, IVsyncTrackerCallback& vsyncTrackerCallback) + ISchedulerCallback& callback) : Scheduler(*this, callback, Feature::kContentDetection, factory, - selectorPtr->getActiveMode().fps, timeStats, vsyncTrackerCallback) { + selectorPtr->getActiveMode().fps, timeStats) { const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); registerDisplayInternal(displayId, std::move(selectorPtr), std::shared_ptr<VsyncSchedule>( @@ -289,6 +289,8 @@ private: } void sample() override {} + void sendNotifyExpectedPresentHint(PhysicalDisplayId) override {} + // MessageQueue overrides: void scheduleFrame() override {} void postMessage(sp<MessageHandler>&& handler) override { handler->handleMessage(Message()); } @@ -396,8 +398,7 @@ public: } // namespace surfaceflinger::test // TODO(b/189053744) : Create a common test/mock library for surfaceflinger -class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback, - private scheduler::IVsyncTrackerCallback { +class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback { public: using HotplugEvent = SurfaceFlinger::HotplugEvent; @@ -660,7 +661,6 @@ public: std::unique_ptr<EventThread> appEventThread, std::unique_ptr<EventThread> sfEventThread, scheduler::ISchedulerCallback* callback = nullptr, - scheduler::IVsyncTrackerCallback* vsyncTrackerCallback = nullptr, bool hasMultipleModes = false) { constexpr DisplayModeId kModeId60{0}; DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz)); @@ -675,8 +675,7 @@ public: mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), std::move(vsyncTracker), mRefreshRateSelector, mFactory, *mFlinger->mTimeStats, - *(callback ?: this), - *(vsyncTrackerCallback ?: this)); + *(callback ?: this)); mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread)); mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread)); @@ -796,9 +795,7 @@ private: void kernelTimerChanged(bool) override {} void triggerOnFrameRateOverridesChanged() override {} void onChoreographerAttached() override {} - - // IVsyncTrackerCallback overrides - void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {} + void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {} surfaceflinger::test::Factory mFactory; sp<SurfaceFlinger> mFlinger = diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp index a6b12d0a12..8cd6e1bb3a 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp @@ -103,6 +103,7 @@ struct EventThreadCallback : public IEventThreadCallback { bool throttleVsync(TimePoint, uid_t) override { return false; } Period getVsyncPeriod(uid_t) override { return kSyncPeriod; } void resync() override {} + void onExpectedPresentTimePosted(TimePoint) override {} }; void SchedulerFuzzer::fuzzEventThread() { @@ -180,21 +181,15 @@ void SchedulerFuzzer::fuzzVSyncDispatchTimerQueue() { dump<scheduler::VSyncDispatchTimerQueueEntry>(&entry, &mFdp); } -struct VsyncTrackerCallback : public scheduler::IVsyncTrackerCallback { - void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {} -}; - void SchedulerFuzzer::fuzzVSyncPredictor() { uint16_t now = mFdp.ConsumeIntegral<uint16_t>(); uint16_t historySize = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX); uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX); nsecs_t idealPeriod = mFdp.ConsumeIntegralInRange<nsecs_t>(1, UINT32_MAX); - VsyncTrackerCallback callback; const auto mode = ftl::as_non_null( mock::createDisplayMode(DisplayModeId(0), Fps::fromPeriodNsecs(idealPeriod))); scheduler::VSyncPredictor tracker{mode, historySize, minimumSamplesForPrediction, - mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/, - callback}; + mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/}; uint16_t period = mFdp.ConsumeIntegral<uint16_t>(); tracker.setDisplayModePtr(ftl::as_non_null( mock::createDisplayMode(DisplayModeId(0), Fps::fromPeriodNsecs(period)))); diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig index f097a13d0e..fcbef01bfa 100644 --- a/services/surfaceflinger/surfaceflinger_flags.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags.aconfig @@ -166,3 +166,11 @@ flag { bug: "293371537" is_fixed_read_only: true } + +flag { + name: "screenshot_fence_preservation" + namespace: "core_graphics" + description: "Bug fix around screenshot fences" + bug: "302703346" + is_fixed_read_only: true +} diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 5809ea02f8..7c191a50e7 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -95,6 +95,7 @@ cc_test { "MessageQueueTest.cpp", "PowerAdvisorTest.cpp", "SmallAreaDetectionAllowMappingsTest.cpp", + "SurfaceFlinger_ColorMatrixTest.cpp", "SurfaceFlinger_CreateDisplayTest.cpp", "SurfaceFlinger_DestroyDisplayTest.cpp", "SurfaceFlinger_DisplayModeSwitching.cpp", diff --git a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h new file mode 100644 index 0000000000..34e4ba5e78 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h @@ -0,0 +1,86 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <compositionengine/Display.h> +#include <compositionengine/mock/DisplaySurface.h> +#include <renderengine/mock/RenderEngine.h> + +#include "TestableSurfaceFlinger.h" +#include "mock/DisplayHardware/MockComposer.h" +#include "mock/DisplayHardware/MockPowerAdvisor.h" +#include "mock/MockTimeStats.h" +#include "mock/system/window/MockNativeWindow.h" + +namespace android { + +// Minimal setup to use TestableSurfaceFlinger::commitAndComposite. +struct CommitAndCompositeTest : testing::Test { + void SetUp() override { + mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID}); + mComposer = new Hwc2::mock::Composer(); + mPowerAdvisor = new Hwc2::mock::PowerAdvisor(); + mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); + mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats)); + mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); + mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor)); + + constexpr bool kIsPrimary = true; + FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary) + .setPowerMode(hal::PowerMode::ON) + .inject(&mFlinger, mComposer); + auto compostionEngineDisplayArgs = + compositionengine::DisplayCreationArgsBuilder() + .setId(DEFAULT_DISPLAY_ID) + .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT}) + .setPowerAdvisor(mPowerAdvisor) + .setName("Internal display") + .build(); + auto compositionDisplay = + compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(), + std::move(compostionEngineDisplayArgs)); + mDisplay = FakeDisplayDeviceInjector(mFlinger, compositionDisplay, + ui::DisplayConnectionType::Internal, HWC_DISPLAY, + kIsPrimary) + .setDisplaySurface(mDisplaySurface) + .setNativeWindow(mNativeWindow) + .setPowerMode(hal::PowerMode::ON) + .setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector()) + .skipRegisterDisplay() + .inject(); + } + + using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; + using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; + + static constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; + static constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); + static constexpr int DEFAULT_DISPLAY_WIDTH = 1920; + static constexpr int DEFAULT_DISPLAY_HEIGHT = 1024; + + TestableSurfaceFlinger mFlinger; + renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); + sp<DisplayDevice> mDisplay; + sp<compositionengine::mock::DisplaySurface> mDisplaySurface = + sp<compositionengine::mock::DisplaySurface>::make(); + sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); + mock::TimeStats* mTimeStats = new mock::TimeStats(); + Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr; + Hwc2::mock::Composer* mComposer = nullptr; +}; + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index beb2147c98..7d8a30a727 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -25,6 +25,7 @@ #include <compositionengine/Display.h> #include <compositionengine/mock/DisplaySurface.h> +#include <ftl/future.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <gui/IProducerListener.h> @@ -227,14 +228,6 @@ void CompositionTest::captureScreenComposition() { LayerCase::cleanup(this); } -template <class T> -std::future<T> futureOf(T obj) { - std::promise<T> resultPromise; - std::future<T> resultFuture = resultPromise.get_future(); - resultPromise.set_value(std::move(obj)); - return resultFuture; -} - /* ------------------------------------------------------------------------ * Variants for each display configuration which can be tested */ @@ -327,13 +320,13 @@ struct BaseDisplayVariant { .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>&, const std::shared_ptr<renderengine::ExternalTexture>&, - base::unique_fd&&) -> std::future<FenceResult> { + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.clip); - return futureOf<FenceResult>(Fence::NO_FENCE); + return ftl::yield<FenceResult>(Fence::NO_FENCE); }); } @@ -378,14 +371,14 @@ struct BaseDisplayVariant { .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>&, const std::shared_ptr<renderengine::ExternalTexture>&, - base::unique_fd&&) -> std::future<FenceResult> { + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.clip); EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace); - return futureOf<FenceResult>(Fence::NO_FENCE); + return ftl::yield<FenceResult>(Fence::NO_FENCE); }); } @@ -578,7 +571,7 @@ struct BaseLayerProperties { .WillOnce([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, const std::shared_ptr<renderengine::ExternalTexture>&, - base::unique_fd&&) -> std::future<FenceResult> { + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -586,7 +579,8 @@ struct BaseLayerProperties { displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so gtet the back layer. - std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE); + ftl::Future<FenceResult> resultFuture = + ftl::yield<FenceResult>(Fence::NO_FENCE); if (layerSettings.empty()) { ADD_FAILURE() << "layerSettings was not expected to be empty in " "setupREBufferCompositionCommonCallExpectations " @@ -627,7 +621,7 @@ struct BaseLayerProperties { .WillOnce([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, const std::shared_ptr<renderengine::ExternalTexture>&, - base::unique_fd&&) -> std::future<FenceResult> { + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -635,7 +629,8 @@ struct BaseLayerProperties { displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so get the back layer. - std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE); + ftl::Future<FenceResult> resultFuture = + ftl::yield<FenceResult>(Fence::NO_FENCE); if (layerSettings.empty()) { ADD_FAILURE() << "layerSettings was not expected to be empty in " @@ -709,7 +704,7 @@ struct CommonSecureLayerProperties : public BaseLayerProperties<LayerProperties> .WillOnce([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, const std::shared_ptr<renderengine::ExternalTexture>&, - base::unique_fd&&) -> std::future<FenceResult> { + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -717,7 +712,8 @@ struct CommonSecureLayerProperties : public BaseLayerProperties<LayerProperties> displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so get the back layer. - std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE); + ftl::Future<FenceResult> resultFuture = + ftl::yield<FenceResult>(Fence::NO_FENCE); if (layerSettings.empty()) { ADD_FAILURE() << "layerSettings was not expected to be empty in " "setupInsecureREBufferCompositionCommonCallExpectations " diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index 13796650c8..fa31643df1 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -80,8 +80,7 @@ void DisplayTransactionTest::injectMockScheduler(PhysicalDisplayId displayId) { std::unique_ptr<EventThread>(mEventThread), std::unique_ptr<EventThread>(mSFEventThread), TestableSurfaceFlinger::DefaultDisplayMode{displayId}, - TestableSurfaceFlinger::SchedulerCallbackImpl::kMock, - TestableSurfaceFlinger::VsyncTrackerCallbackImpl::kMock); + TestableSurfaceFlinger::SchedulerCallbackImpl::kMock); } void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) { diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index 387d2f26e6..f26336a655 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -461,9 +461,11 @@ struct HwcDisplayVariant { ? IComposerClient::DisplayConnectionType::INTERNAL : IComposerClient::DisplayConnectionType::EXTERNAL; + using ::testing::AtLeast; EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), - Return(hal::V2_4::Error::NONE))); + .Times(AtLeast(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(CONNECTION_TYPE), + Return(hal::V2_4::Error::NONE))); } EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)) diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index 45db0c56a9..d5ec654c59 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -85,6 +85,7 @@ protected: bool throttleVsync(TimePoint, uid_t) override; Period getVsyncPeriod(uid_t) override; void resync() override; + void onExpectedPresentTimePosted(TimePoint) override; void setupEventThread(); sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder, @@ -107,6 +108,7 @@ protected: int32_t expectedConfigId, nsecs_t expectedVsyncPeriod); void expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t); + void expectOnExpectedPresentTimePosted(nsecs_t expectedPresentTime); void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId, std::vector<FrameRateOverride>); @@ -131,6 +133,7 @@ protected: mVSyncCallbackUnregisterRecorder; AsyncCallRecorder<void (*)()> mResyncCallRecorder; AsyncCallRecorder<void (*)(nsecs_t, uid_t)> mThrottleVsyncCallRecorder; + AsyncCallRecorder<void (*)(nsecs_t)> mOnExpectedPresentTimePostedRecorder; ConnectionEventRecorder mConnectionEventCallRecorder{0}; ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0}; @@ -190,6 +193,10 @@ void EventThreadTest::resync() { mResyncCallRecorder.recordCall(); } +void EventThreadTest::onExpectedPresentTimePosted(TimePoint expectedPresentTime) { + mOnExpectedPresentTimePostedRecorder.recordCall(expectedPresentTime.ns()); +} + void EventThreadTest::setupEventThread() { mTokenManager = std::make_unique<frametimeline::impl::TokenManager>(); mThread = std::make_unique<impl::EventThread>("EventThreadTest", mVsyncSchedule, @@ -244,6 +251,12 @@ void EventThreadTest::expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid EXPECT_EQ(uid, std::get<1>(args.value())); } +void EventThreadTest::expectOnExpectedPresentTimePosted(nsecs_t expectedPresentTime) { + auto args = mOnExpectedPresentTimePostedRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + EXPECT_EQ(expectedPresentTime, std::get<0>(args.value())); +} + void EventThreadTest::expectVsyncEventReceivedByConnection( const char* name, ConnectionEventRecorder& connectionEventRecorder, nsecs_t expectedTimestamp, unsigned expectedCount) { @@ -410,6 +423,7 @@ TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) { onVSyncEvent(123, 456, 789); expectThrottleVsyncReceived(456, mConnectionUid); expectVsyncEventReceivedByConnection(123, 1u); + expectOnExpectedPresentTimePosted(456); // EventThread is requesting one more callback due to VsyncRequest::SingleSuppressCallback expectVSyncCallbackScheduleReceived(true); @@ -562,16 +576,19 @@ TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) { onVSyncEvent(123, 456, 789); expectThrottleVsyncReceived(456, mConnectionUid); expectVsyncEventReceivedByConnection(123, 1u); + expectOnExpectedPresentTimePosted(456); // A second event should go to the same places. onVSyncEvent(456, 123, 0); expectThrottleVsyncReceived(123, mConnectionUid); expectVsyncEventReceivedByConnection(456, 2u); + expectOnExpectedPresentTimePosted(123); // A third event should go to the same places. onVSyncEvent(789, 777, 111); expectThrottleVsyncReceived(777, mConnectionUid); expectVsyncEventReceivedByConnection(789, 3u); + expectOnExpectedPresentTimePosted(777); } TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) { diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index a5c0657868..a25804c909 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -104,7 +104,7 @@ TEST_F(HWComposerTest, isHeadless) { TEST_F(HWComposerTest, getActiveMode) { // Unknown display. - EXPECT_EQ(mHwc.getActiveMode(PhysicalDisplayId::fromPort(0)), std::nullopt); + EXPECT_EQ(mHwc.getActiveMode(PhysicalDisplayId::fromPort(0)), ftl::Unexpected(BAD_INDEX)); constexpr hal::HWDisplayId kHwcDisplayId = 2; expectHotplugConnect(kHwcDisplayId); @@ -117,14 +117,20 @@ TEST_F(HWComposerTest, getActiveMode) { EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _)) .WillOnce(Return(HalError::BAD_DISPLAY)); - EXPECT_EQ(mHwc.getActiveMode(info->id), std::nullopt); + EXPECT_EQ(mHwc.getActiveMode(info->id), ftl::Unexpected(UNKNOWN_ERROR)); + } + { + EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _)) + .WillOnce(Return(HalError::BAD_CONFIG)); + + EXPECT_EQ(mHwc.getActiveMode(info->id), ftl::Unexpected(NO_INIT)); } { constexpr hal::HWConfigId kConfigId = 42; EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _)) .WillOnce(DoAll(SetArgPointee<1>(kConfigId), Return(HalError::NONE))); - EXPECT_EQ(mHwc.getActiveMode(info->id), kConfigId); + EXPECT_EQ(mHwc.getActiveMode(info->id).value_opt(), kConfigId); } } diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp index 734fddb2a3..01762c13b6 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp @@ -165,12 +165,8 @@ protected: DisplayModeId(0)); mock::SchedulerCallback mSchedulerCallback; - mock::VsyncTrackerCallback mVsyncTrackerCallback; - TestableSurfaceFlinger mFlinger; - - TestableScheduler* mScheduler = - new TestableScheduler(mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback); + TestableScheduler* mScheduler = new TestableScheduler(mSelector, mFlinger, mSchedulerCallback); }; namespace { diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index 9456e37f5c..bc06a31173 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -146,11 +146,8 @@ protected: DisplayModeId(0)); mock::SchedulerCallback mSchedulerCallback; - - mock::VsyncTrackerCallback mVsyncTrackerCallback; TestableSurfaceFlinger mFlinger; - TestableScheduler* mScheduler = - new TestableScheduler(mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback); + TestableScheduler* mScheduler = new TestableScheduler(mSelector, mFlinger, mSchedulerCallback); }; namespace { diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp index 22cfbd8d8a..9fe9ee8515 100644 --- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp @@ -64,10 +64,8 @@ protected: HI_FPS)), DisplayModeId(0)); mock::SchedulerCallback mSchedulerCallback; - mock::VsyncTrackerCallback mVsyncTrackerCallback; TestableSurfaceFlinger mFlinger; - TestableScheduler* mScheduler = - new TestableScheduler(mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback); + TestableScheduler* mScheduler = new TestableScheduler(mSelector, mFlinger, mSchedulerCallback); }; namespace { diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 13b47ffa4c..3baa48d002 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -1237,4 +1237,25 @@ TEST_F(LayerSnapshotTest, NonVisibleLayerWithInput) { EXPECT_TRUE(foundInputLayer); } +TEST_F(LayerSnapshotTest, canOccludePresentation) { + setFlags(12, layer_state_t::eCanOccludePresentation, layer_state_t::eCanOccludePresentation); + LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), + .layerLifecycleManager = mLifecycleManager, + .includeMetadata = false, + .displays = mFrontEndDisplayInfos, + .displayChanges = false, + .globalShadowSettings = globalShadowSettings, + .supportsBlur = true, + .supportedLayerGenericMetadata = {}, + .genericLayerMetadataKeyMap = {}}; + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_EQ(getSnapshot(1)->inputInfo.canOccludePresentation, false); + + // ensure we can set the property on the window info for layer and all its children + EXPECT_EQ(getSnapshot(12)->inputInfo.canOccludePresentation, true); + EXPECT_EQ(getSnapshot(121)->inputInfo.canOccludePresentation, true); + EXPECT_EQ(getSnapshot(1221)->inputInfo.canOccludePresentation, true); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp index 249ed4017d..f5661fccf5 100644 --- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp @@ -41,6 +41,7 @@ struct NoOpCompositor final : ICompositor { return {}; } void sample() override {} + void sendNotifyExpectedPresentHint(PhysicalDisplayId) {} } gNoOpCompositor; class TestableMessageQueue : public impl::MessageQueue { diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp index 9c66a97573..415b0d2edf 100644 --- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp +++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp @@ -74,12 +74,14 @@ void PowerAdvisorTest::SetUp() { void PowerAdvisorTest::startPowerHintSession(bool returnValidSession) { mMockPowerHintSession = ndk::SharedRefBase::make<NiceMock<MockIPowerHintSession>>(); if (returnValidSession) { - ON_CALL(*mMockPowerHalController, createHintSession) - .WillByDefault( - Return(HalResult<std::shared_ptr<IPowerHintSession>>:: - fromStatus(binder::Status::ok(), mMockPowerHintSession))); + ON_CALL(*mMockPowerHalController, createHintSessionWithConfig) + .WillByDefault(DoAll(SetArgPointee<5>(aidl::android::hardware::power::SessionConfig{ + .id = 12}), + Return(HalResult<std::shared_ptr<IPowerHintSession>>:: + fromStatus(binder::Status::ok(), + mMockPowerHintSession)))); } else { - ON_CALL(*mMockPowerHalController, createHintSession) + ON_CALL(*mMockPowerHalController, createHintSessionWithConfig) .WillByDefault(Return(HalResult<std::shared_ptr<IPowerHintSession>>:: fromStatus(binder::Status::ok(), nullptr))); } @@ -283,7 +285,7 @@ TEST_F(PowerAdvisorTest, hintSessionValidWhenNullFromPowerHAL) { } TEST_F(PowerAdvisorTest, hintSessionOnlyCreatedOnce) { - EXPECT_CALL(*mMockPowerHalController, createHintSession(_, _, _, _)).Times(1); + EXPECT_CALL(*mMockPowerHalController, createHintSessionWithConfig(_, _, _, _, _, _)).Times(1); mPowerAdvisor->onBootFinished(); startPowerHintSession(); mPowerAdvisor->startPowerHintSession({1, 2, 3}); @@ -335,7 +337,7 @@ TEST_F(PowerAdvisorTest, hintSessionTestNotifyReportRace) { return ndk::ScopedAStatus::fromExceptionCode(-127); }); - ON_CALL(*mMockPowerHalController, createHintSession) + ON_CALL(*mMockPowerHalController, createHintSessionWithConfig) .WillByDefault(Return( HalResult<std::shared_ptr<IPowerHintSession>>:: fromStatus(ndk::ScopedAStatus::fromExceptionCode(-127), nullptr))); diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index 1e526ba348..c03cbd7142 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -1624,6 +1624,7 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_HighH EXPECT_EQ(120_Hz, actualFrameRateMode.fps); EXPECT_EQ(kModeId120, actualFrameRateMode.modePtr->getId()); + // No touch boost, for example a game that uses setFrameRate(30, default compatibility). lr1.vote = LayerVoteType::ExplicitCategory; lr1.frameRateCategory = FrameRateCategory::HighHint; lr1.name = "ExplicitCategory HighHint"; @@ -1652,8 +1653,9 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_HighH lr2.frameRateCategory = FrameRateCategory::Low; lr2.name = "ExplicitCategory Low"; actualFrameRateMode = selector.getBestFrameRateMode(layers); - EXPECT_EQ(30_Hz, actualFrameRateMode.fps); - EXPECT_EQ(kModeId30, actualFrameRateMode.modePtr->getId()); + // Gets touch boost + EXPECT_EQ(120_Hz, actualFrameRateMode.fps); + EXPECT_EQ(kModeId120, actualFrameRateMode.modePtr->getId()); lr1.vote = LayerVoteType::ExplicitCategory; lr1.frameRateCategory = FrameRateCategory::HighHint; diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 6986689463..10c584871e 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -95,10 +95,8 @@ protected: kDisplay1Mode60->getId()); mock::SchedulerCallback mSchedulerCallback; - mock::VsyncTrackerCallback mVsyncTrackerCallback; TestableSurfaceFlinger mFlinger; - TestableScheduler* mScheduler = - new TestableScheduler{mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback}; + TestableScheduler* mScheduler = new TestableScheduler{mSelector, mFlinger, mSchedulerCallback}; surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder; ConnectionHandle mConnectionHandle; @@ -516,6 +514,7 @@ TEST_F(SchedulerTest, onFrameSignalMultipleDisplays) { } void sample() override {} + void sendNotifyExpectedPresentHint(PhysicalDisplayId) override {} } compositor(*mScheduler); mScheduler->doFrameSignal(compositor, VsyncId(42)); @@ -568,7 +567,7 @@ TEST_F(SchedulerTest, nextFrameIntervalTest) { frameRate.getPeriodNsecs())})); std::shared_ptr<VSyncPredictor> vrrTracker = std::make_shared<VSyncPredictor>(kMode, kHistorySize, kMinimumSamplesForPrediction, - kOutlierTolerancePercent, mVsyncTrackerCallback); + kOutlierTolerancePercent); std::shared_ptr<RefreshRateSelector> vrrSelectorPtr = std::make_shared<RefreshRateSelector>(makeModes(kMode), kMode->getId()); TestableScheduler scheduler{std::make_unique<android::mock::VsyncController>(), @@ -576,8 +575,7 @@ TEST_F(SchedulerTest, nextFrameIntervalTest) { vrrSelectorPtr, mFlinger.getFactory(), mFlinger.getTimeStats(), - mSchedulerCallback, - mVsyncTrackerCallback}; + mSchedulerCallback}; scheduler.registerDisplay(kMode->getPhysicalDisplayId(), vrrSelectorPtr, vrrTracker); vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp new file mode 100644 index 0000000000..f127213f0d --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp @@ -0,0 +1,63 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "LibSurfaceFlingerUnittests" + +#include "CommitAndCompositeTest.h" + +#define EXPECT_COLOR_MATRIX_CHANGED(current, drawing) \ + EXPECT_EQ(current, mFlinger.currentState().colorMatrixChanged); \ + EXPECT_EQ(drawing, mFlinger.drawingState().colorMatrixChanged); + +namespace android { + +class ColorMatrixTest : public CommitAndCompositeTest {}; + +TEST_F(ColorMatrixTest, colorMatrixChanged) { + EXPECT_COLOR_MATRIX_CHANGED(true, true); + mFlinger.mutableTransactionFlags() |= eTransactionNeeded; + + mFlinger.commitAndComposite(); + EXPECT_COLOR_MATRIX_CHANGED(false, false); + + mFlinger.setDaltonizerType(ColorBlindnessType::Deuteranomaly); + EXPECT_COLOR_MATRIX_CHANGED(true, false); + + mFlinger.commit(); + EXPECT_COLOR_MATRIX_CHANGED(false, true); + + mFlinger.commitAndComposite(); + EXPECT_COLOR_MATRIX_CHANGED(false, false); +} + +TEST_F(ColorMatrixTest, colorMatrixChangedAfterDisplayTransaction) { + EXPECT_COLOR_MATRIX_CHANGED(true, true); + mFlinger.mutableTransactionFlags() |= eTransactionNeeded; + + mFlinger.commitAndComposite(); + EXPECT_COLOR_MATRIX_CHANGED(false, false); + + mFlinger.createDisplay(String8("Test Display"), false); + + mFlinger.commit(); + EXPECT_COLOR_MATRIX_CHANGED(false, true); + + mFlinger.commitAndComposite(); + EXPECT_COLOR_MATRIX_CHANGED(false, false); +} + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp index 8b16a8a480..82b4ad0b4f 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp @@ -21,9 +21,13 @@ #include "mock/DisplayHardware/MockDisplayMode.h" #include "mock/MockDisplayModeSpecs.h" +#include <com_android_graphics_surfaceflinger_flags.h> +#include <common/test/FlagUtils.h> #include <ftl/fake_guard.h> #include <scheduler/Fps.h> +using namespace com::android::graphics::surfaceflinger; + namespace android { namespace { @@ -360,6 +364,13 @@ MATCHER_P(ModeSettledTo, modeId, "") { } TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { + SET_FLAG_FOR_TEST(flags::connected_display, true); + + // For the inner display, this is handled by setupHwcHotplugCallExpectations. + EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _)) + .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL), + Return(hal::V2_4::Error::NONE))); + const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); @@ -429,6 +440,12 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { } TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) { + SET_FLAG_FOR_TEST(flags::connected_display, true); + + // For the inner display, this is handled by setupHwcHotplugCallExpectations. + EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _)) + .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL), + Return(hal::V2_4::Error::NONE))); const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); @@ -512,6 +529,13 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) { } TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { + SET_FLAG_FOR_TEST(flags::connected_display, true); + + // For the inner display, this is handled by setupHwcHotplugCallExpectations. + EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _)) + .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL), + Return(hal::V2_4::Error::NONE))); + const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp index 93c282963f..19f8debe84 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp @@ -17,11 +17,15 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" +#include <com_android_graphics_surfaceflinger_flags.h> +#include <common/test/FlagUtils.h> #include "DualDisplayTransactionTest.h" #include <gmock/gmock.h> #include <gtest/gtest.h> +using namespace com::android::graphics::surfaceflinger; + namespace android { namespace { @@ -163,6 +167,7 @@ TEST_F(FoldableTest, requestsHardwareVsyncForBothDisplays) { } TEST_F(FoldableTest, requestVsyncOnPowerOn) { + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kInnerDisplayId, true)) .Times(1); EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kOuterDisplayId, true)) @@ -173,6 +178,7 @@ TEST_F(FoldableTest, requestVsyncOnPowerOn) { } TEST_F(FoldableTest, disableVsyncOnPowerOffPacesetter) { + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); // When the device boots, the inner display should be the pacesetter. ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp index 29acfaa1a5..4e9fba7bda 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp @@ -17,79 +17,16 @@ #undef LOG_TAG #define LOG_TAG "SurfaceFlingerGetDisplayStatsTest" -#include <compositionengine/Display.h> -#include <compositionengine/mock/DisplaySurface.h> #include <gmock/gmock.h> #include <gtest/gtest.h> -#include <renderengine/mock/RenderEngine.h> #include <ui/DisplayStatInfo.h> -#include "TestableSurfaceFlinger.h" -#include "mock/DisplayHardware/MockComposer.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" -#include "mock/MockTimeStats.h" -#include "mock/system/window/MockNativeWindow.h" -using namespace android; -using namespace testing; +#include "CommitAndCompositeTest.h" namespace android { namespace { -using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; -using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; -constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; -constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); -constexpr int DEFAULT_DISPLAY_WIDTH = 1920; -constexpr int DEFAULT_DISPLAY_HEIGHT = 1024; - -class SurfaceFlingerGetDisplayStatsTest : public Test { -public: - void SetUp() override; - -protected: - TestableSurfaceFlinger mFlinger; - renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); - sp<DisplayDevice> mDisplay; - sp<compositionengine::mock::DisplaySurface> mDisplaySurface = - sp<compositionengine::mock::DisplaySurface>::make(); - sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); - mock::TimeStats* mTimeStats = new mock::TimeStats(); - Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr; - Hwc2::mock::Composer* mComposer = nullptr; -}; - -void SurfaceFlingerGetDisplayStatsTest::SetUp() { - mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID}); - mComposer = new Hwc2::mock::Composer(); - mPowerAdvisor = new Hwc2::mock::PowerAdvisor(); - mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); - mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats)); - mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); - mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor)); - static constexpr bool kIsPrimary = true; - FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary) - .setPowerMode(hal::PowerMode::ON) - .inject(&mFlinger, mComposer); - auto compostionEngineDisplayArgs = - compositionengine::DisplayCreationArgsBuilder() - .setId(DEFAULT_DISPLAY_ID) - .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT}) - .setPowerAdvisor(mPowerAdvisor) - .setName("injected display") - .build(); - auto compositionDisplay = - compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(), - std::move(compostionEngineDisplayArgs)); - mDisplay = - FakeDisplayDeviceInjector(mFlinger, compositionDisplay, - ui::DisplayConnectionType::Internal, HWC_DISPLAY, kIsPrimary) - .setDisplaySurface(mDisplaySurface) - .setNativeWindow(mNativeWindow) - .setPowerMode(hal::PowerMode::ON) - .setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector()) - .skipRegisterDisplay() - .inject(); -} +struct SurfaceFlingerGetDisplayStatsTest : CommitAndCompositeTest {}; // TODO (b/277364366): Clients should be updated to pass in the display they want. TEST_F(SurfaceFlingerGetDisplayStatsTest, nullptrSucceeds) { diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp index 7206e2977d..91b901838c 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp @@ -17,31 +17,81 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" +#include <gui/SurfaceComposerClient.h> #include "DisplayTransactionTestHelpers.h" namespace android { using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; +using android::hardware::graphics::composer::V2_1::Error; class NotifyExpectedPresentTest : public DisplayTransactionTest { public: void SetUp() override { - mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this).inject(); - FakeHwcDisplayInjector(mDisplay->getPhysicalId(), hal::DisplayType::PHYSICAL, kIsPrimary) + const auto display = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this).inject(); + mPhysicalDisplayId = display->getPhysicalId(); + FakeHwcDisplayInjector(mPhysicalDisplayId, hal::DisplayType::PHYSICAL, /*isPrimary=*/true) .setPowerMode(hal::PowerMode::ON) .inject(&mFlinger, mComposer); + + ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(mPhysicalDisplayId, + TimePoint::fromNs(0), + kFps60Hz)); + mCompositor = std::make_unique<Compositor>(mPhysicalDisplayId, mFlinger); } protected: - sp<DisplayDevice> mDisplay; - static constexpr bool kIsPrimary = true; - static constexpr hal::HWDisplayId HWC_DISPLAY_ID = - FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; -}; + struct Compositor final : ICompositor { + explicit Compositor(PhysicalDisplayId displayId, TestableSurfaceFlinger& surfaceFlinger) + : displayId(displayId), surfaceFlinger(surfaceFlinger) {} -TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) { - const auto physicDisplayId = mDisplay->getPhysicalId(); - auto expectedPresentTime = systemTime() + ms2ns(10); + void sendNotifyExpectedPresentHint(PhysicalDisplayId id) override { + surfaceFlinger.sendNotifyExpectedPresentHint(id); + } + + bool commit(PhysicalDisplayId, const scheduler::FrameTargets&) override { + return committed; + } + + CompositeResultsPerDisplay composite(PhysicalDisplayId pacesetterId, + const scheduler::FrameTargeters& targeters) override { + pacesetterIds.composite = pacesetterId; + CompositeResultsPerDisplay results; + + for (const auto& [id, targeter] : targeters) { + vsyncIds.composite.emplace_back(id, targeter->target().vsyncId()); + surfaceFlinger.resetNotifyExpectedPresentHintState(pacesetterId); + results.try_emplace(id, + CompositeResult{.compositionCoverage = + CompositionCoverage::Hwc}); + } + + return results; + } + + void sample() override {} + void configure() override {} + + struct { + PhysicalDisplayId commit; + PhysicalDisplayId composite; + } pacesetterIds; + + using VsyncIds = std::vector<std::pair<PhysicalDisplayId, VsyncId>>; + struct { + VsyncIds commit; + VsyncIds composite; + } vsyncIds; + + bool committed = true; + PhysicalDisplayId displayId; + TestableSurfaceFlinger& surfaceFlinger; + }; + + PhysicalDisplayId mPhysicalDisplayId; + std::unique_ptr<Compositor> mCompositor; + static constexpr hal::HWDisplayId kHwcDisplayId = + FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; static constexpr Fps kFps60Hz = 60_Hz; static constexpr int32_t kFrameInterval5HzNs = static_cast<Fps>(5_Hz).getPeriodNsecs(); static constexpr int32_t kFrameInterval60HzNs = kFps60Hz.getPeriodNsecs(); @@ -49,89 +99,171 @@ TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) { static constexpr Period kVsyncPeriod = Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs()); static constexpr Period kTimeoutNs = Period::fromNs(kFrameInterval5HzNs); - static constexpr auto kLastExpectedPresentTimestamp = TimePoint::fromNs(0); +}; - ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(physicDisplayId, - kLastExpectedPresentTimestamp, - kFps60Hz)); +TEST_F(NotifyExpectedPresentTest, noNotifyExpectedPresentHintCall_absentTimeout) { + auto expectedPresentTime = systemTime() + ms2ns(10); + ASSERT_NO_FATAL_FAILURE( + mFlinger.setNotifyExpectedPresentData(mPhysicalDisplayId, + TimePoint::fromNs(expectedPresentTime), + kFps60Hz)); + EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + for (int i = 0; i < 5; i++) { + expectedPresentTime += 2 * kFrameInterval5HzNs; + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + /*timeoutOpt*/ std::nullopt); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); + } +} +TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentHint_zeroTimeout) { + auto expectedPresentTime = systemTime() + ms2ns(10); { - // Very first ExpectedPresent after idle, no previous timestamp + // Very first ExpectedPresent after idle, no previous timestamp. EXPECT_CALL(*mComposer, - notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, - kFrameInterval60HzNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + + // Present frame + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + // Present happens and NotifyExpectedPresentHintStatus is start. + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); } { - // Absent timeoutNs - expectedPresentTime += 2 * kFrameInterval5HzNs; - EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + mCompositor->committed = false; + expectedPresentTime += kFrameInterval60HzNs; + EXPECT_CALL(*mComposer, + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) + .WillOnce(Return(Error::NONE)); + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, - /*timeoutOpt*/ std::nullopt); + Period::fromNs(0)); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + // Hint sent + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); } { - // Timeout is 0 expectedPresentTime += kFrameInterval60HzNs; EXPECT_CALL(*mComposer, - notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, - kFrameInterval60HzNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, Period::fromNs(0)); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + // Hint is executed + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); } +} + +TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) { + auto expectedPresentTime = systemTime() + ms2ns(10); { - // ExpectedPresent is after the timeoutNs + // Very first ExpectedPresent after idle, no previous timestamp + mCompositor->committed = false; + EXPECT_CALL(*mComposer, + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) + .WillOnce(Return(Error::NONE)); + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + kTimeoutNs); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + } + { + // ExpectedPresentTime is after the timeoutNs + mCompositor->committed = true; + expectedPresentTime += 2 * kFrameInterval5HzNs; + EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + kTimeoutNs); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + // Present happens notifyExpectedPresentHintStatus is Start + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); + + // Another expectedPresent after timeout expectedPresentTime += 2 * kFrameInterval5HzNs; EXPECT_CALL(*mComposer, - notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, - kFrameInterval60HzNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); } { // ExpectedPresent has not changed - EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); } { - // ExpectedPresent is after the last reported ExpectedPresent. + // ExpectedPresent is after the last reported ExpectedPresent and within timeout. expectedPresentTime += kFrameInterval60HzNs; - EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); } { // ExpectedPresent is before the last reported ExpectedPresent but after the timeoutNs, // representing we changed our decision and want to present earlier than previously // reported. + mCompositor->committed = false; expectedPresentTime -= kFrameInterval120HzNs; EXPECT_CALL(*mComposer, - notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, - kFrameInterval60HzNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintIsScheduledOnPresent(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); } } TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) { - const auto physicDisplayId = mDisplay->getPhysicalId(); const auto now = systemTime(); auto expectedPresentTime = now; static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs()); - ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(physicDisplayId, + ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(mPhysicalDisplayId, TimePoint::fromNs(now), Fps::fromValue(0))); static constexpr int32_t kFrameIntervalNs120Hz = static_cast<Fps>(120_Hz).getPeriodNsecs(); @@ -147,7 +279,7 @@ TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) { struct FrameRateIntervalTestData { int32_t frameIntervalNs; - bool callExpectedPresent; + bool callNotifyExpectedPresentHint; }; const std::vector<FrameRateIntervalTestData> frameIntervals = { {kFrameIntervalNs60Hz, true}, {kFrameIntervalNs96Hz, true}, @@ -159,21 +291,35 @@ TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) { {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs120Hz, true}, }; - for (const auto& [frameIntervalNs, callExpectedPresent] : frameIntervals) { - { - expectedPresentTime += frameIntervalNs; - if (callExpectedPresent) { - EXPECT_CALL(*mComposer, - notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, - frameIntervalNs)) - .WillOnce(Return(Error::NONE)); - } else { - EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); - } - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), - Fps::fromPeriodNsecs(frameIntervalNs), - kTimeoutNs); + for (size_t i = 0; i < frameIntervals.size(); i++) { + const auto& [frameIntervalNs, callNotifyExpectedPresentHint] = frameIntervals[i]; + expectedPresentTime += frameIntervalNs; + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), + Fps::fromPeriodNsecs(frameIntervalNs), kTimeoutNs); + + if (callNotifyExpectedPresentHint) { + mCompositor->committed = false; + ASSERT_TRUE(mFlinger.verifyHintIsScheduledOnPresent(mPhysicalDisplayId)) + << "Hint not scheduled for frameInterval " << frameIntervalNs << " at index " + << i; + EXPECT_CALL(*mComposer, + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs)) + .WillOnce(Return(Error::NONE)); + } else { + // Only lastExpectedPresentTime is updated + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)) + << "LastExpectedPresentTime for frameInterval " << frameIntervalNs + << "at index " << i << " did not match for frameInterval " << frameIntervalNs; + EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + } + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + + if (callNotifyExpectedPresentHint) { + // Present resumes the calls to the notifyExpectedPresentHint. + mCompositor->committed = true; + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); } } } diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp index b80cb6625f..c3934e64f8 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp @@ -17,84 +17,18 @@ #undef LOG_TAG #define LOG_TAG "SurfaceFlingerPowerHintTest" -#include <compositionengine/Display.h> -#include <compositionengine/mock/DisplaySurface.h> -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <renderengine/mock/RenderEngine.h> -#include <algorithm> #include <chrono> -#include <memory> -#include "TestableSurfaceFlinger.h" -#include "mock/DisplayHardware/MockComposer.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" -#include "mock/MockTimeStats.h" -#include "mock/system/window/MockNativeWindow.h" -using namespace android; -using namespace android::Hwc2::mock; -using namespace android::hardware::power; +#include "CommitAndCompositeTest.h" + using namespace std::chrono_literals; -using namespace testing; +using testing::_; +using testing::Return; namespace android { namespace { -using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; -using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; - -constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; -constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); -constexpr int DEFAULT_DISPLAY_WIDTH = 1920; -constexpr int DEFAULT_DISPLAY_HEIGHT = 1024; - -class SurfaceFlingerPowerHintTest : public Test { -public: - void SetUp() override; -protected: - TestableSurfaceFlinger mFlinger; - renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); - sp<DisplayDevice> mDisplay; - sp<compositionengine::mock::DisplaySurface> mDisplaySurface = - sp<compositionengine::mock::DisplaySurface>::make(); - sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); - mock::TimeStats* mTimeStats = new mock::TimeStats(); - Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr; - Hwc2::mock::Composer* mComposer = nullptr; -}; - -void SurfaceFlingerPowerHintTest::SetUp() { - mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID}); - mComposer = new Hwc2::mock::Composer(); - mPowerAdvisor = new Hwc2::mock::PowerAdvisor(); - mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); - mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats)); - mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); - mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor)); - static constexpr bool kIsPrimary = true; - FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary) - .setPowerMode(hal::PowerMode::ON) - .inject(&mFlinger, mComposer); - auto compostionEngineDisplayArgs = - compositionengine::DisplayCreationArgsBuilder() - .setId(DEFAULT_DISPLAY_ID) - .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT}) - .setPowerAdvisor(mPowerAdvisor) - .setName("injected display") - .build(); - auto compositionDisplay = - compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(), - std::move(compostionEngineDisplayArgs)); - mDisplay = - FakeDisplayDeviceInjector(mFlinger, compositionDisplay, - ui::DisplayConnectionType::Internal, HWC_DISPLAY, kIsPrimary) - .setDisplaySurface(mDisplaySurface) - .setNativeWindow(mNativeWindow) - .setPowerMode(hal::PowerMode::ON) - .setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector()) - .skipRegisterDisplay() - .inject(); -} +class SurfaceFlingerPowerHintTest : public CommitAndCompositeTest {}; TEST_F(SurfaceFlingerPowerHintTest, sendDurationsIncludingHwcWaitTime) { ON_CALL(*mPowerAdvisor, usePowerHintSession()).WillByDefault(Return(true)); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp index 15fe6008a5..83e2f980ce 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp @@ -17,11 +17,15 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" +#include <com_android_graphics_surfaceflinger_flags.h> +#include <common/test/FlagUtils.h> #include "DisplayTransactionTestHelpers.h" #include <gmock/gmock.h> #include <gtest/gtest.h> +using namespace com::android::graphics::surfaceflinger; + namespace android { namespace { @@ -453,6 +457,7 @@ TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay } TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) { + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>(); } @@ -461,6 +466,7 @@ TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalD } TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) { + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>(); } @@ -473,6 +479,7 @@ TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) } TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) { + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>(); } @@ -481,10 +488,12 @@ TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) } TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) { + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>(); } TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) { + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>(); } diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.cpp b/services/surfaceflinger/tests/unittests/TestableScheduler.cpp index e0b736677d..7b92a5bb3b 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.cpp +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.cpp @@ -21,11 +21,10 @@ namespace android::scheduler { TestableScheduler::TestableScheduler(RefreshRateSelectorPtr selectorPtr, TestableSurfaceFlinger& testableSurfaceFlinger, - ISchedulerCallback& callback, - IVsyncTrackerCallback& vsyncTrackerCallback) + ISchedulerCallback& callback) : TestableScheduler(std::make_unique<android::mock::VsyncController>(), std::make_shared<android::mock::VSyncTracker>(), std::move(selectorPtr), testableSurfaceFlinger.getFactory(), - testableSurfaceFlinger.getTimeStats(), callback, vsyncTrackerCallback) {} + testableSurfaceFlinger.getTimeStats(), callback) {} } // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index 6213713f3a..25a85dfa20 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -41,18 +41,16 @@ namespace android::scheduler { class TestableScheduler : public Scheduler, private ICompositor { public: TestableScheduler(RefreshRateSelectorPtr selectorPtr, - TestableSurfaceFlinger& testableSurfaceFlinger, ISchedulerCallback& callback, - IVsyncTrackerCallback& vsyncTrackerCallback); + TestableSurfaceFlinger& testableSurfaceFlinger, ISchedulerCallback& callback); TestableScheduler(std::unique_ptr<VsyncController> controller, std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr, surfaceflinger::Factory& factory, TimeStats& timeStats, - ISchedulerCallback& schedulerCallback, - IVsyncTrackerCallback& vsyncTrackerCallback) + ISchedulerCallback& schedulerCallback) : Scheduler(*this, schedulerCallback, (FeatureFlags)Feature::kContentDetection | Feature::kSmallDirtyContentDetection, - factory, selectorPtr->getActiveMode().fps, timeStats, vsyncTrackerCallback) { + factory, selectorPtr->getActiveMode().fps, timeStats) { const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); registerDisplay(displayId, std::move(selectorPtr), std::move(controller), std::move(tracker)); @@ -210,6 +208,7 @@ private: return {}; } void sample() override {} + void sendNotifyExpectedPresentHint(PhysicalDisplayId) override {} }; } // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 9ef0749dc6..46a079cfa1 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -53,7 +53,6 @@ #include "mock/MockFrameTimeline.h" #include "mock/MockFrameTracer.h" #include "mock/MockSchedulerCallback.h" -#include "mock/MockVsyncTrackerCallback.h" #include "mock/system/window/MockNativeWindow.h" #include "Scheduler/VSyncTracker.h" @@ -205,8 +204,6 @@ public: enum class SchedulerCallbackImpl { kNoOp, kMock }; - enum class VsyncTrackerCallbackImpl { kNoOp, kMock }; - struct DefaultDisplayMode { // The ID of the injected RefreshRateSelector and its default display mode. PhysicalDisplayId displayId; @@ -220,14 +217,13 @@ public: TimeStats& getTimeStats() { return *mFlinger->mTimeStats; } - void setupScheduler( - std::unique_ptr<scheduler::VsyncController> vsyncController, - std::shared_ptr<scheduler::VSyncTracker> vsyncTracker, - std::unique_ptr<EventThread> appEventThread, std::unique_ptr<EventThread> sfEventThread, - DisplayModesVariant modesVariant, - SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp, - VsyncTrackerCallbackImpl vsyncTrackerCallbackImpl = VsyncTrackerCallbackImpl::kNoOp, - bool useNiceMock = false) { + void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController, + std::shared_ptr<scheduler::VSyncTracker> vsyncTracker, + std::unique_ptr<EventThread> appEventThread, + std::unique_ptr<EventThread> sfEventThread, + DisplayModesVariant modesVariant, + SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp, + bool useNiceMock = false) { RefreshRateSelectorPtr selectorPtr = ftl::match( modesVariant, [](DefaultDisplayMode arg) { @@ -245,12 +241,6 @@ public: ? static_cast<ISchedulerCallback&>(mNoOpSchedulerCallback) : static_cast<ISchedulerCallback&>(mSchedulerCallback); - using VsyncTrackerCallback = scheduler::IVsyncTrackerCallback; - VsyncTrackerCallback& vsyncTrackerCallback = - vsyncTrackerCallbackImpl == VsyncTrackerCallbackImpl::kNoOp - ? static_cast<VsyncTrackerCallback&>(mNoOpVsyncTrackerCallback) - : static_cast<VsyncTrackerCallback&>(mVsyncTrackerCallback); - if (useNiceMock) { mScheduler = new testing::NiceMock<scheduler::TestableScheduler>(std::move(vsyncController), @@ -258,14 +248,12 @@ public: std::move(selectorPtr), mFactory, *mFlinger->mTimeStats, - schedulerCallback, - vsyncTrackerCallback); + schedulerCallback); } else { mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(selectorPtr), mFactory, - *mFlinger->mTimeStats, schedulerCallback, - vsyncTrackerCallback); + *mFlinger->mTimeStats, schedulerCallback); } mScheduler->initVsync(*mTokenManager, 0ms); @@ -307,8 +295,7 @@ public: EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0)); setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread), std::move(sfEventThread), DefaultDisplayMode{options.displayId}, - SchedulerCallbackImpl::kNoOp, VsyncTrackerCallbackImpl::kNoOp, - options.useNiceMock); + SchedulerCallbackImpl::kNoOp, options.useNiceMock); } void resetScheduler(scheduler::Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); } @@ -698,6 +685,36 @@ public: frameInterval, timeoutOpt); } + void sendNotifyExpectedPresentHint(PhysicalDisplayId displayId) { + ftl::FakeGuard guard(kMainThreadContext); + mFlinger->sendNotifyExpectedPresentHint(displayId); + } + + bool verifyHintIsScheduledOnPresent(PhysicalDisplayId displayId) { + return mFlinger->mNotifyExpectedPresentMap.at(displayId).hintStatus == + SurfaceFlinger::NotifyExpectedPresentHintStatus::ScheduleOnPresent; + } + + bool verifyHintIsSent(PhysicalDisplayId displayId) { + return mFlinger->mNotifyExpectedPresentMap.at(displayId).hintStatus == + SurfaceFlinger::NotifyExpectedPresentHintStatus::Sent; + } + + bool verifyHintStatusIsStart(PhysicalDisplayId displayId) { + return mFlinger->mNotifyExpectedPresentMap.at(displayId).hintStatus == + SurfaceFlinger::NotifyExpectedPresentHintStatus::Start; + } + + bool verifyHintStatusIsScheduledOnTx(PhysicalDisplayId displayId) { + return mFlinger->mNotifyExpectedPresentMap.at(displayId).hintStatus == + SurfaceFlinger::NotifyExpectedPresentHintStatus::ScheduleOnTx; + } + + bool verifyLastExpectedPresentTime(PhysicalDisplayId displayId, nsecs_t expectedPresentTime) { + return mFlinger->mNotifyExpectedPresentMap.at(displayId) + .lastExpectedPresentTimestamp.ns() == expectedPresentTime; + } + void setNotifyExpectedPresentData(PhysicalDisplayId displayId, TimePoint lastExpectedPresentTimestamp, Fps lastFrameInterval) { @@ -706,6 +723,11 @@ public: displayData.lastFrameInterval = lastFrameInterval; } + void resetNotifyExpectedPresentHintState(PhysicalDisplayId displayId) { + mFlinger->mNotifyExpectedPresentMap.at(displayId).hintStatus = + SurfaceFlinger::NotifyExpectedPresentHintStatus::Start; + } + ~TestableSurfaceFlinger() { // All these pointer and container clears help ensure that GMock does // not report a leaked object, since the SurfaceFlinger instance may @@ -1099,8 +1121,6 @@ private: sp<SurfaceFlinger> mFlinger; scheduler::mock::SchedulerCallback mSchedulerCallback; scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback; - scheduler::mock::VsyncTrackerCallback mVsyncTrackerCallback; - scheduler::mock::NoOpVsyncTrackerCallback mNoOpVsyncTrackerCallback; std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager; scheduler::TestableScheduler* mScheduler = nullptr; Hwc2::mock::PowerAdvisor mPowerAdvisor; diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index d952b70d4b..b9f3d70c6b 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -26,7 +26,6 @@ #include <common/test/FlagUtils.h> #include "Scheduler/VSyncPredictor.h" #include "mock/DisplayHardware/MockDisplayMode.h" -#include "mock/MockVsyncTrackerCallback.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -82,14 +81,13 @@ struct VSyncPredictorTest : testing::Test { nsecs_t mNow = 0; nsecs_t mPeriod = 1000; ftl::NonNull<DisplayModePtr> mMode = displayMode(mPeriod); - scheduler::mock::VsyncTrackerCallback mVsyncTrackerCallback; static constexpr size_t kHistorySize = 10; static constexpr size_t kMinimumSamplesForPrediction = 6; static constexpr size_t kOutlierTolerancePercent = 25; static constexpr nsecs_t mMaxRoundingError = 100; VSyncPredictor tracker{mMode, kHistorySize, kMinimumSamplesForPrediction, - kOutlierTolerancePercent, mVsyncTrackerCallback}; + kOutlierTolerancePercent}; }; TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) { @@ -410,8 +408,7 @@ TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) { // See b/151146131 TEST_F(VSyncPredictorTest, hasEnoughPrecision) { const auto mode = displayMode(mPeriod); - VSyncPredictor tracker{mode, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent, - mVsyncTrackerCallback}; + VSyncPredictor tracker{mode, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent}; std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675, 840923581635, 840940161584, 840956868096, 840973702473, 840990256277, 841007116851, @@ -658,48 +655,6 @@ TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod)); } -TEST_F(VSyncPredictorTest, vsyncTrackerCallback) { - SET_FLAG_FOR_TEST(flags::vrr_config, true); - - const auto refreshRate = Fps::fromPeriodNsecs(mPeriod); - NotifyExpectedPresentConfig notifyExpectedPresentConfig; - notifyExpectedPresentConfig.timeoutNs = Period::fromNs(30).ns(); - - hal::VrrConfig vrrConfig; - vrrConfig.notifyExpectedPresentConfig = notifyExpectedPresentConfig; - vrrConfig.minFrameIntervalNs = refreshRate.getPeriodNsecs(); - - const int32_t kGroup = 0; - const auto kResolution = ui::Size(1920, 1080); - const auto mode = - ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), refreshRate, vrrConfig, kGroup, - kResolution, DEFAULT_DISPLAY_ID)); - - tracker.setDisplayModePtr(mode); - auto last = mNow; - for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { - EXPECT_CALL(mVsyncTrackerCallback, - onVsyncGenerated(TimePoint::fromNs(last + mPeriod), mode, - FpsMatcher(refreshRate))) - .Times(1); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod)); - mNow += mPeriod; - last = mNow; - tracker.addVsyncTimestamp(mNow); - } - - tracker.setRenderRate(refreshRate / 2); - { - // out of render rate phase - EXPECT_CALL(mVsyncTrackerCallback, - onVsyncGenerated(TimePoint::fromNs(mNow + 3 * mPeriod), mode, - FpsMatcher(refreshRate / 2))) - .Times(1); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), - Eq(mNow + 3 * mPeriod)); - } -} - TEST_F(VSyncPredictorTest, adjustsVrrTimeline) { SET_FLAG_FOR_TEST(flags::vrr_config, true); @@ -716,7 +671,7 @@ TEST_F(VSyncPredictorTest, adjustsVrrTimeline) { .build()); VSyncPredictor vrrTracker{kMode, kHistorySize, kMinimumSamplesForPrediction, - kOutlierTolerancePercent, mVsyncTrackerCallback}; + kOutlierTolerancePercent}; vrrTracker.setRenderRate(minFrameRate); vrrTracker.addVsyncTimestamp(0); @@ -733,28 +688,6 @@ TEST_F(VSyncPredictorTest, adjustsVrrTimeline) { EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500)); EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000)); } - -TEST_F(VSyncPredictorTest, absentVrrConfigNoVsyncTrackerCallback) { - SET_FLAG_FOR_TEST(flags::vrr_config, true); - const auto refreshRate = Fps::fromPeriodNsecs(mPeriod); - const std::optional<hal::VrrConfig> vrrConfigOpt = std::nullopt; - constexpr int32_t kGroup = 0; - constexpr auto kResolution = ui::Size(1920, 1080); - const auto mode = - ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), refreshRate, vrrConfigOpt, - kGroup, kResolution, DEFAULT_DISPLAY_ID)); - tracker.setDisplayModePtr(mode); - - auto last = mNow; - for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { - EXPECT_CALL(mVsyncTrackerCallback, onVsyncGenerated(_, _, _)).Times(0); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod)); - mNow += mPeriod; - last = mNow; - tracker.addVsyncTimestamp(mNow); - } -} - } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h index 22b2cccf03..4ca05423d7 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h +++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h @@ -28,6 +28,8 @@ struct SchedulerCallback final : ISchedulerCallback { MOCK_METHOD(void, kernelTimerChanged, (bool), (override)); MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override)); MOCK_METHOD(void, onChoreographerAttached, (), (override)); + MOCK_METHOD(void, onExpectedPresentTimePosted, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps), + (override)); }; struct NoOpSchedulerCallback final : ISchedulerCallback { @@ -36,6 +38,7 @@ struct NoOpSchedulerCallback final : ISchedulerCallback { void kernelTimerChanged(bool) override {} void triggerOnFrameRateOverridesChanged() override {} void onChoreographerAttached() override {} + void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {} }; } // namespace android::scheduler::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h deleted file mode 100644 index b48529f4ac..0000000000 --- a/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <gmock/gmock.h> - -#include "Scheduler/VSyncTracker.h" - -namespace android::scheduler::mock { - -struct VsyncTrackerCallback final : IVsyncTrackerCallback { - MOCK_METHOD(void, onVsyncGenerated, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps), (override)); -}; - -struct NoOpVsyncTrackerCallback final : IVsyncTrackerCallback { - void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override{}; -}; -} // namespace android::scheduler::mock diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp index 32947246d8..be71dc2ce3 100644 --- a/services/vibratorservice/test/Android.bp +++ b/services/vibratorservice/test/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_haptics_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" |