diff options
73 files changed, 2778 insertions, 556 deletions
diff --git a/include/ftl/details/function.h b/include/ftl/details/function.h new file mode 100644 index 0000000000..35c5a8b302 --- /dev/null +++ b/include/ftl/details/function.h @@ -0,0 +1,135 @@ +/* + * Copyright 2022 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 <array> +#include <cstddef> +#include <cstdint> +#include <cstring> +#include <type_traits> + +namespace android::ftl::details { + +// The maximum allowed value for the template argument `N` in +// `ftl::Function<F, N>`. +constexpr size_t kFunctionMaximumN = 14; + +// Converts a member function pointer type `Ret(Class::*)(Args...)` to an equivalent non-member +// function type `Ret(Args...)`. + +template <typename> +struct remove_member_function_pointer; + +template <typename Class, typename Ret, typename... Args> +struct remove_member_function_pointer<Ret (Class::*)(Args...)> { + using type = Ret(Args...); +}; + +template <typename Class, typename Ret, typename... Args> +struct remove_member_function_pointer<Ret (Class::*)(Args...) const> { + using type = Ret(Args...); +}; + +template <auto MemberFunction> +using remove_member_function_pointer_t = + typename remove_member_function_pointer<decltype(MemberFunction)>::type; + +// Helper functions for binding to the supported targets. + +template <typename Ret, typename... Args> +auto bind_opaque_no_op() -> Ret (*)(void*, Args...) { + return [](void*, Args...) -> Ret { + if constexpr (!std::is_void_v<Ret>) { + return Ret{}; + } + }; +} + +template <typename F, typename Ret, typename... Args> +auto bind_opaque_function_object(const F&) -> Ret (*)(void*, Args...) { + return [](void* opaque, Args... args) -> Ret { + return std::invoke(*static_cast<F*>(opaque), std::forward<Args>(args)...); + }; +} + +template <auto MemberFunction, typename Class, typename Ret, typename... Args> +auto bind_member_function(Class* instance, Ret (*)(Args...) = nullptr) { + return [instance](Args... args) -> Ret { + return std::invoke(MemberFunction, instance, std::forward<Args>(args)...); + }; +} + +template <auto FreeFunction, typename Ret, typename... Args> +auto bind_free_function(Ret (*)(Args...) = nullptr) { + return [](Args... args) -> Ret { return std::invoke(FreeFunction, std::forward<Args>(args)...); }; +} + +// Traits class for the opaque storage used by Function. + +template <std::size_t N> +struct function_opaque_storage { + // The actual type used for the opaque storage. An `N` of zero specifies the minimum useful size, + // which allows a lambda with zero or one capture args. + using type = std::array<std::intptr_t, N + 1>; + + template <typename S> + static constexpr bool require_trivially_copyable = std::is_trivially_copyable_v<S>; + + template <typename S> + static constexpr bool require_trivially_destructible = std::is_trivially_destructible_v<S>; + + template <typename S> + static constexpr bool require_will_fit_in_opaque_storage = sizeof(S) <= sizeof(type); + + template <typename S> + static constexpr bool require_alignment_compatible = + std::alignment_of_v<S> <= std::alignment_of_v<type>; + + // Copies `src` into the opaque storage, and returns that storage. + template <typename S> + static type opaque_copy(const S& src) { + // TODO: Replace with C++20 concepts/constraints which can give more details. + static_assert(require_trivially_copyable<S>, + "ftl::Function can only store lambdas that capture trivially copyable data."); + static_assert( + require_trivially_destructible<S>, + "ftl::Function can only store lambdas that capture trivially destructible data."); + static_assert(require_will_fit_in_opaque_storage<S>, + "ftl::Function has limited storage for lambda captured state. Maybe you need to " + "increase N?"); + static_assert(require_alignment_compatible<S>); + + type opaque; + std::memcpy(opaque.data(), &src, sizeof(S)); + return opaque; + } +}; + +// Traits class to help determine the template parameters to use for a ftl::Function, given a +// function object. + +template <typename F, typename = decltype(&F::operator())> +struct function_traits { + // The function type `F` with which to instantiate the `Function<F, N>` template. + using type = remove_member_function_pointer_t<&F::operator()>; + + // The (minimum) size `N` with which to instantiate the `Function<F, N>` template. + static constexpr std::size_t size = + (std::max(sizeof(std::intptr_t), sizeof(F)) - 1) / sizeof(std::intptr_t); +}; + +} // namespace android::ftl::details diff --git a/include/ftl/function.h b/include/ftl/function.h new file mode 100644 index 0000000000..3538ca4eae --- /dev/null +++ b/include/ftl/function.h @@ -0,0 +1,297 @@ +/* + * Copyright 2022 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 <cstddef> +#include <functional> +#include <type_traits> +#include <utility> + +#include <ftl/details/function.h> + +namespace android::ftl { + +// ftl::Function<F, N> is a container for function object, and can mostly be used in place of +// std::function<F>. +// +// Unlike std::function<F>, a ftl::Function<F, N>: +// +// * Uses a static amount of memory (controlled by N), and never any dynamic allocation. +// * Satisfies the std::is_trivially_copyable<> trait. +// * Satisfies the std::is_trivially_destructible<> trait. +// +// However those same limits are also required from the contained function object in turn. +// +// The size of a ftl::Function<F, N> is guaranteed to be: +// +// sizeof(std::intptr_t) * (N + 2) +// +// A ftl::Function<F, N> can always be implicitly converted to a larger size ftl::Function<F, M>. +// Trying to convert the other way leads to a compilation error. +// +// A default-constructed ftl::Function is in an empty state. The operator bool() overload returns +// false in this state. It is undefined behavior to attempt to invoke the function in this state. +// +// The ftl::Function<F, N> can also be constructed or assigned from ftl::no_op. This sets up the +// ftl::Function to be non-empty, with a function that when called does nothing except +// default-constructs a return value. +// +// The ftl::make_function() helpers construct a ftl::Function<F, N>, including deducing the +// values of F and N from the arguments it is given. +// +// The static ftl::Function<F, N>::make() helpers construct a ftl::Function<F, N> without that +// deduction, and also allow for implicit argument conversion if the target being called needs them. +// +// The construction helpers allow any of the following types of functions to be stored: +// +// * Any SMALL function object (as defined by the C++ Standard), such as a lambda with a small +// capture, or other "functor". The requirements are: +// +// 1) The function object must be trivial to destroy (in fact, the destructor will never +// actually be called once copied to the internal storage). +// 2) The function object must be trivial to copy (the raw bytes will be copied as the +// ftl::Function<F, N> is copied/moved). +// 3) The size of the function object cannot be larger than sizeof(std::intptr_t) * (N + 1), +// and it cannot require stricter alignment than alignof(std::intptr_t). +// +// With the default of N=0, a lambda can only capture a single pointer-sized argument. This is +// enough to capture `this`, which is why N=0 is the default. +// +// * A member function, with the address passed as the template value argument to the construction +// helper function, along with the instance pointer needed to invoke it passed as an ordinary +// argument. +// +// ftl::make_function<&Class::member_function>(this); +// +// Note that the indicated member function will be invoked non-virtually. If you need it to be +// invoked virtually, you should invoke it yourself with a small lambda like so: +// +// ftl::function([this] { virtual_member_function(); }); +// +// * An ordinary function ("free function"), with the address of the function passed as a template +// value argument. +// +// ftl::make_function<&std::atoi>(); +// +// As with the member function helper, as the function is known at compile time, it will be called +// directly. +// +// Example usage: +// +// class MyClass { +// public: +// void on_event() const {} +// int on_string(int*, std::string_view) { return 1; } +// +// auto get_function() { +// return ftl::function([this] { on_event(); }); +// } +// } cls; +// +// // A function container with no arguments, and returning no value. +// ftl::Function<void()> f; +// +// // Construct a ftl::Function containing a small lambda. +// f = cls.get_function(); +// +// // Construct a ftl::Function that calls `cls.on_event()`. +// f = ftl::function<&MyClass::on_event>(&cls); +// +// // Create a do-nothing function. +// f = ftl::no_op; +// +// // Invoke the contained function. +// f(); +// +// // Also invokes it. +// std::invoke(f); +// +// // Create a typedef to give a more meaningful name and bound the size. +// using MyFunction = ftl::Function<int(std::string_view), 2>; +// int* ptr = nullptr; +// auto f1 = MyFunction::make_function( +// [cls = &cls, ptr](std::string_view sv) { +// return cls->on_string(ptr, sv); +// }); +// int r = f1("abc"sv); +// +// // Returns a default-constructed int (0). +// f1 = ftl::no_op; +// r = f1("abc"sv); +// assert(r == 0); + +template <typename F, std::size_t N = 0> +class Function; + +// Used to construct a Function that does nothing. +struct NoOpTag {}; + +constexpr NoOpTag no_op; + +// Detects that a type is a `ftl::Function<F, N>` regardless of what `F` and `N` are. +template <typename> +struct is_function : public std::false_type {}; + +template <typename F, std::size_t N> +struct is_function<Function<F, N>> : public std::true_type {}; + +template <typename T> +constexpr bool is_function_v = is_function<T>::value; + +template <typename Ret, typename... Args, std::size_t N> +class Function<Ret(Args...), N> final { + // Enforce a valid size, with an arbitrary maximum allowed size for the container of + // sizeof(std::intptr_t) * 16, though that maximum can be relaxed. + static_assert(N <= details::kFunctionMaximumN); + + using OpaqueStorageTraits = details::function_opaque_storage<N>; + + public: + // Defining result_type allows ftl::Function to be substituted for std::function. + using result_type = Ret; + + // Constructs an empty ftl::Function. + Function() = default; + + // Constructing or assigning from nullptr_t also creates an empty ftl::Function. + Function(std::nullptr_t) {} + Function& operator=(std::nullptr_t) { return *this = Function(nullptr); } + + // Constructing from NoOpTag sets up a a special no-op function which is valid to call, and which + // returns a default constructed return value. + Function(NoOpTag) : function_(details::bind_opaque_no_op<Ret, Args...>()) {} + Function& operator=(NoOpTag) { return *this = Function(no_op); } + + // Constructing/assigning from a function object stores a copy of that function object, however: + // * It must be trivially copyable, as the implementation makes a copy with memcpy(). + // * It must be trivially destructible, as the implementation doesn't destroy the copy! + // * It must fit in the limited internal storage, which enforces size/alignment restrictions. + + template <typename F, typename = std::enable_if_t<std::is_invocable_r_v<Ret, F, Args...>>> + Function(const F& f) + : opaque_(OpaqueStorageTraits::opaque_copy(f)), + function_(details::bind_opaque_function_object<F, Ret, Args...>(f)) {} + + template <typename F, typename = std::enable_if_t<std::is_invocable_r_v<Ret, F, Args...>>> + Function& operator=(const F& f) noexcept { + return *this = Function{OpaqueStorageTraits::opaque_copy(f), + details::bind_opaque_function_object<F, Ret, Args...>(f)}; + } + + // Constructing/assigning from a smaller ftl::Function is allowed, but not anything else. + + template <std::size_t M> + Function(const Function<Ret(Args...), M>& other) + : opaque_{OpaqueStorageTraits::opaque_copy(other.opaque_)}, function_(other.function_) {} + + template <std::size_t M> + auto& operator=(const Function<Ret(Args...), M>& other) { + return *this = Function{OpaqueStorageTraits::opaque_copy(other.opaque_), other.function_}; + } + + // Returns true if a function is set. + explicit operator bool() const { return function_ != nullptr; } + + // Checks if the other function has the same contents as this one. + bool operator==(const Function& other) const { + return other.opaque_ == opaque_ && other.function_ == function_; + } + bool operator!=(const Function& other) const { return !operator==(other); } + + // Alternative way of testing for a function being set. + bool operator==(std::nullptr_t) const { return function_ == nullptr; } + bool operator!=(std::nullptr_t) const { return function_ != nullptr; } + + // Invokes the function. + Ret operator()(Args... args) const { + return std::invoke(function_, opaque_.data(), std::forward<Args>(args)...); + } + + // Creation helper for function objects, such as lambdas. + template <typename F> + static auto make(const F& f) -> decltype(Function{f}) { + return Function{f}; + } + + // Creation helper for a class pointer and a compile-time chosen member function to call. + template <auto MemberFunction, typename Class> + static auto make(Class* instance) -> decltype(Function{ + details::bind_member_function<MemberFunction>(instance, + static_cast<Ret (*)(Args...)>(nullptr))}) { + return Function{details::bind_member_function<MemberFunction>( + instance, static_cast<Ret (*)(Args...)>(nullptr))}; + } + + // Creation helper for a compile-time chosen free function to call. + template <auto FreeFunction> + static auto make() -> decltype(Function{ + details::bind_free_function<FreeFunction>(static_cast<Ret (*)(Args...)>(nullptr))}) { + return Function{ + details::bind_free_function<FreeFunction>(static_cast<Ret (*)(Args...)>(nullptr))}; + } + + private: + // Needed so a Function<F, M> can be converted to a Function<F, N>. + template <typename, std::size_t> + friend class Function; + + // The function pointer type of function stored in `function_`. The first argument is always + // `&opaque_`. + using StoredFunction = Ret(void*, Args...); + + // The type of the opaque storage, used to hold an appropriate function object. + // The type stored here is ONLY known to the StoredFunction. + // We always use at least one std::intptr_t worth of storage, and always a multiple of that size. + using OpaqueStorage = typename OpaqueStorageTraits::type; + + // Internal constructor for creating from a raw opaque blob + function pointer. + Function(const OpaqueStorage& opaque, StoredFunction* function) + : opaque_(opaque), function_(function) {} + + // Note: `mutable` so that `operator() const` can use it. + mutable OpaqueStorage opaque_{}; + StoredFunction* function_{nullptr}; +}; + +// Makes a ftl::Function given a function object `F`. +template <typename F, typename T = details::function_traits<F>> +Function(const F&) -> Function<typename T::type, T::size>; + +template <typename F> +auto make_function(const F& f) -> decltype(Function{f}) { + return Function{f}; +} + +// Makes a ftl::Function given a `MemberFunction` and a instance pointer to the associated `Class`. +template <auto MemberFunction, typename Class> +auto make_function(Class* instance) + -> decltype(Function{details::bind_member_function<MemberFunction>( + instance, + static_cast<details::remove_member_function_pointer_t<MemberFunction>*>(nullptr))}) { + return Function{details::bind_member_function<MemberFunction>( + instance, static_cast<details::remove_member_function_pointer_t<MemberFunction>*>(nullptr))}; +} + +// Makes a ftl::Function given an ordinary free function. +template <auto FreeFunction> +auto make_function() -> decltype(Function{ + details::bind_free_function<FreeFunction>(static_cast<decltype(FreeFunction)>(nullptr))}) { + return Function{ + details::bind_free_function<FreeFunction>(static_cast<decltype(FreeFunction)>(nullptr))}; +} + +} // namespace android::ftl diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index 9347ce47a5..dc5b1a1712 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -62,8 +62,13 @@ public: /** * Returns the PID of the process which has made the current binder - * call. If not in a binder call, this will return getpid. If the - * call is oneway, this will return 0. + * call. If not in a binder call, this will return getpid. + * + * Warning: oneway transactions do not receive PID. Even if you expect + * a transaction to be synchronous, a misbehaving client could send it + * as an asynchronous call and result in a 0 PID here. Additionally, if + * there is a race and the calling process dies, the PID may still be + * 0 for a synchronous call. */ [[nodiscard]] pid_t getCallingPid() const; diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index db2d2c1b09..b1ab7b0f9a 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h @@ -390,6 +390,12 @@ uid_t AIBinder_getCallingUid() __INTRODUCED_IN(29); * calling process dies and is replaced with another process with elevated permissions and the same * PID. * + * Warning: oneway transactions do not receive PID. Even if you expect + * a transaction to be synchronous, a misbehaving client could send it + * as a synchronous call and result in a 0 PID here. Additionally, if + * there is a race and the calling process dies, the PID may still be + * 0 for a synchronous call. + * * Available since API level 29. * * \return calling pid or the current process's PID if this thread isn't processing a transaction. diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs index a3a2562eb1..8a06274e9c 100644 --- a/libs/binder/rust/src/state.rs +++ b/libs/binder/rust/src/state.rs @@ -101,13 +101,16 @@ impl ThreadState { /// dies and is replaced with another process with elevated permissions and /// the same PID. /// + /// Warning: oneway transactions do not receive PID. Even if you expect + /// a transaction to be synchronous, a misbehaving client could send it + /// as a synchronous call and result in a 0 PID here. Additionally, if + /// there is a race and the calling process dies, the PID may still be + /// 0 for a synchronous call. + /// /// Available since API level 29. /// /// \return calling pid or the current process's PID if this thread isn't /// processing a transaction. - /// - /// If the transaction being processed is a oneway transaction, then this - /// method will return 0. pub fn get_calling_pid() -> pid_t { // Safety: Safe FFI unsafe { sys::AIBinder_getCallingPid() } diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index ea1b5e4998..918680d6a7 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -17,6 +17,7 @@ cc_test { "enum_test.cpp", "fake_guard_test.cpp", "flags_test.cpp", + "function_test.cpp", "future_test.cpp", "match_test.cpp", "mixins_test.cpp", diff --git a/libs/ftl/function_test.cpp b/libs/ftl/function_test.cpp new file mode 100644 index 0000000000..91b5e08041 --- /dev/null +++ b/libs/ftl/function_test.cpp @@ -0,0 +1,379 @@ +/* + * Copyright 2022 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/function.h> +#include <gtest/gtest.h> + +#include <array> +#include <cstddef> +#include <cstdint> +#include <string_view> +#include <type_traits> + +namespace android::test { +namespace { + +// Create an alias to composite requirements defined by the trait class `T` for easier testing. +template <typename T, typename S> +inline constexpr bool is_opaquely_storable = (T::template require_trivially_copyable<S> && + T::template require_trivially_destructible<S> && + T::template require_will_fit_in_opaque_storage<S> && + T::template require_alignment_compatible<S>); + +// `I` gives a count of sizeof(std::intptr_t) bytes , and `J` gives a raw count of bytes +template <size_t I, size_t J = 0> +struct KnownSizeFunctionObject { + using Data = std::array<std::byte, sizeof(std::intptr_t) * I + J>; + void operator()() const {}; + Data data{}; +}; + +} // namespace + +// static_assert the expected type traits +static_assert(std::is_invocable_r_v<void, ftl::Function<void()>>); +static_assert(std::is_trivially_copyable_v<ftl::Function<void()>>); +static_assert(std::is_trivially_destructible_v<ftl::Function<void()>>); +static_assert(std::is_trivially_copy_constructible_v<ftl::Function<void()>>); +static_assert(std::is_trivially_move_constructible_v<ftl::Function<void()>>); +static_assert(std::is_trivially_copy_assignable_v<ftl::Function<void()>>); +static_assert(std::is_trivially_move_assignable_v<ftl::Function<void()>>); + +template <typename T> +using function_traits = ftl::details::function_traits<T>; + +// static_assert that the expected value of N is used for known function object sizes. +static_assert(function_traits<KnownSizeFunctionObject<0, 0>>::size == 0); +static_assert(function_traits<KnownSizeFunctionObject<0, 1>>::size == 0); +static_assert(function_traits<KnownSizeFunctionObject<1, 0>>::size == 0); +static_assert(function_traits<KnownSizeFunctionObject<1, 1>>::size == 1); +static_assert(function_traits<KnownSizeFunctionObject<2, 0>>::size == 1); +static_assert(function_traits<KnownSizeFunctionObject<2, 1>>::size == 2); + +// Check that is_function_v works +static_assert(!ftl::is_function_v<KnownSizeFunctionObject<0>>); +static_assert(!ftl::is_function_v<std::function<void()>>); +static_assert(ftl::is_function_v<ftl::Function<void()>>); + +// static_assert what can and cannot be stored inside the opaque storage + +template <size_t N> +using function_opaque_storage = ftl::details::function_opaque_storage<N>; + +// Function objects can be stored if they fit. +static_assert(is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<0>>); +static_assert(is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<1>>); +static_assert(!is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<2>>); + +static_assert(is_opaquely_storable<function_opaque_storage<1>, KnownSizeFunctionObject<2>>); +static_assert(!is_opaquely_storable<function_opaque_storage<1>, KnownSizeFunctionObject<3>>); + +static_assert(is_opaquely_storable<function_opaque_storage<2>, KnownSizeFunctionObject<3>>); +static_assert(!is_opaquely_storable<function_opaque_storage<2>, KnownSizeFunctionObject<4>>); + +// Another opaque storage can be stored if it fits. This property is used to copy smaller +// ftl::Functions into larger ones. +static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<0>::type>); +static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<1>::type>); +static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<2>::type>); +static_assert(!is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<3>::type>); + +// Function objects that aren't trivially copyable or destroyable cannot be stored. +auto lambda_capturing_unique_ptr = [ptr = std::unique_ptr<void*>()] { static_cast<void>(ptr); }; +static_assert( + !is_opaquely_storable<function_opaque_storage<2>, decltype(lambda_capturing_unique_ptr)>); + +// Keep in sync with "Example usage" in header file. +TEST(Function, Example) { + using namespace std::string_view_literals; + + class MyClass { + public: + void on_event() const {} + int on_string(int*, std::string_view) { return 1; } + + auto get_function() { + return ftl::make_function([this] { on_event(); }); + } + } cls; + + // A function container with no arguments, and returning no value. + ftl::Function<void()> f; + + // Construct a ftl::Function containing a small lambda. + f = cls.get_function(); + + // Construct a ftl::Function that calls `cls.on_event()`. + f = ftl::make_function<&MyClass::on_event>(&cls); + + // Create a do-nothing function. + f = ftl::no_op; + + // Invoke the contained function. + f(); + + // Also invokes it. + std::invoke(f); + + // Create a typedef to give a more meaningful name and bound the size. + using MyFunction = ftl::Function<int(std::string_view), 2>; + int* ptr = nullptr; + auto f1 = + MyFunction::make([cls = &cls, ptr](std::string_view sv) { return cls->on_string(ptr, sv); }); + int r = f1("abc"sv); + + // Returns a default-constructed int (0). + f1 = ftl::no_op; + r = f1("abc"sv); + EXPECT_EQ(r, 0); +} + +TEST(Function, BasicOperations) { + // Default constructible. + ftl::Function<int()> f; + + // Compares as empty + EXPECT_FALSE(f); + EXPECT_TRUE(f == nullptr); + EXPECT_FALSE(f != nullptr); + EXPECT_TRUE(ftl::Function<int()>() == f); + EXPECT_FALSE(ftl::Function<int()>() != f); + + // Assigning no_op sets it to not empty. + f = ftl::no_op; + + // Verify it can be called, and that it returns a default constructed value. + EXPECT_EQ(f(), 0); + + // Comparable when non-empty. + EXPECT_TRUE(f); + EXPECT_FALSE(f == nullptr); + EXPECT_TRUE(f != nullptr); + EXPECT_FALSE(ftl::Function<int()>() == f); + EXPECT_TRUE(ftl::Function<int()>() != f); + + // Constructing from nullptr means empty. + f = ftl::Function<int()>{nullptr}; + EXPECT_FALSE(f); + + // Assigning nullptr means it is empty. + f = nullptr; + EXPECT_FALSE(f); + + // Move construction + f = ftl::no_op; + ftl::Function<int()> g{std::move(f)}; + EXPECT_TRUE(g != nullptr); + + // Move assignment + f = nullptr; + f = std::move(g); + EXPECT_TRUE(f != nullptr); + + // Copy construction + ftl::Function<int()> h{f}; + EXPECT_TRUE(h != nullptr); + + // Copy assignment + g = h; + EXPECT_TRUE(g != nullptr); +} + +TEST(Function, CanMoveConstructFromLambda) { + auto lambda = [] {}; + ftl::Function<void()> f{std::move(lambda)}; +} + +TEST(Function, TerseDeducedConstructAndAssignFromLambda) { + auto f = ftl::Function([] { return 1; }); + EXPECT_EQ(f(), 1); + + f = [] { return 2; }; + EXPECT_EQ(f(), 2); +} + +namespace { + +struct ImplicitConversionsHelper { + auto exact(int) -> int { return 0; } + auto inexact(long) -> short { return 0; } + // TODO: Switch to `auto templated(auto x)` with C++20 + template <typename T> + T templated(T x) { + return x; + } + + static auto static_exact(int) -> int { return 0; } + static auto static_inexact(long) -> short { return 0; } + // TODO: Switch to `static auto static_templated(auto x)` with C++20 + template <typename T> + static T static_templated(T x) { + return x; + } +}; + +} // namespace + +TEST(Function, ImplicitConversions) { + using Function = ftl::Function<int(int)>; + auto check = [](Function f) { return f(0); }; + auto exact = [](int) -> int { return 0; }; + auto inexact = [](long) -> short { return 0; }; + auto templated = [](auto x) { return x; }; + + ImplicitConversionsHelper helper; + + // Note, `check(nullptr)` would crash, so we can only check if it would be invocable. + static_assert(std::is_invocable_v<decltype(check), decltype(nullptr)>); + + // Note: We invoke each of these to fully expand all the templates involved. + EXPECT_EQ(check(ftl::no_op), 0); + + EXPECT_EQ(check(exact), 0); + EXPECT_EQ(check(inexact), 0); + EXPECT_EQ(check(templated), 0); + + EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::exact>(&helper)), 0); + EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::inexact>(&helper)), 0); + EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::templated<int>>(&helper)), 0); + + EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_exact>()), 0); + EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_inexact>()), 0); + EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_templated<int>>()), 0); +} + +TEST(Function, MakeWithNonConstMemberFunction) { + struct Observer { + bool called = false; + void setCalled() { called = true; } + } observer; + + auto f = ftl::make_function<&Observer::setCalled>(&observer); + + f(); + + EXPECT_TRUE(observer.called); + + EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer)); +} + +TEST(Function, MakeWithConstMemberFunction) { + struct Observer { + mutable bool called = false; + void setCalled() const { called = true; } + } observer; + + const auto f = ftl::make_function<&Observer::setCalled>(&observer); + + f(); + + EXPECT_TRUE(observer.called); + + EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer)); +} + +TEST(Function, MakeWithConstClassPointer) { + const struct Observer { + mutable bool called = false; + void setCalled() const { called = true; } + } observer; + + const auto f = ftl::make_function<&Observer::setCalled>(&observer); + + f(); + + EXPECT_TRUE(observer.called); + + EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer)); +} + +TEST(Function, MakeWithNonCapturingLambda) { + auto f = ftl::make_function([](int a, int b) { return a + b; }); + EXPECT_EQ(f(1, 2), 3); +} + +TEST(Function, MakeWithCapturingLambda) { + bool called = false; + auto f = ftl::make_function([&called](int a, int b) { + called = true; + return a + b; + }); + EXPECT_EQ(f(1, 2), 3); + EXPECT_TRUE(called); +} + +TEST(Function, MakeWithCapturingMutableLambda) { + bool called = false; + auto f = ftl::make_function([&called](int a, int b) mutable { + called = true; + return a + b; + }); + EXPECT_EQ(f(1, 2), 3); + EXPECT_TRUE(called); +} + +TEST(Function, MakeWithThreePointerCapturingLambda) { + bool my_bool = false; + int my_int = 0; + float my_float = 0.f; + + auto f = ftl::make_function( + [ptr_bool = &my_bool, ptr_int = &my_int, ptr_float = &my_float](int a, int b) mutable { + *ptr_bool = true; + *ptr_int = 1; + *ptr_float = 1.f; + + return a + b; + }); + + EXPECT_EQ(f(1, 2), 3); + + EXPECT_TRUE(my_bool); + EXPECT_EQ(my_int, 1); + EXPECT_EQ(my_float, 1.f); +} + +TEST(Function, MakeWithFreeFunction) { + auto f = ftl::make_function<&std::make_unique<int, int>>(); + std::unique_ptr<int> unique_int = f(1); + ASSERT_TRUE(unique_int); + EXPECT_EQ(*unique_int, 1); +} + +TEST(Function, CopyToLarger) { + int counter = 0; + ftl::Function<void()> a{[ptr_counter = &counter] { (*ptr_counter)++; }}; + ftl::Function<void(), 1> b = a; + ftl::Function<void(), 2> c = a; + + EXPECT_EQ(counter, 0); + a(); + EXPECT_EQ(counter, 1); + b(); + EXPECT_EQ(counter, 2); + c(); + EXPECT_EQ(counter, 3); + + b = [ptr_counter = &counter] { (*ptr_counter) += 2; }; + c = [ptr_counter = &counter] { (*ptr_counter) += 3; }; + + b(); + EXPECT_EQ(counter, 5); + c(); + EXPECT_EQ(counter, 8); +} + +} // namespace android::test diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index e7b2195056..745b6f30cc 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -113,6 +113,22 @@ static_assert( AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM, "HAL and AHardwareBuffer pixel format don't match"); +static enum AHardwareBufferStatus filterStatus(status_t status) { + switch (status) { + case STATUS_OK: + return AHARDWAREBUFFER_STATUS_OK; + case STATUS_NO_MEMORY: + return AHARDWAREBUFFER_STATUS_NO_MEMORY; + case STATUS_BAD_VALUE: + return AHARDWAREBUFFER_STATUS_BAD_VALUE; + case STATUS_UNKNOWN_TRANSACTION: + case STATUS_INVALID_OPERATION: + return AHARDWAREBUFFER_STATUS_UNSUPPORTED; + default: + return AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR; + } +} + // ---------------------------------------------------------------------------- // Public functions // ---------------------------------------------------------------------------- @@ -511,6 +527,24 @@ binder_status_t AHardwareBuffer_writeToParcel(const AHardwareBuffer* _Nonnull bu return AParcel_viewPlatformParcel(parcel)->write(*gb); } +ADataSpace AHardwareBuffer_getDataSpace(const AHardwareBuffer* _Nonnull buffer) { + const GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer); + if (!gb) return ADATASPACE_UNKNOWN; + ui::Dataspace dataspace = ui::Dataspace::UNKNOWN; + status_t status = gb->getDataspace(&dataspace); + if (status != OK) { + return ADATASPACE_UNKNOWN; + } + return static_cast<ADataSpace>(dataspace); +} + +enum AHardwareBufferStatus AHardwareBuffer_setDataSpace(AHardwareBuffer* buffer, + ADataSpace dataspace) { + GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer); + auto& mapper = GraphicBufferMapper::get(); + return filterStatus(mapper.setDataspace(gb->handle, static_cast<ui::Dataspace>(dataspace))); +} + // ---------------------------------------------------------------------------- // VNDK functions // ---------------------------------------------------------------------------- @@ -552,6 +586,56 @@ int AHardwareBuffer_createFromHandle(const AHardwareBuffer_Desc* desc, return NO_ERROR; } +enum AHardwareBufferStatus AHardwareBuffer_allocate2( + const AHardwareBuffer_Desc* desc, const AHardwareBufferLongOptions* additionalOptions, + size_t additionalOptionsSize, AHardwareBuffer** outBuffer) { + (void)additionalOptions; + (void)additionalOptionsSize; + if (!outBuffer || !desc) return AHARDWAREBUFFER_STATUS_BAD_VALUE; + if (!AHardwareBuffer_isValidDescription(desc, /*log=*/true)) { + return AHARDWAREBUFFER_STATUS_BAD_VALUE; + } + + int format = AHardwareBuffer_convertToPixelFormat(desc->format); + uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage); + + std::vector<GraphicBufferAllocator::AdditionalOptions> extras; + extras.reserve(additionalOptionsSize); + for (size_t i = 0; i < additionalOptionsSize; i++) { + extras.push_back(GraphicBufferAllocator::AdditionalOptions{additionalOptions[i].name, + additionalOptions[i].value}); + } + + const auto extrasCount = extras.size(); + auto gbuffer = sp<GraphicBuffer>::make(GraphicBufferAllocator::AllocationRequest{ + .importBuffer = true, + .width = desc->width, + .height = desc->height, + .format = format, + .layerCount = desc->layers, + .usage = usage, + .requestorName = std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]", + .extras = std::move(extras), + }); + + status_t err = gbuffer->initCheck(); + if (err != 0 || gbuffer->handle == nullptr) { + if (err == NO_MEMORY) { + GraphicBuffer::dumpAllocationsToSystemLog(); + } + ALOGE("GraphicBuffer(w=%u, h=%u, lc=%u, extrasCount=%zd) failed (%s), handle=%p", + desc->width, desc->height, desc->layers, extrasCount, strerror(-err), + gbuffer->handle); + return filterStatus(err == 0 ? UNKNOWN_ERROR : err); + } + + *outBuffer = AHardwareBuffer_from_GraphicBuffer(gbuffer.get()); + + // Ensure the buffer doesn't get destroyed when the sp<> goes away. + AHardwareBuffer_acquire(*outBuffer); + return AHARDWAREBUFFER_STATUS_OK; +} + // ---------------------------------------------------------------------------- // Helpers implementation // ---------------------------------------------------------------------------- @@ -652,12 +736,9 @@ uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t ahardwarebuffer_format) { return ahardwarebuffer_format; } +// TODO: Remove, this is just to make an overly aggressive ABI checker happy int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer) { - GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer); - auto& mapper = GraphicBufferMapper::get(); - ui::Dataspace dataspace = ui::Dataspace::UNKNOWN; - mapper.getDataspace(gb->handle, &dataspace); - return static_cast<int32_t>(dataspace); + return ::AHardwareBuffer_getDataSpace(buffer); } uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) { diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index dd5958de28..f97eed5db3 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -152,31 +152,56 @@ int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transfo int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpace) { static_assert(static_cast<int>(ADATASPACE_UNKNOWN) == static_cast<int>(HAL_DATASPACE_UNKNOWN)); - static_assert(static_cast<int>(STANDARD_MASK) == static_cast<int>(HAL_DATASPACE_STANDARD_MASK)); - static_assert(static_cast<int>(STANDARD_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED)); - static_assert(static_cast<int>(STANDARD_BT709) == static_cast<int>(HAL_DATASPACE_STANDARD_BT709)); - static_assert(static_cast<int>(STANDARD_BT601_625) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625)); - static_assert(static_cast<int>(STANDARD_BT601_625_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED)); - static_assert(static_cast<int>(STANDARD_BT601_525) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525)); - static_assert(static_cast<int>(STANDARD_BT601_525_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED)); - static_assert(static_cast<int>(STANDARD_BT470M) == static_cast<int>(HAL_DATASPACE_STANDARD_BT470M)); - static_assert(static_cast<int>(STANDARD_FILM) == static_cast<int>(HAL_DATASPACE_STANDARD_FILM)); - static_assert(static_cast<int>(STANDARD_DCI_P3) == static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3)); - static_assert(static_cast<int>(STANDARD_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB)); - static_assert(static_cast<int>(TRANSFER_MASK) == static_cast<int>(HAL_DATASPACE_TRANSFER_MASK)); - static_assert(static_cast<int>(TRANSFER_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED)); - static_assert(static_cast<int>(TRANSFER_LINEAR) == static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR)); - static_assert(static_cast<int>(TRANSFER_SMPTE_170M) == static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M)); - static_assert(static_cast<int>(TRANSFER_GAMMA2_2) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2)); - static_assert(static_cast<int>(TRANSFER_GAMMA2_6) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6)); - static_assert(static_cast<int>(TRANSFER_GAMMA2_8) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8)); - static_assert(static_cast<int>(TRANSFER_ST2084) == static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084)); - static_assert(static_cast<int>(TRANSFER_HLG) == static_cast<int>(HAL_DATASPACE_TRANSFER_HLG)); - static_assert(static_cast<int>(RANGE_MASK) == static_cast<int>(HAL_DATASPACE_RANGE_MASK)); - static_assert(static_cast<int>(RANGE_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED)); - static_assert(static_cast<int>(RANGE_FULL) == static_cast<int>(HAL_DATASPACE_RANGE_FULL)); - static_assert(static_cast<int>(RANGE_LIMITED) == static_cast<int>(HAL_DATASPACE_RANGE_LIMITED)); - static_assert(static_cast<int>(RANGE_EXTENDED) == static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_MASK) == + static_cast<int>(HAL_DATASPACE_STANDARD_MASK)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_UNSPECIFIED) == + static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT709) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT709)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_625) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_625_UNADJUSTED) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_525) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_525_UNADJUSTED) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT470M) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT470M)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_FILM) == + static_cast<int>(HAL_DATASPACE_STANDARD_FILM)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_DCI_P3) == + static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_ADOBE_RGB) == + static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_MASK) == + static_cast<int>(HAL_DATASPACE_TRANSFER_MASK)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_UNSPECIFIED) == + static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_LINEAR) == + static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_SMPTE_170M) == + static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_2) == + static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_6) == + static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_8) == + static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_ST2084) == + static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_HLG) == + static_cast<int>(HAL_DATASPACE_TRANSFER_HLG)); + static_assert(static_cast<int>(ADATASPACE_RANGE_MASK) == + static_cast<int>(HAL_DATASPACE_RANGE_MASK)); + static_assert(static_cast<int>(ADATASPACE_RANGE_UNSPECIFIED) == + static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED)); + static_assert(static_cast<int>(ADATASPACE_RANGE_FULL) == + static_cast<int>(HAL_DATASPACE_RANGE_FULL)); + static_assert(static_cast<int>(ADATASPACE_RANGE_LIMITED) == + static_cast<int>(HAL_DATASPACE_RANGE_LIMITED)); + static_assert(static_cast<int>(ADATASPACE_RANGE_EXTENDED) == + static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED)); static_assert(static_cast<int>(ADATASPACE_SRGB) == static_cast<int>(HAL_DATASPACE_V0_SRGB)); static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB)); static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3)); diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h index 880c694934..f145a2f7c2 100644 --- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h +++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h @@ -27,8 +27,8 @@ #include <stdint.h> -struct AHardwareBuffer; -struct AHardwareBuffer_Desc; +#include <vndk/hardware_buffer.h> + struct ANativeWindowBuffer; namespace android { @@ -46,11 +46,6 @@ uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format); // convert HAL format to AHardwareBuffer format (note: this is a no-op) uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format); -// retrieves a dataspace from the AHardwareBuffer metadata, if the device -// support gralloc metadata. Returns UNKNOWN if gralloc metadata is not -// supported. -int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer); - // convert AHardwareBuffer usage bits to HAL usage bits (note: this is a no-op) uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage); diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h index eab21fbdf1..d4278bedf3 100644 --- a/libs/nativewindow/include/android/data_space.h +++ b/libs/nativewindow/include/android/data_space.h @@ -37,7 +37,7 @@ __BEGIN_DECLS /** * ADataSpace. */ -enum ADataSpace { +enum ADataSpace : int32_t { /** * Default-assumption data space, when not explicitly specified. * @@ -63,7 +63,7 @@ enum ADataSpace { * Defines the chromaticity coordinates of the source primaries in terms of * the CIE 1931 definition of x and y specified in ISO 11664-1. */ - STANDARD_MASK = 63 << 16, + ADATASPACE_STANDARD_MASK = 63 << 16, /** * Chromacity coordinates are unknown or are determined by the application. @@ -78,7 +78,7 @@ enum ADataSpace { * For all other formats standard is undefined, and implementations should use * an appropriate standard for the data represented. */ - STANDARD_UNSPECIFIED = 0 << 16, + ADATASPACE_STANDARD_UNSPECIFIED = 0 << 16, /** * <pre> @@ -91,7 +91,7 @@ enum ADataSpace { * Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation * for RGB conversion. */ - STANDARD_BT709 = 1 << 16, + ADATASPACE_STANDARD_BT709 = 1 << 16, /** * <pre> @@ -106,7 +106,7 @@ enum ADataSpace { * to minimize the color shift into RGB space that uses BT.709 * primaries. */ - STANDARD_BT601_625 = 2 << 16, + ADATASPACE_STANDARD_BT601_625 = 2 << 16, /** * <pre> @@ -119,7 +119,7 @@ enum ADataSpace { * Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation * for RGB conversion. */ - STANDARD_BT601_625_UNADJUSTED = 3 << 16, + ADATASPACE_STANDARD_BT601_625_UNADJUSTED = 3 << 16, /** * <pre> @@ -134,7 +134,7 @@ enum ADataSpace { * to minimize the color shift into RGB space that uses BT.709 * primaries. */ - STANDARD_BT601_525 = 4 << 16, + ADATASPACE_STANDARD_BT601_525 = 4 << 16, /** * <pre> @@ -147,7 +147,7 @@ enum ADataSpace { * Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation * for RGB conversion (as in SMPTE 240M). */ - STANDARD_BT601_525_UNADJUSTED = 5 << 16, + ADATASPACE_STANDARD_BT601_525_UNADJUSTED = 5 << 16, /** * <pre> @@ -160,7 +160,7 @@ enum ADataSpace { * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation * for RGB conversion. */ - STANDARD_BT2020 = 6 << 16, + ADATASPACE_STANDARD_BT2020 = 6 << 16, /** * <pre> @@ -173,7 +173,7 @@ enum ADataSpace { * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation * for RGB conversion using the linear domain. */ - STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16, + ADATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16, /** * <pre> @@ -186,7 +186,7 @@ enum ADataSpace { * Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation * for RGB conversion. */ - STANDARD_BT470M = 8 << 16, + ADATASPACE_STANDARD_BT470M = 8 << 16, /** * <pre> @@ -199,7 +199,7 @@ enum ADataSpace { * Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation * for RGB conversion. */ - STANDARD_FILM = 9 << 16, + ADATASPACE_STANDARD_FILM = 9 << 16, /** * SMPTE EG 432-1 and SMPTE RP 431-2. (DCI-P3) @@ -210,7 +210,7 @@ enum ADataSpace { * red 0.680 0.320 * white (D65) 0.3127 0.3290</pre> */ - STANDARD_DCI_P3 = 10 << 16, + ADATASPACE_STANDARD_DCI_P3 = 10 << 16, /** * Adobe RGB @@ -221,7 +221,7 @@ enum ADataSpace { * red 0.640 0.330 * white (D65) 0.3127 0.3290</pre> */ - STANDARD_ADOBE_RGB = 11 << 16, + ADATASPACE_STANDARD_ADOBE_RGB = 11 << 16, /** * Transfer aspect @@ -236,7 +236,7 @@ enum ADataSpace { * component. Implementation may apply the transfer function in RGB space * for all pixel formats if desired. */ - TRANSFER_MASK = 31 << 22, + ADATASPACE_TRANSFER_MASK = 31 << 22, /** * Transfer characteristics are unknown or are determined by the @@ -244,13 +244,13 @@ enum ADataSpace { * * Implementations should use the following transfer functions: * - * For YCbCr formats: use TRANSFER_SMPTE_170M - * For RGB formats: use TRANSFER_SRGB + * For YCbCr formats: use ADATASPACE_TRANSFER_SMPTE_170M + * For RGB formats: use ADATASPACE_TRANSFER_SRGB * * For all other formats transfer function is undefined, and implementations * should use an appropriate standard for the data represented. */ - TRANSFER_UNSPECIFIED = 0 << 22, + ADATASPACE_TRANSFER_UNSPECIFIED = 0 << 22, /** * Linear transfer. @@ -260,7 +260,7 @@ enum ADataSpace { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_LINEAR = 1 << 22, + ADATASPACE_TRANSFER_LINEAR = 1 << 22, /** * sRGB transfer. @@ -271,7 +271,7 @@ enum ADataSpace { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_SRGB = 2 << 22, + ADATASPACE_TRANSFER_SRGB = 2 << 22, /** * SMPTE 170M transfer. @@ -282,7 +282,7 @@ enum ADataSpace { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_SMPTE_170M = 3 << 22, + ADATASPACE_TRANSFER_SMPTE_170M = 3 << 22, /** * Display gamma 2.2. @@ -292,7 +292,7 @@ enum ADataSpace { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_GAMMA2_2 = 4 << 22, + ADATASPACE_TRANSFER_GAMMA2_2 = 4 << 22, /** * Display gamma 2.6. @@ -302,7 +302,7 @@ enum ADataSpace { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_GAMMA2_6 = 5 << 22, + ADATASPACE_TRANSFER_GAMMA2_6 = 5 << 22, /** * Display gamma 2.8. @@ -312,7 +312,7 @@ enum ADataSpace { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_GAMMA2_8 = 6 << 22, + ADATASPACE_TRANSFER_GAMMA2_8 = 6 << 22, /** * SMPTE ST 2084 (Dolby Perceptual Quantizer). @@ -328,7 +328,7 @@ enum ADataSpace { * L = 1 corresponds to 10000 cd/m2 * E - corresponding electrical signal</pre> */ - TRANSFER_ST2084 = 7 << 22, + ADATASPACE_TRANSFER_ST2084 = 7 << 22, /** * ARIB STD-B67 Hybrid Log Gamma. @@ -344,7 +344,7 @@ enum ADataSpace { * to reference white level of 100 cd/m2 * E - corresponding electrical signal</pre> */ - TRANSFER_HLG = 8 << 22, + ADATASPACE_TRANSFER_HLG = 8 << 22, /** * Range aspect @@ -352,7 +352,7 @@ enum ADataSpace { * Defines the range of values corresponding to the unit range of 0-1. * This is defined for YCbCr only, but can be expanded to RGB space. */ - RANGE_MASK = 7 << 27, + ADATASPACE_RANGE_MASK = 7 << 27, /** * Range is unknown or are determined by the application. Implementations @@ -365,13 +365,13 @@ enum ADataSpace { * For all other formats range is undefined, and implementations should use * an appropriate range for the data represented. */ - RANGE_UNSPECIFIED = 0 << 27, + ADATASPACE_RANGE_UNSPECIFIED = 0 << 27, /** * Full range uses all values for Y, Cb and Cr from * 0 to 2^b-1, where b is the bit depth of the color format. */ - RANGE_FULL = 1 << 27, + ADATASPACE_RANGE_FULL = 1 << 27, /** * Limited range uses values 16/256*2^b to 235/256*2^b for Y, and @@ -386,7 +386,7 @@ enum ADataSpace { * Luma (Y) samples should range from 64 to 940, inclusive * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive */ - RANGE_LIMITED = 2 << 27, + ADATASPACE_RANGE_LIMITED = 2 << 27, /** * Extended range is used for scRGB. Intended for use with @@ -395,7 +395,7 @@ enum ADataSpace { * color outside the sRGB gamut. * Used to blend / merge multiple dataspaces on a single display. */ - RANGE_EXTENDED = 3 << 27, + ADATASPACE_RANGE_EXTENDED = 3 << 27, /** * scRGB linear encoding @@ -410,7 +410,8 @@ enum ADataSpace { * * Uses extended range, linear transfer and BT.709 standard. */ - ADATASPACE_SCRGB_LINEAR = 406913024, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_EXTENDED + ADATASPACE_SCRGB_LINEAR = 406913024, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_LINEAR | + // ADATASPACE_RANGE_EXTENDED /** * sRGB gamma encoding @@ -425,7 +426,8 @@ enum ADataSpace { * * Uses full range, sRGB transfer BT.709 standard. */ - ADATASPACE_SRGB = 142671872, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_FULL + ADATASPACE_SRGB = 142671872, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SRGB | + // ADATASPACE_RANGE_FULL /** * scRGB @@ -440,14 +442,16 @@ enum ADataSpace { * * Uses extended range, sRGB transfer and BT.709 standard. */ - ADATASPACE_SCRGB = 411107328, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_EXTENDED + ADATASPACE_SCRGB = 411107328, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SRGB | + // ADATASPACE_RANGE_EXTENDED /** * Display P3 * * Uses full range, sRGB transfer and D65 DCI-P3 standard. */ - ADATASPACE_DISPLAY_P3 = 143261696, // STANDARD_DCI_P3 | TRANSFER_SRGB | RANGE_FULL + ADATASPACE_DISPLAY_P3 = 143261696, // ADATASPACE_STANDARD_DCI_P3 | ADATASPACE_TRANSFER_SRGB | + // ADATASPACE_RANGE_FULL /** * ITU-R Recommendation 2020 (BT.2020) @@ -456,7 +460,8 @@ enum ADataSpace { * * Uses full range, SMPTE 2084 (PQ) transfer and BT2020 standard. */ - ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL + ADATASPACE_BT2020_PQ = 163971072, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_ST2084 | + // ADATASPACE_RANGE_FULL /** * ITU-R Recommendation 2020 (BT.2020) @@ -465,7 +470,8 @@ enum ADataSpace { * * Uses limited range, SMPTE 2084 (PQ) transfer and BT2020 standard. */ - ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED + ADATASPACE_BT2020_ITU_PQ = 298188800, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_ST2084 + // | ADATASPACE_RANGE_LIMITED /** * Adobe RGB @@ -475,7 +481,8 @@ enum ADataSpace { * Note: Application is responsible for gamma encoding the data as * a 2.2 gamma encoding is not supported in HW. */ - ADATASPACE_ADOBE_RGB = 151715840, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL + ADATASPACE_ADOBE_RGB = 151715840, // ADATASPACE_STANDARD_ADOBE_RGB | + // ADATASPACE_TRANSFER_GAMMA2_2 | ADATASPACE_RANGE_FULL /** * JPEG File Interchange Format (JFIF) @@ -484,7 +491,8 @@ enum ADataSpace { * * Uses full range, SMPTE 170M transfer and BT.601_625 standard. */ - ADATASPACE_JFIF = 146931712, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_FULL + ADATASPACE_JFIF = 146931712, // ADATASPACE_STANDARD_BT601_625 | ADATASPACE_TRANSFER_SMPTE_170M | + // ADATASPACE_RANGE_FULL /** * ITU-R Recommendation 601 (BT.601) - 625-line @@ -493,7 +501,8 @@ enum ADataSpace { * * Uses limited range, SMPTE 170M transfer and BT.601_625 standard. */ - ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED + ADATASPACE_BT601_625 = 281149440, // ADATASPACE_STANDARD_BT601_625 | + // ADATASPACE_TRANSFER_SMPTE_170M | ADATASPACE_RANGE_LIMITED /** * ITU-R Recommendation 601 (BT.601) - 525-line @@ -502,7 +511,8 @@ enum ADataSpace { * * Uses limited range, SMPTE 170M transfer and BT.601_525 standard. */ - ADATASPACE_BT601_525 = 281280512, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED + ADATASPACE_BT601_525 = 281280512, // ADATASPACE_STANDARD_BT601_525 | + // ADATASPACE_TRANSFER_SMPTE_170M | ADATASPACE_RANGE_LIMITED /** * ITU-R Recommendation 2020 (BT.2020) @@ -511,7 +521,8 @@ enum ADataSpace { * * Uses full range, SMPTE 170M transfer and BT2020 standard. */ - ADATASPACE_BT2020 = 147193856, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_FULL + ADATASPACE_BT2020 = 147193856, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_SMPTE_170M | + // ADATASPACE_RANGE_FULL /** * ITU-R Recommendation 709 (BT.709) @@ -520,7 +531,8 @@ enum ADataSpace { * * Uses limited range, SMPTE 170M transfer and BT.709 standard. */ - ADATASPACE_BT709 = 281083904, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_LIMITED + ADATASPACE_BT709 = 281083904, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SMPTE_170M | + // ADATASPACE_RANGE_LIMITED /** * SMPTE EG 432-1 and SMPTE RP 431-2 @@ -532,7 +544,8 @@ enum ADataSpace { * Note: Application is responsible for gamma encoding the data as * a 2.6 gamma encoding is not supported in HW. */ - ADATASPACE_DCI_P3 = 155844608, // STANDARD_DCI_P3 | TRANSFER_GAMMA2_6 | RANGE_FULL + ADATASPACE_DCI_P3 = 155844608, // ADATASPACE_STANDARD_DCI_P3 | ADATASPACE_TRANSFER_GAMMA2_6 | + // ADATASPACE_RANGE_FULL /** * sRGB linear encoding @@ -546,21 +559,24 @@ enum ADataSpace { * * Uses full range, linear transfer and BT.709 standard. */ - ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL + ADATASPACE_SRGB_LINEAR = 138477568, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_LINEAR | + // ADATASPACE_RANGE_FULL /** * Hybrid Log Gamma encoding * * Uses full range, hybrid log gamma transfer and BT2020 standard. */ - ADATASPACE_BT2020_HLG = 168165376, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL + ADATASPACE_BT2020_HLG = 168165376, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG | + // ADATASPACE_RANGE_FULL /** * ITU Hybrid Log Gamma encoding * * Uses limited range, hybrid log gamma transfer and BT2020 standard. */ - ADATASPACE_BT2020_ITU_HLG = 302383104, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED + ADATASPACE_BT2020_ITU_HLG = 302383104, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG | + // ADATASPACE_RANGE_LIMITED /** * Depth @@ -574,7 +590,37 @@ enum ADataSpace { * * Embedded depth metadata following the dynamic depth specification. */ - ADATASPACE_DYNAMIC_DEPTH = 4098 + ADATASPACE_DYNAMIC_DEPTH = 4098, + +#ifndef ADATASPACE_SKIP_LEGACY_DEFINES + STANDARD_MASK = ADATASPACE_STANDARD_MASK, + STANDARD_UNSPECIFIED = ADATASPACE_STANDARD_UNSPECIFIED, + STANDARD_BT709 = ADATASPACE_STANDARD_BT709, + STANDARD_BT601_625 = ADATASPACE_STANDARD_BT601_625, + STANDARD_BT601_625_UNADJUSTED = ADATASPACE_STANDARD_BT601_625_UNADJUSTED, + STANDARD_BT601_525 = ADATASPACE_STANDARD_BT601_525, + STANDARD_BT601_525_UNADJUSTED = ADATASPACE_STANDARD_BT601_525_UNADJUSTED, + STANDARD_BT470M = ADATASPACE_STANDARD_BT470M, + STANDARD_BT2020 = ADATASPACE_STANDARD_BT2020, + STANDARD_FILM = ADATASPACE_STANDARD_FILM, + STANDARD_DCI_P3 = ADATASPACE_STANDARD_DCI_P3, + STANDARD_ADOBE_RGB = ADATASPACE_STANDARD_ADOBE_RGB, + TRANSFER_MASK = ADATASPACE_TRANSFER_MASK, + TRANSFER_UNSPECIFIED = ADATASPACE_TRANSFER_UNSPECIFIED, + TRANSFER_LINEAR = ADATASPACE_TRANSFER_LINEAR, + TRANSFER_SMPTE_170M = ADATASPACE_TRANSFER_SMPTE_170M, + TRANSFER_GAMMA2_2 = ADATASPACE_TRANSFER_GAMMA2_2, + TRANSFER_GAMMA2_6 = ADATASPACE_TRANSFER_GAMMA2_6, + TRANSFER_GAMMA2_8 = ADATASPACE_TRANSFER_GAMMA2_8, + TRANSFER_SRGB = ADATASPACE_TRANSFER_SRGB, + TRANSFER_ST2084 = ADATASPACE_TRANSFER_ST2084, + TRANSFER_HLG = ADATASPACE_TRANSFER_HLG, + RANGE_MASK = ADATASPACE_RANGE_MASK, + RANGE_UNSPECIFIED = ADATASPACE_RANGE_UNSPECIFIED, + RANGE_FULL = ADATASPACE_RANGE_FULL, + RANGE_LIMITED = ADATASPACE_RANGE_LIMITED, + RANGE_EXTENDED = ADATASPACE_RANGE_EXTENDED, +#endif }; __END_DECLS diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h index 21798d0e29..e0e30c3283 100644 --- a/libs/nativewindow/include/android/hardware_buffer.h +++ b/libs/nativewindow/include/android/hardware_buffer.h @@ -46,6 +46,9 @@ #define ANDROID_HARDWARE_BUFFER_H #include <android/rect.h> +#define ADATASPACE_SKIP_LEGACY_DEFINES +#include <android/data_space.h> +#undef ADATASPACE_SKIP_LEGACY_DEFINES #include <inttypes.h> #include <sys/cdefs.h> diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h index 21931bb553..491860125f 100644 --- a/libs/nativewindow/include/vndk/hardware_buffer.h +++ b/libs/nativewindow/include/vndk/hardware_buffer.h @@ -21,6 +21,7 @@ #include <android/hardware_buffer.h> #include <cutils/native_handle.h> +#include <errno.h> __BEGIN_DECLS @@ -105,6 +106,76 @@ enum { AHARDWAREBUFFER_USAGE_CAMERA_MASK = 6UL << 16, }; +/** + * Additional options for AHardwareBuffer_allocate2. These correspond to + * android.hardware.graphics.common.ExtendableType + */ +typedef struct { + const char* _Nonnull name; + int64_t value; +} AHardwareBufferLongOptions; + +enum AHardwareBufferStatus : int32_t { + /* Success, no error */ + AHARDWAREBUFFER_STATUS_OK = 0, + /* There's insufficient memory to satisfy the request */ + AHARDWAREBUFFER_STATUS_NO_MEMORY = -ENOMEM, + /* The given argument is invalid */ + AHARDWAREBUFFER_STATUS_BAD_VALUE = -EINVAL, + /* The requested operation is not supported by the device */ + AHARDWAREBUFFER_STATUS_UNSUPPORTED = -ENOSYS, + /* An unknown error occurred */ + AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR = (-2147483647 - 1), +}; + +/** + * Allocates a buffer that matches the passed AHardwareBuffer_Desc with additional options + * + * If allocation succeeds, the buffer can be used according to the + * usage flags specified in its description. If a buffer is used in ways + * not compatible with its usage flags, the results are undefined and + * may include program termination. + * + * @param desc The AHardwareBuffer_Desc that describes the allocation to request. Note that `stride` + * is ignored. + * @param additionalOptions A pointer to an array of AHardwareBufferLongOptions with additional + * string key + long value options that may be specified. May be null if + * `additionalOptionsSize` is 0 + * @param additionalOptionsSize The number of additional options to pass + * @param outBuffer The resulting buffer allocation + * @return AHARDWAREBUFFER_STATUS_OK on success + * AHARDWAREBUFFER_STATUS_NO_MEMORY if there's insufficient resources for the allocation + * AHARDWAREBUFFER_STATUS_BAD_VALUE if the provided description & options are not supported + * by the device + * AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR for any other error + * any reason. The returned buffer has a reference count of 1. + */ +enum AHardwareBufferStatus AHardwareBuffer_allocate2( + const AHardwareBuffer_Desc* _Nonnull desc, + const AHardwareBufferLongOptions* _Nullable additionalOptions, size_t additionalOptionsSize, + AHardwareBuffer* _Nullable* _Nonnull outBuffer) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Queries the dataspace of the given AHardwareBuffer. + * + * @param buffer The non-null buffer for which to query the Dataspace + * @return The dataspace of the buffer, or ADATASPACE_UNKNOWN if one hasn't been set + */ +enum ADataSpace AHardwareBuffer_getDataSpace(const AHardwareBuffer* _Nonnull buffer) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets the dataspace of the given AHardwareBuffer + * @param buffer The non-null buffer for which to set the dataspace + * @param dataSpace The dataspace to set + * @return AHARDWAREBUFFER_STATUS_OK on success, + * AHARDWAREBUFFER_STATUS_UNSUPPORTED if the device doesn't support setting the dataspace, + * AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR for any other failure. + */ +enum AHardwareBufferStatus AHardwareBuffer_setDataSpace(AHardwareBuffer* _Nonnull buffer, + enum ADataSpace dataSpace) + __INTRODUCED_IN(__ANDROID_API_V__); + __END_DECLS #endif /* ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H */ diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index dcb506815c..a185a59fca 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -2,6 +2,7 @@ LIBNATIVEWINDOW { global: AHardwareBuffer_acquire; AHardwareBuffer_allocate; + AHardwareBuffer_allocate2; # llndk # systemapi AHardwareBuffer_createFromHandle; # llndk # systemapi AHardwareBuffer_describe; AHardwareBuffer_getId; # introduced=31 @@ -16,6 +17,8 @@ LIBNATIVEWINDOW { AHardwareBuffer_unlock; AHardwareBuffer_readFromParcel; # introduced=34 AHardwareBuffer_writeToParcel; # introduced=34 + AHardwareBuffer_getDataSpace; # llndk # systemapi + AHardwareBuffer_setDataSpace; # llndk # systemapi ANativeWindowBuffer_getHardwareBuffer; # llndk ANativeWindow_OemStorageGet; # llndk ANativeWindow_OemStorageSet; # llndk diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp index ef863b6d67..1f0128a48e 100644 --- a/libs/nativewindow/tests/AHardwareBufferTest.cpp +++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "AHardwareBuffer_test" //#define LOG_NDEBUG 0 +#include <android-base/properties.h> +#include <android/data_space.h> #include <android/hardware/graphics/common/1.0/types.h> #include <gtest/gtest.h> #include <private/android/AHardwareBufferHelpers.h> @@ -26,6 +28,10 @@ using namespace android; using android::hardware::graphics::common::V1_0::BufferUsage; +static bool IsCuttlefish() { + return ::android::base::GetProperty("ro.product.board", "") == "cutf"; +} + static ::testing::AssertionResult BuildHexFailureMessage(uint64_t expected, uint64_t actual, const char* type) { std::ostringstream ss; @@ -170,3 +176,83 @@ TEST(AHardwareBufferTest, GetIdTest) { EXPECT_NE(id1, id2); } + +TEST(AHardwareBufferTest, Allocate2NoExtras) { + AHardwareBuffer_Desc desc{ + .width = 64, + .height = 1, + .layers = 1, + .format = AHARDWAREBUFFER_FORMAT_BLOB, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, + .stride = 0, + }; + + AHardwareBuffer* buffer = nullptr; + ASSERT_EQ(0, AHardwareBuffer_allocate2(&desc, nullptr, 0, &buffer)); + uint64_t id = 0; + EXPECT_EQ(0, AHardwareBuffer_getId(buffer, &id)); + EXPECT_NE(0, id); + AHardwareBuffer_Desc desc2{}; + AHardwareBuffer_describe(buffer, &desc2); + EXPECT_EQ(desc.width, desc2.width); + EXPECT_EQ(desc.height, desc2.height); + EXPECT_GE(desc2.stride, desc2.width); + + AHardwareBuffer_release(buffer); +} + +TEST(AHardwareBufferTest, Allocate2WithExtras) { + if (!IsCuttlefish()) { + GTEST_SKIP() << "Unknown gralloc HAL, cannot test extras"; + } + + AHardwareBuffer_Desc desc{ + .width = 64, + .height = 48, + .layers = 1, + .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, + .stride = 0, + }; + + AHardwareBuffer* buffer = nullptr; + std::array<AHardwareBufferLongOptions, 1> extras = {{ + {.name = "android.hardware.graphics.common.Dataspace", ADATASPACE_DISPLAY_P3}, + }}; + ASSERT_EQ(0, AHardwareBuffer_allocate2(&desc, extras.data(), extras.size(), &buffer)); + uint64_t id = 0; + EXPECT_EQ(0, AHardwareBuffer_getId(buffer, &id)); + EXPECT_NE(0, id); + AHardwareBuffer_Desc desc2{}; + AHardwareBuffer_describe(buffer, &desc2); + EXPECT_EQ(desc.width, desc2.width); + EXPECT_EQ(desc.height, desc2.height); + EXPECT_GE(desc2.stride, desc2.width); + + EXPECT_EQ(ADATASPACE_DISPLAY_P3, AHardwareBuffer_getDataSpace(buffer)); + + AHardwareBuffer_release(buffer); +} + +TEST(AHardwareBufferTest, GetSetDataspace) { + AHardwareBuffer_Desc desc{ + .width = 64, + .height = 48, + .layers = 1, + .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, + .stride = 0, + }; + + AHardwareBuffer* buffer = nullptr; + ASSERT_EQ(0, AHardwareBuffer_allocate(&desc, &buffer)); + + EXPECT_EQ(ADATASPACE_UNKNOWN, AHardwareBuffer_getDataSpace(buffer)); + AHardwareBufferStatus status = AHardwareBuffer_setDataSpace(buffer, ADATASPACE_DISPLAY_P3); + if (status != AHARDWAREBUFFER_STATUS_UNSUPPORTED) { + EXPECT_EQ(0, status); + EXPECT_EQ(ADATASPACE_DISPLAY_P3, AHardwareBuffer_getDataSpace(buffer)); + } + + AHardwareBuffer_release(buffer); +}
\ No newline at end of file diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp index 30737c1bf6..d7c7eb3153 100644 --- a/libs/nativewindow/tests/Android.bp +++ b/libs/nativewindow/tests/Android.bp @@ -31,6 +31,7 @@ cc_test { "device-tests", ], shared_libs: [ + "libbase", "libgui", "liblog", "libnativewindow", @@ -44,5 +45,8 @@ cc_test { "ANativeWindowTest.cpp", "c_compatibility.c", ], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], } diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index 2053c6a34f..d688b51793 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -236,7 +236,13 @@ EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bo err = selectEGLConfig(display, format, 0, &config); if (err != NO_ERROR) { // this EGL is too lame for android - LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up"); + LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up" + " (format: %d, vendor: %s, version: %s, extensions: %s, Client" + " API: %s)", + format, eglQueryString(display, EGL_VENDOR), + eglQueryString(display, EGL_VERSION), + eglQueryString(display, EGL_EXTENSIONS), + eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported"); } } } diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp index e9b5decee8..a5aca9912f 100644 --- a/libs/ui/Gralloc2.cpp +++ b/libs/ui/Gralloc2.cpp @@ -384,8 +384,8 @@ std::string Gralloc2Allocator::dumpDebugInfo(bool /*less*/) const { status_t Gralloc2Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, - uint32_t bufferCount, uint32_t* outStride, - buffer_handle_t* outBufferHandles, bool importBuffers) const { + uint32_t* outStride, buffer_handle_t* outBufferHandles, + bool importBuffers) const { IMapper::BufferDescriptorInfo descriptorInfo = {}; descriptorInfo.width = width; descriptorInfo.height = height; @@ -400,6 +400,8 @@ status_t Gralloc2Allocator::allocate(std::string /*requestorName*/, uint32_t wid return error; } + constexpr auto bufferCount = 1; + auto ret = mAllocator->allocate(descriptor, bufferCount, [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) { diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp index 474d381dbb..152b35a505 100644 --- a/libs/ui/Gralloc3.cpp +++ b/libs/ui/Gralloc3.cpp @@ -371,7 +371,7 @@ std::string Gralloc3Allocator::dumpDebugInfo(bool /*less*/) const { status_t Gralloc3Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, - uint64_t usage, uint32_t bufferCount, uint32_t* outStride, + uint64_t usage, uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers) const { IMapper::BufferDescriptorInfo descriptorInfo; sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo); @@ -383,6 +383,8 @@ status_t Gralloc3Allocator::allocate(std::string /*requestorName*/, uint32_t wid return error; } + constexpr auto bufferCount = 1; + auto ret = mAllocator->allocate(descriptor, bufferCount, [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) { diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp index 03ff58a76c..d6970e0477 100644 --- a/libs/ui/Gralloc4.cpp +++ b/libs/ui/Gralloc4.cpp @@ -1069,7 +1069,7 @@ std::string Gralloc4Allocator::dumpDebugInfo(bool less) const { status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, - uint64_t usage, uint32_t bufferCount, uint32_t* outStride, + uint64_t usage, uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers) const { IMapper::BufferDescriptorInfo descriptorInfo; if (auto error = sBufferDescriptorInfo(requestorName, width, height, format, layerCount, usage, @@ -1084,6 +1084,8 @@ status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, return error; } + constexpr auto bufferCount = 1; + if (mAidlAllocator) { AllocationResult result; #pragma clang diagnostic push diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp index 2ec6d18fda..b07e15534a 100644 --- a/libs/ui/Gralloc5.cpp +++ b/libs/ui/Gralloc5.cpp @@ -19,6 +19,7 @@ #include <ui/Gralloc5.h> +#include <aidl/android/hardware/graphics/allocator/AllocationError.h> #include <aidlcommonsupport/NativeHandle.h> #include <android/binder_manager.h> #include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h> @@ -215,55 +216,75 @@ std::string Gralloc5Allocator::dumpDebugInfo(bool less) const { status_t Gralloc5Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, - uint64_t usage, uint32_t bufferCount, uint32_t *outStride, - buffer_handle_t *outBufferHandles, bool importBuffers) const { - auto descriptorInfo = makeDescriptor(requestorName, width, height, format, layerCount, usage); + uint64_t usage, uint32_t* outStride, + buffer_handle_t* outBufferHandles, bool importBuffers) const { + auto result = allocate(GraphicBufferAllocator::AllocationRequest{ + .importBuffer = importBuffers, + .width = width, + .height = height, + .format = format, + .layerCount = layerCount, + .usage = usage, + .requestorName = requestorName, + }); + + *outStride = result.stride; + outBufferHandles[0] = result.handle; + return result.status; +} + +GraphicBufferAllocator::AllocationResult Gralloc5Allocator::allocate( + const GraphicBufferAllocator::AllocationRequest& request) const { + auto descriptorInfo = makeDescriptor(request.requestorName, request.width, request.height, + request.format, request.layerCount, request.usage); if (!descriptorInfo) { - return BAD_VALUE; + return GraphicBufferAllocator::AllocationResult{BAD_VALUE}; + } + + descriptorInfo->additionalOptions.reserve(request.extras.size()); + for (const auto& option : request.extras) { + ExtendableType type; + type.name = option.name; + type.value = option.value; + descriptorInfo->additionalOptions.push_back(std::move(type)); } AllocationResult result; - auto status = mAllocator->allocate2(*descriptorInfo, bufferCount, &result); + auto status = mAllocator->allocate2(*descriptorInfo, 1, &result); if (!status.isOk()) { auto error = status.getExceptionCode(); if (error == EX_SERVICE_SPECIFIC) { - error = status.getServiceSpecificError(); - } - if (error == OK) { - error = UNKNOWN_ERROR; + switch (static_cast<AllocationError>(status.getServiceSpecificError())) { + case AllocationError::BAD_DESCRIPTOR: + error = BAD_VALUE; + break; + case AllocationError::NO_RESOURCES: + error = NO_MEMORY; + break; + default: + error = UNKNOWN_ERROR; + break; + } } - return error; + return GraphicBufferAllocator::AllocationResult{error}; } - if (importBuffers) { - for (uint32_t i = 0; i < bufferCount; i++) { - auto handle = makeFromAidl(result.buffers[i]); - auto error = mMapper.importBuffer(handle, &outBufferHandles[i]); - native_handle_delete(handle); - if (error != NO_ERROR) { - for (uint32_t j = 0; j < i; j++) { - mMapper.freeBuffer(outBufferHandles[j]); - outBufferHandles[j] = nullptr; - } - return error; - } + GraphicBufferAllocator::AllocationResult ret{OK}; + if (request.importBuffer) { + auto handle = makeFromAidl(result.buffers[0]); + auto error = mMapper.importBuffer(handle, &ret.handle); + native_handle_delete(handle); + if (error != NO_ERROR) { + return GraphicBufferAllocator::AllocationResult{error}; } } else { - for (uint32_t i = 0; i < bufferCount; i++) { - outBufferHandles[i] = dupFromAidl(result.buffers[i]); - if (!outBufferHandles[i]) { - for (uint32_t j = 0; j < i; j++) { - auto buffer = const_cast<native_handle_t *>(outBufferHandles[j]); - native_handle_close(buffer); - native_handle_delete(buffer); - outBufferHandles[j] = nullptr; - } - return NO_MEMORY; - } + ret.handle = dupFromAidl(result.buffers[0]); + if (!ret.handle) { + return GraphicBufferAllocator::AllocationResult{NO_MEMORY}; } } - *outStride = result.stride; + ret.stride = result.stride; // Release all the resources held by AllocationResult (specifically any remaining FDs) result = {}; @@ -272,7 +293,7 @@ status_t Gralloc5Allocator::allocate(std::string requestorName, uint32_t width, // is marked apex_available (b/214400477) and libbinder isn't (which of course is correct) // IPCThreadState::self()->flushCommands(); - return OK; + return ret; } void Gralloc5Mapper::preload() { diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 429760ffe0..c007fdb587 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -106,6 +106,26 @@ GraphicBuffer::GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod m inUsage, inStride); } +GraphicBuffer::GraphicBuffer(const GraphicBufferAllocator::AllocationRequest& request) + : GraphicBuffer() { + GraphicBufferAllocator& allocator = GraphicBufferAllocator::get(); + auto result = allocator.allocate(request); + mInitCheck = result.status; + if (result.status == NO_ERROR) { + handle = result.handle; + stride = result.stride; + + mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts); + + width = static_cast<int>(request.width); + height = static_cast<int>(request.height); + format = request.format; + layerCount = request.layerCount; + usage = request.usage; + usage_deprecated = int(usage); + } +} + GraphicBuffer::~GraphicBuffer() { ATRACE_CALL(); @@ -143,6 +163,10 @@ ANativeWindowBuffer* GraphicBuffer::getNativeBuffer() const const_cast<GraphicBuffer*>(this)); } +status_t GraphicBuffer::getDataspace(ui::Dataspace* outDataspace) const { + return mBufferMapper.getDataspace(handle, outDataspace); +} + status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage) { diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index eb0bd4ed0a..98082fb81e 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -113,6 +113,79 @@ void GraphicBufferAllocator::dumpToSystemLog(bool less) { ALOGD("%s", s.c_str()); } +auto GraphicBufferAllocator::allocate(const AllocationRequest& request) -> AllocationResult { + ATRACE_CALL(); + if (!request.width || !request.height) { + return AllocationResult(BAD_VALUE); + } + + const auto width = request.width; + const auto height = request.height; + + const uint32_t bpp = bytesPerPixel(request.format); + if (std::numeric_limits<size_t>::max() / width / height < static_cast<size_t>(bpp)) { + ALOGE("Failed to allocate (%u x %u) layerCount %u format %d " + "usage %" PRIx64 ": Requesting too large a buffer size", + request.width, request.height, request.layerCount, request.format, request.usage); + return AllocationResult(BAD_VALUE); + } + + if (request.layerCount < 1) { + return AllocationResult(BAD_VALUE); + } + + auto result = mAllocator->allocate(request); + if (result.status == UNKNOWN_TRANSACTION) { + if (!request.extras.empty()) { + ALOGE("Failed to allocate with additional options, allocator version mis-match? " + "gralloc version = %d", + (int)mMapper.getMapperVersion()); + return result; + } + // If there's no additional options, fall back to previous allocate version + result.status = mAllocator->allocate(request.requestorName, request.width, request.height, + request.format, request.layerCount, request.usage, + &result.stride, &result.handle, request.importBuffer); + } + + if (result.status != NO_ERROR) { + ALOGE("Failed to allocate (%u x %u) layerCount %u format %d " + "usage %" PRIx64 ": %d", + request.width, request.height, request.layerCount, request.format, request.usage, + result.status); + return result; + } + + if (!request.importBuffer) { + return result; + } + size_t bufSize; + + // if stride has no meaning or is too large, + // approximate size with the input width instead + if ((result.stride) != 0 && + std::numeric_limits<size_t>::max() / height / (result.stride) < static_cast<size_t>(bpp)) { + bufSize = static_cast<size_t>(width) * height * bpp; + } else { + bufSize = static_cast<size_t>((result.stride)) * height * bpp; + } + + Mutex::Autolock _l(sLock); + KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); + alloc_rec_t rec; + rec.width = width; + rec.height = height; + rec.stride = result.stride; + rec.format = request.format; + rec.layerCount = request.layerCount; + rec.usage = request.usage; + rec.size = bufSize; + rec.requestorName = request.requestorName; + list.add(result.handle, rec); + + return result; +} + status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, buffer_handle_t* handle, uint32_t* stride, @@ -141,7 +214,7 @@ status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13)); status_t error = mAllocator->allocate(requestorName, width, height, format, layerCount, usage, - 1, stride, handle, importBuffer); + stride, handle, importBuffer); if (error != NO_ERROR) { ALOGE("Failed to allocate (%u x %u) layerCount %u format %d " "usage %" PRIx64 ": %d", diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h index 496ba57789..e6015e0b5e 100644 --- a/libs/ui/include/ui/Gralloc.h +++ b/libs/ui/include/ui/Gralloc.h @@ -23,6 +23,7 @@ #include <ui/PixelFormat.h> #include <ui/Rect.h> #include <utils/StrongPointer.h> +#include "GraphicBufferAllocator.h" #include <string> @@ -218,9 +219,13 @@ public: */ virtual status_t allocate(std::string requestorName, uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, - uint32_t bufferCount, uint32_t* outStride, - buffer_handle_t* outBufferHandles, + uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers = true) const = 0; + + virtual GraphicBufferAllocator::AllocationResult allocate( + const GraphicBufferAllocator::AllocationRequest&) const { + return GraphicBufferAllocator::AllocationResult(UNKNOWN_TRANSACTION); + } }; } // namespace android diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h index a7b6f49206..e50bb3af3e 100644 --- a/libs/ui/include/ui/Gralloc2.h +++ b/libs/ui/include/ui/Gralloc2.h @@ -81,9 +81,8 @@ public: std::string dumpDebugInfo(bool less = true) const override; status_t allocate(std::string requestorName, uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, - uint32_t* outStride, buffer_handle_t* outBufferHandles, - bool importBuffers = true) const override; + PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride, + buffer_handle_t* outBufferHandles, bool importBuffers = true) const override; private: const Gralloc2Mapper& mMapper; diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h index 7367549964..035684abcd 100644 --- a/libs/ui/include/ui/Gralloc3.h +++ b/libs/ui/include/ui/Gralloc3.h @@ -82,9 +82,8 @@ public: std::string dumpDebugInfo(bool less = true) const override; status_t allocate(std::string requestorName, uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, - uint32_t* outStride, buffer_handle_t* outBufferHandles, - bool importBuffers = true) const override; + PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride, + buffer_handle_t* outBufferHandles, bool importBuffers = true) const override; private: const Gralloc3Mapper& mMapper; diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h index df43be87cd..0f469c0b7e 100644 --- a/libs/ui/include/ui/Gralloc4.h +++ b/libs/ui/include/ui/Gralloc4.h @@ -174,9 +174,8 @@ public: std::string dumpDebugInfo(bool less = true) const override; status_t allocate(std::string requestorName, uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, - uint32_t* outStride, buffer_handle_t* outBufferHandles, - bool importBuffers = true) const override; + PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride, + buffer_handle_t* outBufferHandles, bool importBuffers = true) const override; private: const Gralloc4Mapper& mMapper; diff --git a/libs/ui/include/ui/Gralloc5.h b/libs/ui/include/ui/Gralloc5.h index 44b97d1a6f..f9e8f5e9fd 100644 --- a/libs/ui/include/ui/Gralloc5.h +++ b/libs/ui/include/ui/Gralloc5.h @@ -172,10 +172,12 @@ public: [[nodiscard]] status_t allocate(std::string requestorName, uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, - uint32_t bufferCount, uint32_t *outStride, - buffer_handle_t *outBufferHandles, + uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers) const override; + [[nodiscard]] GraphicBufferAllocator::AllocationResult allocate( + const GraphicBufferAllocator::AllocationRequest&) const override; + private: const Gralloc5Mapper &mMapper; std::shared_ptr<aidl::android::hardware::graphics::allocator::IAllocator> mAllocator; diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h index f859848b0c..652d8ba709 100644 --- a/libs/ui/include/ui/GraphicBuffer.h +++ b/libs/ui/include/ui/GraphicBuffer.h @@ -26,6 +26,7 @@ #include <android/hardware_buffer.h> #include <ui/ANativeObjectBase.h> +#include <ui/GraphicBufferAllocator.h> #include <ui/GraphicBufferMapper.h> #include <ui/PixelFormat.h> #include <ui/Rect.h> @@ -103,6 +104,8 @@ public: uint32_t inLayerCount, uint64_t inUsage, std::string requestorName = "<Unknown>"); + GraphicBuffer(const GraphicBufferAllocator::AllocationRequest&); + // Create a GraphicBuffer from an existing handle. enum HandleWrapMethod : uint8_t { // Wrap and use the handle directly. It assumes the handle has been @@ -169,6 +172,8 @@ public: mGenerationNumber = generation; } + status_t getDataspace(ui::Dataspace* outDataspace) const; + // This function is privileged. It requires access to the allocator // device or service, which usually involves adding suitable selinux // rules. diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h index 3ed988c4c2..8f461e193b 100644 --- a/libs/ui/include/ui/GraphicBufferAllocator.h +++ b/libs/ui/include/ui/GraphicBufferAllocator.h @@ -22,6 +22,7 @@ #include <memory> #include <string> +#include <vector> #include <cutils/native_handle.h> @@ -42,6 +43,35 @@ class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator> public: static inline GraphicBufferAllocator& get() { return getInstance(); } + struct AdditionalOptions { + const char* name; + int64_t value; + }; + + struct AllocationRequest { + bool importBuffer; + uint32_t width; + uint32_t height; + PixelFormat format; + uint32_t layerCount; + uint64_t usage; + std::string requestorName; + std::vector<AdditionalOptions> extras; + }; + + struct AllocationResult { + status_t status; + buffer_handle_t handle = nullptr; + uint32_t stride = 0; + + explicit AllocationResult(status_t status) : status(status) {} + + explicit AllocationResult(buffer_handle_t handle, uint32_t stride) + : status(OK), handle(handle), stride(stride) {} + }; + + AllocationResult allocate(const AllocationRequest&); + /** * Allocates and imports a gralloc buffer. * diff --git a/libs/ui/tests/GraphicBufferAllocator_test.cpp b/libs/ui/tests/GraphicBufferAllocator_test.cpp index f4c0afa71b..efca083e6e 100644 --- a/libs/ui/tests/GraphicBufferAllocator_test.cpp +++ b/libs/ui/tests/GraphicBufferAllocator_test.cpp @@ -51,7 +51,7 @@ public: std::cout << "Setting expected stride to " << stride << std::endl; EXPECT_CALL(*(reinterpret_cast<const mock::MockGrallocAllocator*>(mAllocator.get())), allocate) - .WillOnce(DoAll(SetArgPointee<7>(stride), Return(err))); + .WillOnce(DoAll(SetArgPointee<6>(stride), Return(err))); } std::unique_ptr<const GrallocAllocator>& getAllocator() { return mAllocator; } }; diff --git a/libs/ui/tests/mock/MockGrallocAllocator.h b/libs/ui/tests/mock/MockGrallocAllocator.h index d62e3e2192..d02b3873e0 100644 --- a/libs/ui/tests/mock/MockGrallocAllocator.h +++ b/libs/ui/tests/mock/MockGrallocAllocator.h @@ -35,7 +35,7 @@ public: MOCK_METHOD(std::string, dumpDebugInfo, (bool less), (const, override)); MOCK_METHOD(status_t, allocate, (std::string requestorName, uint32_t width, uint32_t height, PixelFormat format, - uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride, + uint32_t layerCount, uint64_t usage, uint32_t* outStride, buffer_handle_t* outBufferHandles, bool less), (const, override)); }; diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 45c9b5cf0b..69f42bc800 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -77,6 +77,7 @@ filegroup { "InputCommonConverter.cpp", "InputDeviceMetricsCollector.cpp", "InputFilter.cpp", + "InputFilterCallbacks.cpp", "InputProcessor.cpp", "PointerChoreographer.cpp", "PreferStylusOverTouchBlocker.cpp", diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp index 9c4a3eb274..5d87d34adb 100644 --- a/services/inputflinger/InputFilter.cpp +++ b/services/inputflinger/InputFilter.cpp @@ -44,31 +44,9 @@ AidlKeyEvent notifyKeyArgsToKeyEvent(const NotifyKeyArgs& args) { return event; } -NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) { - return NotifyKeyArgs(event.id, event.eventTime, event.readTime, event.deviceId, - static_cast<uint32_t>(event.source), event.displayId, event.policyFlags, - static_cast<int32_t>(event.action), event.flags, event.keyCode, - event.scanCode, event.metaState, event.downTime); -} - -namespace { - -class RustCallbacks : public IInputFilter::BnInputFilterCallbacks { -public: - RustCallbacks(InputListenerInterface& nextListener) : mNextListener(nextListener) {} - ndk::ScopedAStatus sendKeyEvent(const AidlKeyEvent& event) override { - mNextListener.notifyKey(keyEventToNotifyKeyArgs(event)); - return ndk::ScopedAStatus::ok(); - } - -private: - InputListenerInterface& mNextListener; -}; - -} // namespace - InputFilter::InputFilter(InputListenerInterface& listener, IInputFlingerRust& rust) - : mNextListener(listener), mCallbacks(ndk::SharedRefBase::make<RustCallbacks>(listener)) { + : mNextListener(listener), + mCallbacks(ndk::SharedRefBase::make<InputFilterCallbacks>(listener)) { LOG_ALWAYS_FATAL_IF(!rust.createInputFilter(mCallbacks, &mInputFilterRust).isOk()); LOG_ALWAYS_FATAL_IF(!mInputFilterRust); } @@ -92,11 +70,11 @@ void InputFilter::notifyConfigurationChanged(const NotifyConfigurationChangedArg } void InputFilter::notifyKey(const NotifyKeyArgs& args) { - if (!isFilterEnabled()) { - mNextListener.notifyKey(args); + if (isFilterEnabled()) { + LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk()); return; } - LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk()); + mNextListener.notifyKey(args); } void InputFilter::notifyMotion(const NotifyMotionArgs& args) { @@ -138,6 +116,15 @@ void InputFilter::setAccessibilityBounceKeysThreshold(nsecs_t threshold) { } } +void InputFilter::setAccessibilityStickyKeysEnabled(bool enabled) { + std::scoped_lock _l(mLock); + + if (mConfig.stickyKeysEnabled != enabled) { + mConfig.stickyKeysEnabled = enabled; + LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyConfigurationChanged(mConfig).isOk()); + } +} + void InputFilter::dump(std::string& dump) { dump += "InputFilter:\n"; } diff --git a/services/inputflinger/InputFilter.h b/services/inputflinger/InputFilter.h index 06f7d0e601..9fa7a8758f 100644 --- a/services/inputflinger/InputFilter.h +++ b/services/inputflinger/InputFilter.h @@ -18,6 +18,7 @@ #include <aidl/com/android/server/inputflinger/IInputFlingerRust.h> #include <utils/Mutex.h> +#include "InputFilterCallbacks.h" #include "InputListener.h" #include "NotifyArgs.h" @@ -33,6 +34,7 @@ public: */ virtual void dump(std::string& dump) = 0; virtual void setAccessibilityBounceKeysThreshold(nsecs_t threshold) = 0; + virtual void setAccessibilityStickyKeysEnabled(bool enabled) = 0; }; class InputFilter : public InputFilterInterface { @@ -56,11 +58,12 @@ public: void notifyDeviceReset(const NotifyDeviceResetArgs& args) override; void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override; void setAccessibilityBounceKeysThreshold(nsecs_t threshold) override; + void setAccessibilityStickyKeysEnabled(bool enabled) override; void dump(std::string& dump) override; private: InputListenerInterface& mNextListener; - std::shared_ptr<IInputFilterCallbacks> mCallbacks; + std::shared_ptr<InputFilterCallbacks> mCallbacks; std::shared_ptr<IInputFilter> mInputFilterRust; mutable std::mutex mLock; InputFilterConfiguration mConfig GUARDED_BY(mLock); diff --git a/services/inputflinger/InputFilterCallbacks.cpp b/services/inputflinger/InputFilterCallbacks.cpp new file mode 100644 index 0000000000..8c8f5e8f71 --- /dev/null +++ b/services/inputflinger/InputFilterCallbacks.cpp @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#define LOG_TAG "InputFilterCallbacks" + +#include "InputFilterCallbacks.h" + +namespace android { + +using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent; + +NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) { + return NotifyKeyArgs(event.id, event.eventTime, event.readTime, event.deviceId, + static_cast<uint32_t>(event.source), event.displayId, event.policyFlags, + static_cast<int32_t>(event.action), event.flags, event.keyCode, + event.scanCode, event.metaState, event.downTime); +} + +InputFilterCallbacks::InputFilterCallbacks(InputListenerInterface& listener) + : mNextListener(listener) {} + +ndk::ScopedAStatus InputFilterCallbacks::sendKeyEvent(const AidlKeyEvent& event) { + mNextListener.notifyKey(keyEventToNotifyKeyArgs(event)); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus InputFilterCallbacks::onModifierStateChanged(int32_t modifierState, + int32_t lockedModifierState) { + std::scoped_lock _l(mLock); + mStickyModifierState.modifierState = modifierState; + mStickyModifierState.lockedModifierState = lockedModifierState; + ALOGI("Sticky keys modifier state changed: modifierState=%d, lockedModifierState=%d", + modifierState, lockedModifierState); + return ndk::ScopedAStatus::ok(); +} + +uint32_t InputFilterCallbacks::getModifierState() { + std::scoped_lock _l(mLock); + return mStickyModifierState.modifierState; +} + +uint32_t InputFilterCallbacks::getLockedModifierState() { + std::scoped_lock _l(mLock); + return mStickyModifierState.lockedModifierState; +} + +} // namespace android diff --git a/services/inputflinger/InputFilterCallbacks.h b/services/inputflinger/InputFilterCallbacks.h new file mode 100644 index 0000000000..c0a80fb6dc --- /dev/null +++ b/services/inputflinger/InputFilterCallbacks.h @@ -0,0 +1,55 @@ +/* + * 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 <aidl/com/android/server/inputflinger/IInputFlingerRust.h> +#include <android/binder_auto_utils.h> +#include <utils/Mutex.h> +#include <mutex> +#include "InputListener.h" +#include "NotifyArgs.h" + +/** + * The C++ component of InputFilter designed as a wrapper around the rust callback implementation. + */ +namespace android { + +using IInputFilter = aidl::com::android::server::inputflinger::IInputFilter; +using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent; + +class InputFilterCallbacks : public IInputFilter::BnInputFilterCallbacks { +public: + explicit InputFilterCallbacks(InputListenerInterface& listener); + ~InputFilterCallbacks() override = default; + + uint32_t getModifierState(); + uint32_t getLockedModifierState(); + +private: + InputListenerInterface& mNextListener; + mutable std::mutex mLock; + struct StickyModifierState { + uint32_t modifierState; + uint32_t lockedModifierState; + } mStickyModifierState GUARDED_BY(mLock); + + ndk::ScopedAStatus sendKeyEvent(const AidlKeyEvent& event) override; + ndk::ScopedAStatus onModifierStateChanged(int32_t modifierState, + int32_t lockedModifierState) override; +}; + +} // namespace android
\ No newline at end of file diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl index 14b41cd00c..2921d30b22 100644 --- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl +++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl @@ -33,6 +33,9 @@ interface IInputFilter { interface IInputFilterCallbacks { /** Sends back a filtered key event */ void sendKeyEvent(in KeyEvent event); + + /** Sends back modifier state */ + void onModifierStateChanged(int modifierState, int lockedModifierState); } /** Returns if InputFilter is enabled */ diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl index 3b2e88ba24..38b161203b 100644 --- a/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl +++ b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl @@ -22,4 +22,6 @@ package com.android.server.inputflinger; parcelable InputFilterConfiguration { // Threshold value for Bounce keys filter (check bounce_keys_filter.rs) long bounceKeysThresholdNs; + // If sticky keys filter is enabled + boolean stickyKeysEnabled; }
\ No newline at end of file diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs index 340ff8e296..e94a71fbf8 100644 --- a/services/inputflinger/rust/input_filter.rs +++ b/services/inputflinger/rust/input_filter.rs @@ -27,6 +27,7 @@ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ }; use crate::bounce_keys_filter::BounceKeysFilter; +use crate::sticky_keys_filter::StickyKeysFilter; use log::{error, info}; use std::sync::{Arc, Mutex, RwLock}; @@ -91,6 +92,14 @@ impl IInputFilter for InputFilter { let mut state = self.state.lock().unwrap(); let mut first_filter: Box<dyn Filter + Send + Sync> = Box::new(BaseFilter::new(self.callbacks.clone())); + if config.stickyKeysEnabled { + first_filter = Box::new(StickyKeysFilter::new( + first_filter, + ModifierStateListener::new(self.callbacks.clone()), + )); + state.enabled = true; + info!("Sticky keys filter is installed"); + } if config.bounceKeysThresholdNs > 0 { first_filter = Box::new(BounceKeysFilter::new(first_filter, config.bounceKeysThresholdNs)); @@ -125,34 +134,43 @@ impl Filter for BaseFilter { } } +pub struct ModifierStateListener { + callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>, +} + +impl ModifierStateListener { + /// Create a new InputFilter instance. + pub fn new(callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>) -> ModifierStateListener { + Self { callbacks } + } + + pub fn modifier_state_changed(&self, modifier_state: u32, locked_modifier_state: u32) { + let _ = self + .callbacks + .read() + .unwrap() + .onModifierStateChanged(modifier_state as i32, locked_modifier_state as i32); + } +} + #[cfg(test)] mod tests { - use crate::input_filter::{test_filter::TestFilter, Filter, InputFilter}; + use crate::input_filter::{ + test_callbacks::TestCallbacks, test_filter::TestFilter, InputFilter, + }; use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source; - use binder::{Interface, Strong}; + use binder::Strong; use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, IInputFilter::IInputFilter, - IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, InputFilterConfiguration::InputFilterConfiguration, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; use std::sync::{Arc, RwLock}; - struct FakeCallbacks {} - - impl Interface for FakeCallbacks {} - - impl IInputFilterCallbacks for FakeCallbacks { - fn sendKeyEvent(&self, _event: &KeyEvent) -> binder::Result<()> { - Result::Ok(()) - } - } - #[test] fn test_not_enabled_with_default_filter() { - let fake_callbacks: Strong<dyn IInputFilterCallbacks> = - Strong::new(Box::new(FakeCallbacks {})); - let input_filter = InputFilter::new(fake_callbacks); + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks))); let result = input_filter.isEnabled(); assert!(result.is_ok()); assert!(!result.unwrap()); @@ -160,17 +178,21 @@ mod tests { #[test] fn test_notify_key_with_no_filters() { - let fake_callbacks: Strong<dyn IInputFilterCallbacks> = - Strong::new(Box::new(FakeCallbacks {})); - let input_filter = InputFilter::new(fake_callbacks); + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks.clone()))); let event = create_key_event(); assert!(input_filter.notifyKey(&event).is_ok()); + assert_eq!(test_callbacks.last_event().unwrap(), event); } #[test] fn test_notify_key_with_filter() { let test_filter = TestFilter::new(); - let input_filter = create_input_filter(Box::new(test_filter.clone())); + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::create_input_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))), + ); let event = create_key_event(); assert!(input_filter.notifyKey(&event).is_ok()); assert_eq!(test_filter.last_event().unwrap(), event); @@ -179,7 +201,11 @@ mod tests { #[test] fn test_notify_devices_changed() { let test_filter = TestFilter::new(); - let input_filter = create_input_filter(Box::new(test_filter.clone())); + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::create_input_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))), + ); assert!(input_filter .notifyInputDevicesChanged(&[DeviceInfo { deviceId: 0, external: true }]) .is_ok()); @@ -188,21 +214,30 @@ mod tests { #[test] fn test_notify_configuration_changed_enabled_bounce_keys() { - let fake_callbacks: Strong<dyn IInputFilterCallbacks> = - Strong::new(Box::new(FakeCallbacks {})); - let input_filter = InputFilter::new(fake_callbacks); - let result = input_filter - .notifyConfigurationChanged(&InputFilterConfiguration { bounceKeysThresholdNs: 100 }); + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks))); + let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration { + bounceKeysThresholdNs: 100, + stickyKeysEnabled: false, + }); assert!(result.is_ok()); let result = input_filter.isEnabled(); assert!(result.is_ok()); assert!(result.unwrap()); } - fn create_input_filter(filter: Box<dyn Filter + Send + Sync>) -> InputFilter { - let fake_callbacks: Strong<dyn IInputFilterCallbacks> = - Strong::new(Box::new(FakeCallbacks {})); - InputFilter::create_input_filter(filter, Arc::new(RwLock::new(fake_callbacks))) + #[test] + fn test_notify_configuration_changed_enabled_sticky_keys() { + let test_callbacks = TestCallbacks::new(); + let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks))); + let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration { + bounceKeysThresholdNs: 0, + stickyKeysEnabled: true, + }); + assert!(result.is_ok()); + let result = input_filter.isEnabled(); + assert!(result.is_ok()); + assert!(result.unwrap()); } fn create_key_event() -> KeyEvent { @@ -272,3 +307,69 @@ pub mod test_filter { } } } + +#[cfg(test)] +pub mod test_callbacks { + use binder::Interface; + use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ + IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, KeyEvent::KeyEvent, + }; + use std::sync::{Arc, RwLock, RwLockWriteGuard}; + + #[derive(Default)] + struct TestCallbacksInner { + last_modifier_state: u32, + last_locked_modifier_state: u32, + last_event: Option<KeyEvent>, + } + + #[derive(Default, Clone)] + pub struct TestCallbacks(Arc<RwLock<TestCallbacksInner>>); + + impl Interface for TestCallbacks {} + + impl TestCallbacks { + pub fn new() -> Self { + Default::default() + } + + fn inner(&self) -> RwLockWriteGuard<'_, TestCallbacksInner> { + self.0.write().unwrap() + } + + pub fn last_event(&self) -> Option<KeyEvent> { + self.0.read().unwrap().last_event + } + + pub fn clear(&mut self) { + self.inner().last_event = None; + self.inner().last_modifier_state = 0; + self.inner().last_locked_modifier_state = 0; + } + + pub fn get_last_modifier_state(&self) -> u32 { + self.0.read().unwrap().last_modifier_state + } + + pub fn get_last_locked_modifier_state(&self) -> u32 { + self.0.read().unwrap().last_locked_modifier_state + } + } + + impl IInputFilterCallbacks for TestCallbacks { + fn sendKeyEvent(&self, event: &KeyEvent) -> binder::Result<()> { + self.inner().last_event = Some(*event); + Result::Ok(()) + } + + fn onModifierStateChanged( + &self, + modifier_state: i32, + locked_modifier_state: i32, + ) -> std::result::Result<(), binder::Status> { + self.inner().last_modifier_state = modifier_state as u32; + self.inner().last_locked_modifier_state = locked_modifier_state as u32; + Result::Ok(()) + } + } +} diff --git a/services/inputflinger/rust/lib.rs b/services/inputflinger/rust/lib.rs index 68cd4800fe..fa16898835 100644 --- a/services/inputflinger/rust/lib.rs +++ b/services/inputflinger/rust/lib.rs @@ -21,6 +21,7 @@ mod bounce_keys_filter; mod input_filter; +mod sticky_keys_filter; use crate::input_filter::InputFilter; use binder::{ diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs new file mode 100644 index 0000000000..da581b82bf --- /dev/null +++ b/services/inputflinger/rust/sticky_keys_filter.rs @@ -0,0 +1,515 @@ +/* + * 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. + */ + +//! Sticky keys input filter implementation. +//! Sticky keys is an accessibility feature that assists users who have physical disabilities or +//! helps users reduce repetitive strain injury. It serializes keystrokes instead of pressing +//! multiple keys at a time, allowing the user to press and release a modifier key, such as Shift, +//! Ctrl, Alt, or any other modifier key, and have it remain active until any other key is pressed. +use crate::input_filter::{Filter, ModifierStateListener}; +use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ + DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, +}; +use std::collections::HashSet; + +// Modifier keycodes: values are from /frameworks/native/include/android/keycodes.h +const KEYCODE_ALT_LEFT: i32 = 57; +const KEYCODE_ALT_RIGHT: i32 = 58; +const KEYCODE_SHIFT_LEFT: i32 = 59; +const KEYCODE_SHIFT_RIGHT: i32 = 60; +const KEYCODE_SYM: i32 = 63; +const KEYCODE_CTRL_LEFT: i32 = 113; +const KEYCODE_CTRL_RIGHT: i32 = 114; +const KEYCODE_CAPS_LOCK: i32 = 115; +const KEYCODE_SCROLL_LOCK: i32 = 116; +const KEYCODE_META_LEFT: i32 = 117; +const KEYCODE_META_RIGHT: i32 = 118; +const KEYCODE_FUNCTION: i32 = 119; +const KEYCODE_NUM_LOCK: i32 = 143; + +// Modifier states: values are from /frameworks/native/include/android/input.h +const META_ALT_ON: u32 = 0x02; +const META_ALT_LEFT_ON: u32 = 0x10; +const META_ALT_RIGHT_ON: u32 = 0x20; +const META_SHIFT_ON: u32 = 0x01; +const META_SHIFT_LEFT_ON: u32 = 0x40; +const META_SHIFT_RIGHT_ON: u32 = 0x80; +const META_CTRL_ON: u32 = 0x1000; +const META_CTRL_LEFT_ON: u32 = 0x2000; +const META_CTRL_RIGHT_ON: u32 = 0x4000; +const META_META_ON: u32 = 0x10000; +const META_META_LEFT_ON: u32 = 0x20000; +const META_META_RIGHT_ON: u32 = 0x40000; + +pub struct StickyKeysFilter { + next: Box<dyn Filter + Send + Sync>, + listener: ModifierStateListener, + /// Tracking devices that contributed to the modifier state. + contributing_devices: HashSet<i32>, + /// State describing the current enabled modifiers. This contain both locked and non-locked + /// modifier state bits. + modifier_state: u32, + /// State describing the current locked modifiers. These modifiers will not be cleared on a + /// non-modifier key press. They will be cleared only if the locked modifier key is pressed + /// again. + locked_modifier_state: u32, +} + +impl StickyKeysFilter { + /// Create a new StickyKeysFilter instance. + pub fn new( + next: Box<dyn Filter + Send + Sync>, + listener: ModifierStateListener, + ) -> StickyKeysFilter { + Self { + next, + listener, + contributing_devices: HashSet::new(), + modifier_state: 0, + locked_modifier_state: 0, + } + } +} + +impl Filter for StickyKeysFilter { + fn notify_key(&mut self, event: &KeyEvent) { + let up = event.action == KeyEventAction::UP; + let mut modifier_state = self.modifier_state; + let mut locked_modifier_state = self.locked_modifier_state; + if !is_ephemeral_modifier_key(event.keyCode) { + // If non-ephemeral modifier key (i.e. non-modifier keys + toggle modifier keys like + // CAPS_LOCK, NUM_LOCK etc.), don't block key and pass in the sticky modifier state with + // the KeyEvent. + let old_modifier_state = event.metaState as u32; + let mut new_event = *event; + // Send the current modifier state with the key event before clearing non-locked + // modifier state + new_event.metaState = + (clear_ephemeral_modifier_state(old_modifier_state) | modifier_state) as i32; + self.next.notify_key(&new_event); + if up && !is_modifier_key(event.keyCode) { + modifier_state = + clear_ephemeral_modifier_state(modifier_state) | locked_modifier_state; + } + } else if up { + // Update contributing devices to track keyboards + self.contributing_devices.insert(event.deviceId); + // If ephemeral modifier key, capture the key and update the sticky modifier states + let modifier_key_mask = get_ephemeral_modifier_key_mask(event.keyCode); + let symmetrical_modifier_key_mask = get_symmetrical_modifier_key_mask(event.keyCode); + if locked_modifier_state & modifier_key_mask != 0 { + locked_modifier_state &= !symmetrical_modifier_key_mask; + modifier_state &= !symmetrical_modifier_key_mask; + } else if modifier_key_mask & modifier_state != 0 { + locked_modifier_state |= modifier_key_mask; + modifier_state = + (modifier_state & !symmetrical_modifier_key_mask) | modifier_key_mask; + } else { + modifier_state |= modifier_key_mask; + } + } + if self.modifier_state != modifier_state + || self.locked_modifier_state != locked_modifier_state + { + self.modifier_state = modifier_state; + self.locked_modifier_state = locked_modifier_state; + self.listener.modifier_state_changed(modifier_state, locked_modifier_state); + } + } + + fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]) { + // Clear state if all contributing devices removed + self.contributing_devices.retain(|id| device_infos.iter().any(|x| *id == x.deviceId)); + if self.contributing_devices.is_empty() + && (self.modifier_state != 0 || self.locked_modifier_state != 0) + { + self.modifier_state = 0; + self.locked_modifier_state = 0; + self.listener.modifier_state_changed(0, 0); + } + self.next.notify_devices_changed(device_infos); + } +} + +fn is_modifier_key(keycode: i32) -> bool { + matches!( + keycode, + KEYCODE_ALT_LEFT + | KEYCODE_ALT_RIGHT + | KEYCODE_SHIFT_LEFT + | KEYCODE_SHIFT_RIGHT + | KEYCODE_CTRL_LEFT + | KEYCODE_CTRL_RIGHT + | KEYCODE_META_LEFT + | KEYCODE_META_RIGHT + | KEYCODE_SYM + | KEYCODE_FUNCTION + | KEYCODE_CAPS_LOCK + | KEYCODE_NUM_LOCK + | KEYCODE_SCROLL_LOCK + ) +} + +fn is_ephemeral_modifier_key(keycode: i32) -> bool { + matches!( + keycode, + KEYCODE_ALT_LEFT + | KEYCODE_ALT_RIGHT + | KEYCODE_SHIFT_LEFT + | KEYCODE_SHIFT_RIGHT + | KEYCODE_CTRL_LEFT + | KEYCODE_CTRL_RIGHT + | KEYCODE_META_LEFT + | KEYCODE_META_RIGHT + ) +} + +fn get_ephemeral_modifier_key_mask(keycode: i32) -> u32 { + match keycode { + KEYCODE_ALT_LEFT => META_ALT_LEFT_ON | META_ALT_ON, + KEYCODE_ALT_RIGHT => META_ALT_RIGHT_ON | META_ALT_ON, + KEYCODE_SHIFT_LEFT => META_SHIFT_LEFT_ON | META_SHIFT_ON, + KEYCODE_SHIFT_RIGHT => META_SHIFT_RIGHT_ON | META_SHIFT_ON, + KEYCODE_CTRL_LEFT => META_CTRL_LEFT_ON | META_CTRL_ON, + KEYCODE_CTRL_RIGHT => META_CTRL_RIGHT_ON | META_CTRL_ON, + KEYCODE_META_LEFT => META_META_LEFT_ON | META_META_ON, + KEYCODE_META_RIGHT => META_META_RIGHT_ON | META_META_ON, + _ => 0, + } +} + +/// Modifier mask including both left and right versions of a modifier key. +fn get_symmetrical_modifier_key_mask(keycode: i32) -> u32 { + match keycode { + KEYCODE_ALT_LEFT | KEYCODE_ALT_RIGHT => META_ALT_LEFT_ON | META_ALT_RIGHT_ON | META_ALT_ON, + KEYCODE_SHIFT_LEFT | KEYCODE_SHIFT_RIGHT => { + META_SHIFT_LEFT_ON | META_SHIFT_RIGHT_ON | META_SHIFT_ON + } + KEYCODE_CTRL_LEFT | KEYCODE_CTRL_RIGHT => { + META_CTRL_LEFT_ON | META_CTRL_RIGHT_ON | META_CTRL_ON + } + KEYCODE_META_LEFT | KEYCODE_META_RIGHT => { + META_META_LEFT_ON | META_META_RIGHT_ON | META_META_ON + } + _ => 0, + } +} + +fn clear_ephemeral_modifier_state(modifier_state: u32) -> u32 { + modifier_state + & !(META_ALT_LEFT_ON + | META_ALT_RIGHT_ON + | META_ALT_ON + | META_SHIFT_LEFT_ON + | META_SHIFT_RIGHT_ON + | META_SHIFT_ON + | META_CTRL_LEFT_ON + | META_CTRL_RIGHT_ON + | META_CTRL_ON + | META_META_LEFT_ON + | META_META_RIGHT_ON + | META_META_ON) +} + +#[cfg(test)] +mod tests { + use crate::input_filter::{ + test_callbacks::TestCallbacks, test_filter::TestFilter, Filter, ModifierStateListener, + }; + use crate::sticky_keys_filter::{ + StickyKeysFilter, KEYCODE_ALT_LEFT, KEYCODE_ALT_RIGHT, KEYCODE_CAPS_LOCK, + KEYCODE_CTRL_LEFT, KEYCODE_CTRL_RIGHT, KEYCODE_FUNCTION, KEYCODE_META_LEFT, + KEYCODE_META_RIGHT, KEYCODE_NUM_LOCK, KEYCODE_SCROLL_LOCK, KEYCODE_SHIFT_LEFT, + KEYCODE_SHIFT_RIGHT, KEYCODE_SYM, META_ALT_LEFT_ON, META_ALT_ON, META_ALT_RIGHT_ON, + META_CTRL_LEFT_ON, META_CTRL_ON, META_CTRL_RIGHT_ON, META_META_LEFT_ON, META_META_ON, + META_META_RIGHT_ON, META_SHIFT_LEFT_ON, META_SHIFT_ON, META_SHIFT_RIGHT_ON, + }; + use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source; + use binder::Strong; + use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ + DeviceInfo::DeviceInfo, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, + KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, + }; + use std::sync::{Arc, RwLock}; + + static DEVICE_ID: i32 = 1; + static KEY_A: i32 = 29; + static BASE_KEY_DOWN: KeyEvent = KeyEvent { + id: 1, + deviceId: DEVICE_ID, + downTime: 0, + readTime: 0, + eventTime: 0, + source: Source::KEYBOARD, + displayId: 0, + policyFlags: 0, + action: KeyEventAction::DOWN, + flags: 0, + keyCode: 0, + scanCode: 0, + metaState: 0, + }; + + static BASE_KEY_UP: KeyEvent = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_DOWN }; + + #[test] + fn test_notify_key_consumes_ephemeral_modifier_keys() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + let key_codes = &[ + KEYCODE_ALT_LEFT, + KEYCODE_ALT_RIGHT, + KEYCODE_CTRL_LEFT, + KEYCODE_CTRL_RIGHT, + KEYCODE_SHIFT_LEFT, + KEYCODE_SHIFT_RIGHT, + KEYCODE_META_LEFT, + KEYCODE_META_RIGHT, + ]; + for key_code in key_codes.iter() { + sticky_keys_filter.notify_key(&KeyEvent { keyCode: *key_code, ..BASE_KEY_DOWN }); + assert!(test_filter.last_event().is_none()); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: *key_code, ..BASE_KEY_UP }); + assert!(test_filter.last_event().is_none()); + } + } + + #[test] + fn test_notify_key_passes_non_ephemeral_modifier_keys() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + let key_codes = &[ + KEYCODE_CAPS_LOCK, + KEYCODE_NUM_LOCK, + KEYCODE_SCROLL_LOCK, + KEYCODE_FUNCTION, + KEYCODE_SYM, + ]; + for key_code in key_codes.iter() { + let event = KeyEvent { keyCode: *key_code, ..BASE_KEY_DOWN }; + sticky_keys_filter.notify_key(&event); + assert_eq!(test_filter.last_event().unwrap(), event); + let event = KeyEvent { keyCode: *key_code, ..BASE_KEY_UP }; + sticky_keys_filter.notify_key(&event); + assert_eq!(test_filter.last_event().unwrap(), event); + } + } + + #[test] + fn test_notify_key_passes_non_modifier_keys() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + let event = KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN }; + sticky_keys_filter.notify_key(&event); + assert_eq!(test_filter.last_event().unwrap(), event); + + let event = KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP }; + sticky_keys_filter.notify_key(&event); + assert_eq!(test_filter.last_event().unwrap(), event); + } + + #[test] + fn test_modifier_state_updated_on_modifier_key_press() { + let mut test_filter = TestFilter::new(); + let mut test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + let test_states = &[ + (KEYCODE_ALT_LEFT, META_ALT_ON | META_ALT_LEFT_ON), + (KEYCODE_ALT_RIGHT, META_ALT_ON | META_ALT_RIGHT_ON), + (KEYCODE_CTRL_LEFT, META_CTRL_ON | META_CTRL_LEFT_ON), + (KEYCODE_CTRL_RIGHT, META_CTRL_ON | META_CTRL_RIGHT_ON), + (KEYCODE_SHIFT_LEFT, META_SHIFT_ON | META_SHIFT_LEFT_ON), + (KEYCODE_SHIFT_RIGHT, META_SHIFT_ON | META_SHIFT_RIGHT_ON), + (KEYCODE_META_LEFT, META_META_ON | META_META_LEFT_ON), + (KEYCODE_META_RIGHT, META_META_ON | META_META_RIGHT_ON), + ]; + for test_state in test_states.iter() { + test_filter.clear(); + test_callbacks.clear(); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN }); + assert_eq!(test_callbacks.get_last_modifier_state(), 0); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP }); + assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + + // Re-send keys to lock it + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN }); + assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP }); + assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), test_state.1); + + // Re-send keys to clear + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN }); + assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), test_state.1); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP }); + assert_eq!(test_callbacks.get_last_modifier_state(), 0); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + } + } + + #[test] + fn test_modifier_state_cleared_on_non_modifier_key_press() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP }); + + assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP }); + + assert_eq!(test_callbacks.get_last_modifier_state(), 0); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + } + + #[test] + fn test_locked_modifier_state_not_cleared_on_non_modifier_key_press() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP }); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP }); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_SHIFT_LEFT, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_SHIFT_LEFT, ..BASE_KEY_UP }); + + assert_eq!( + test_callbacks.get_last_modifier_state(), + META_SHIFT_LEFT_ON | META_SHIFT_ON | META_CTRL_LEFT_ON | META_CTRL_ON + ); + assert_eq!( + test_callbacks.get_last_locked_modifier_state(), + META_CTRL_LEFT_ON | META_CTRL_ON + ); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP }); + + assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON); + assert_eq!( + test_callbacks.get_last_locked_modifier_state(), + META_CTRL_LEFT_ON | META_CTRL_ON + ); + } + + #[test] + fn test_key_events_have_sticky_modifier_state() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN }); + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP }); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN }); + assert_eq!( + test_filter.last_event().unwrap().metaState as u32, + META_CTRL_LEFT_ON | META_CTRL_ON + ); + + sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP }); + assert_eq!( + test_filter.last_event().unwrap().metaState as u32, + META_CTRL_LEFT_ON | META_CTRL_ON + ); + } + + #[test] + fn test_modifier_state_not_cleared_until_all_devices_removed() { + let test_filter = TestFilter::new(); + let test_callbacks = TestCallbacks::new(); + let mut sticky_keys_filter = setup_filter( + Box::new(test_filter.clone()), + Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))), + ); + sticky_keys_filter.notify_key(&KeyEvent { + deviceId: 1, + keyCode: KEYCODE_CTRL_LEFT, + ..BASE_KEY_DOWN + }); + sticky_keys_filter.notify_key(&KeyEvent { + deviceId: 1, + keyCode: KEYCODE_CTRL_LEFT, + ..BASE_KEY_UP + }); + + sticky_keys_filter.notify_key(&KeyEvent { + deviceId: 2, + keyCode: KEYCODE_CTRL_LEFT, + ..BASE_KEY_DOWN + }); + sticky_keys_filter.notify_key(&KeyEvent { + deviceId: 2, + keyCode: KEYCODE_CTRL_LEFT, + ..BASE_KEY_UP + }); + + sticky_keys_filter.notify_devices_changed(&[DeviceInfo { deviceId: 2, external: true }]); + assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON); + assert_eq!( + test_callbacks.get_last_locked_modifier_state(), + META_CTRL_LEFT_ON | META_CTRL_ON + ); + + sticky_keys_filter.notify_devices_changed(&[]); + assert_eq!(test_callbacks.get_last_modifier_state(), 0); + assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0); + } + + fn setup_filter( + next: Box<dyn Filter + Send + Sync>, + callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>, + ) -> StickyKeysFilter { + StickyKeysFilter::new(next, ModifierStateListener::new(callbacks)) + } +} diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index 490cf2f55a..193b84d313 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -34,7 +34,9 @@ namespace { // Helpers to std::visit with lambdas. template <typename... V> -struct Visitor : V... {}; +struct Visitor : V... { + using V::operator()...; +}; template <typename... V> Visitor(V...) -> Visitor<V...>; diff --git a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp index 0bd8fb3de3..dc5a2130e7 100644 --- a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp @@ -65,6 +65,27 @@ private: std::map<int32_t /*displayId*/, InputVerifier> mVerifiers; }; +void scrambleWindow(FuzzedDataProvider& fdp, FakeWindowHandle& window) { + const int32_t left = fdp.ConsumeIntegralInRange<int32_t>(0, 100); + const int32_t top = fdp.ConsumeIntegralInRange<int32_t>(0, 100); + const int32_t width = fdp.ConsumeIntegralInRange<int32_t>(0, 100); + const int32_t height = fdp.ConsumeIntegralInRange<int32_t>(0, 100); + + window.setFrame(Rect(left, top, left + width, top + height)); + window.setSlippery(fdp.ConsumeBool()); + window.setDupTouchToWallpaper(fdp.ConsumeBool()); + window.setIsWallpaper(fdp.ConsumeBool()); + window.setVisible(fdp.ConsumeBool()); + window.setPreventSplitting(fdp.ConsumeBool()); + const bool isTrustedOverlay = fdp.ConsumeBool(); + window.setTrustedOverlay(isTrustedOverlay); + if (isTrustedOverlay) { + window.setSpy(fdp.ConsumeBool()); + } else { + window.setSpy(false); + } +} + } // namespace sp<FakeWindowHandle> generateFuzzedWindow(FuzzedDataProvider& fdp, InputDispatcher& dispatcher, @@ -73,17 +94,9 @@ sp<FakeWindowHandle> generateFuzzedWindow(FuzzedDataProvider& fdp, InputDispatch std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); std::string windowName = android::base::StringPrintf("Win") + std::to_string(windowNumber++); sp<FakeWindowHandle> window = - sp<FakeWindowHandle>::make(application, dispatcher, "Fake", displayId); + sp<FakeWindowHandle>::make(application, dispatcher, windowName, displayId); - const int32_t left = fdp.ConsumeIntegralInRange<int32_t>(0, 100); - const int32_t top = fdp.ConsumeIntegralInRange<int32_t>(0, 100); - const int32_t width = fdp.ConsumeIntegralInRange<int32_t>(0, 100); - const int32_t height = fdp.ConsumeIntegralInRange<int32_t>(0, 100); - - window->setFrame(Rect(left, top, left + width, top + height)); - window->setSlippery(fdp.ConsumeBool()); - window->setDupTouchToWallpaper(fdp.ConsumeBool()); - window->setTrustedOverlay(fdp.ConsumeBool()); + scrambleWindow(fdp, *window); return window; } @@ -113,7 +126,14 @@ void randomizeWindows( windowsPerDisplay.erase(displayId); } }, - // Could also clone a window, change flags, reposition, etc... + // Change flags or move some of the existing windows + [&]() -> void { + for (auto& window : windows) { + if (fdp.ConsumeBool()) { + scrambleWindow(fdp, *window); + } + } + }, })(); } diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index 11c56a8c56..0dd4dd6525 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -7,6 +7,18 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +aconfig_declarations { + name: "sensorservice_flags", + package: "com.android.frameworks.sensorservice.flags", + srcs: ["senserservice_flags.aconfig"], +} + +cc_aconfig_library { + name: "sensorservice_flags_c_lib", + aconfig_declarations: "dynamic_sensors_flags", + host_supported: true, +} + cc_library { name: "libsensorservice", diff --git a/services/sensorservice/senserservice_flags.aconfig b/services/sensorservice/senserservice_flags.aconfig new file mode 100644 index 0000000000..6b74f6feb2 --- /dev/null +++ b/services/sensorservice/senserservice_flags.aconfig @@ -0,0 +1,8 @@ +package: "com.android.frameworks.sensorservice.flags" + +flag { + name: "dynamic_sensor_hal_reconnect_handling" + namespace: "sensors" + description: "This flag controls if the dynamic sensor data will be clean up after HAL is disconnected." + bug: "307782607" +}
\ No newline at end of file diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 8b736be5e9..9e35717c95 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -148,8 +148,7 @@ public: MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&, getOverlaySupport, (), (const, override)); MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool)); - MOCK_METHOD(status_t, notifyExpectedPresentIfRequired, - (PhysicalDisplayId, Period, TimePoint, Fps, std::optional<Period>)); + MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps)); }; } // namespace mock diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index c25f9ddd12..64a8ae7fcd 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -271,7 +271,10 @@ AidlComposer::AidlComposer(const std::string& serviceName) { } } } - + if (getLayerLifecycleBatchCommand()) { + mEnableLayerCommandBatchingFlag = + FlagManager::getInstance().enable_layer_command_batching(); + } ALOGI("Loaded AIDL composer3 HAL service"); } @@ -288,7 +291,7 @@ bool AidlComposer::isSupported(OptionalFeature feature) const { } } -bool AidlComposer::getDisplayConfigurationsSupported() const { +bool AidlComposer::isVrrSupported() const { return mComposerInterfaceVersion >= 3 && FlagManager::getInstance().vrr_config(); } @@ -407,25 +410,58 @@ Error AidlComposer::acceptDisplayChanges(Display display) { Error AidlComposer::createLayer(Display display, Layer* outLayer) { int64_t layer; - const auto status = mAidlComposerClient->createLayer(translate<int64_t>(display), - kMaxLayerBufferCount, &layer); - if (!status.isOk()) { - ALOGE("createLayer failed %s", status.getDescription().c_str()); - return static_cast<Error>(status.getServiceSpecificError()); + Error error = Error::NONE; + if (!mEnableLayerCommandBatchingFlag) { + const auto status = mAidlComposerClient->createLayer(translate<int64_t>(display), + kMaxLayerBufferCount, &layer); + if (!status.isOk()) { + ALOGE("createLayer failed %s", status.getDescription().c_str()); + return static_cast<Error>(status.getServiceSpecificError()); + } + } else { + // generate a unique layerID. map in AidlComposer with <SF_layerID, HWC_layerID> + // Add this as a new displayCommand in execute command. + // return the SF generated layerID instead of calling HWC + layer = mLayerID++; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerLifecycleBatchCommandType(translate<int64_t>(display), + translate<int64_t>(layer), + LayerLifecycleBatchCommandType::CREATE); + writer->get().setNewBufferSlotCount(translate<int64_t>(display), + translate<int64_t>(layer), kMaxLayerBufferCount); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); } - *outLayer = translate<Layer>(layer); - return Error::NONE; + return error; } Error AidlComposer::destroyLayer(Display display, Layer layer) { - const auto status = mAidlComposerClient->destroyLayer(translate<int64_t>(display), - translate<int64_t>(layer)); - if (!status.isOk()) { - ALOGE("destroyLayer failed %s", status.getDescription().c_str()); - return static_cast<Error>(status.getServiceSpecificError()); + Error error = Error::NONE; + if (!mEnableLayerCommandBatchingFlag) { + const auto status = mAidlComposerClient->destroyLayer(translate<int64_t>(display), + translate<int64_t>(layer)); + if (!status.isOk()) { + ALOGE("destroyLayer failed %s", status.getDescription().c_str()); + return static_cast<Error>(status.getServiceSpecificError()); + } + } else { + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get() + .setLayerLifecycleBatchCommandType(translate<int64_t>(display), + translate<int64_t>(layer), + LayerLifecycleBatchCommandType::DESTROY); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); } - return Error::NONE; + + return error; } Error AidlComposer::getActiveConfig(Display display, Config* outConfig) { @@ -591,6 +627,13 @@ Error AidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTyp return Error::NONE; } +bool AidlComposer::getLayerLifecycleBatchCommand() { + std::vector<Capability> capabilities = getCapabilities(); + bool hasCapability = std::find(capabilities.begin(), capabilities.end(), + Capability::LAYER_LIFECYCLE_BATCH_COMMAND) != capabilities.end(); + return hasCapability; +} + Error AidlComposer::getOverlaySupport(AidlOverlayProperties* outProperties) { const auto status = mAidlComposerClient->getOverlaySupport(outProperties); if (!status.isOk()) { diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index 51ac1f5e6a..ea0e53a202 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -66,7 +66,7 @@ public: ~AidlComposer() override; bool isSupported(OptionalFeature) const; - bool getDisplayConfigurationsSupported() const; + bool isVrrSupported() const; std::vector<aidl::android::hardware::graphics::composer3::Capability> getCapabilities() override; @@ -262,7 +262,7 @@ private: void removeDisplay(Display) EXCLUDES(mMutex); void addReader(Display) REQUIRES(mMutex); void removeReader(Display) REQUIRES(mMutex); - + bool getLayerLifecycleBatchCommand(); bool hasMultiThreadedPresentSupport(Display); // 64KiB minus a small space for metadata such as read/write pointers @@ -293,6 +293,8 @@ private: ftl::SharedMutex mMutex; int32_t mComposerInterfaceVersion = 1; + bool mEnableLayerCommandBatchingFlag = false; + std::atomic<int64_t> mLayerID = 1; // Buffer slots for layers are cleared by setting the slot buffer to this buffer. sp<GraphicBuffer> mClearSlotBuffer; diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 1a24222af3..bc067a0e5d 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -105,7 +105,7 @@ public: }; virtual bool isSupported(OptionalFeature) const = 0; - virtual bool getDisplayConfigurationsSupported() const = 0; + virtual bool isVrrSupported() const = 0; virtual std::vector<aidl::android::hardware::graphics::composer3::Capability> getCapabilities() = 0; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 6b67865bdb..3ffd8ea316 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -15,6 +15,7 @@ */ // TODO(b/129481165): remove the #pragma below and fix conversion issues +#include <chrono> #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" @@ -78,59 +79,6 @@ using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::DisplayCapability; namespace hal = android::hardware::graphics::composer::hal; -namespace { -bool isFrameIntervalOnCadence(android::TimePoint expectedPresentTime, - android::TimePoint lastExpectedPresentTimestamp, - android::Fps lastFrameInterval, android::Period timeout, - android::Duration threshold) { - if (lastFrameInterval.getPeriodNsecs() == 0) { - return false; - } - - const auto expectedPresentTimeDeltaNs = - expectedPresentTime.ns() - lastExpectedPresentTimestamp.ns(); - - if (expectedPresentTimeDeltaNs > timeout.ns()) { - return false; - } - - const auto expectedPresentPeriods = static_cast<nsecs_t>( - std::round(static_cast<float>(expectedPresentTimeDeltaNs) / - static_cast<float>(lastFrameInterval.getPeriodNsecs()))); - const auto calculatedPeriodsOutNs = lastFrameInterval.getPeriodNsecs() * expectedPresentPeriods; - const auto calculatedExpectedPresentTimeNs = - lastExpectedPresentTimestamp.ns() + calculatedPeriodsOutNs; - const auto presentTimeDelta = - std::abs(expectedPresentTime.ns() - calculatedExpectedPresentTimeNs); - return presentTimeDelta < threshold.ns(); -} - -bool isExpectedPresentWithinTimeout(android::TimePoint expectedPresentTime, - android::TimePoint lastExpectedPresentTimestamp, - std::optional<android::Period> timeoutOpt, - android::Duration threshold) { - if (!timeoutOpt) { - // Always within timeout if timeoutOpt is absent and don't send hint - // for the timeout - return true; - } - - if (timeoutOpt->ns() == 0) { - // Always outside timeout if timeoutOpt is 0 and always send - // the hint for the timeout. - return false; - } - - if (expectedPresentTime.ns() < lastExpectedPresentTimestamp.ns() + timeoutOpt->ns()) { - return true; - } - - // Check if within the threshold as it can be just outside the timeout - return std::abs(expectedPresentTime.ns() - - (lastExpectedPresentTimestamp.ns() + timeoutOpt->ns())) < threshold.ns(); -} -} // namespace - namespace android { HWComposer::~HWComposer() = default; @@ -321,7 +269,7 @@ std::vector<HWComposer::HWCDisplayMode> HWComposer::getModes(PhysicalDisplayId d const auto hwcDisplayId = mDisplayData.at(displayId).hwcDisplay->getId(); - if (mComposer->getDisplayConfigurationsSupported()) { + if (mComposer->isVrrSupported()) { return getModesFromDisplayConfigurations(hwcDisplayId, maxFrameIntervalNs); } @@ -538,13 +486,6 @@ status_t HWComposer::getDeviceCompositionChanges( }(); displayData.validateWasSkipped = false; - { - std::scoped_lock lock{displayData.expectedPresentLock}; - if (expectedPresentTime > displayData.lastExpectedPresentTimestamp.ns()) { - displayData.lastExpectedPresentTimestamp = TimePoint::fromNs(expectedPresentTime); - } - } - ATRACE_FORMAT("NextFrameInterval %d_Hz", frameInterval.getIntValue()); if (canSkipValidate) { sp<Fence> outPresentFence = Fence::NO_FENCE; @@ -939,55 +880,15 @@ status_t HWComposer::setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId return NO_ERROR; } -status_t HWComposer::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId, - Period vsyncPeriod, - TimePoint expectedPresentTime, - Fps frameInterval, - std::optional<Period> timeoutOpt) { +status_t HWComposer::notifyExpectedPresent(PhysicalDisplayId displayId, + TimePoint expectedPresentTime, Fps frameInterval) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); - auto& displayData = mDisplayData[displayId]; - if (!displayData.hwcDisplay) { - // Display setup has not completed yet - return BAD_INDEX; - } - { - std::scoped_lock lock{displayData.expectedPresentLock}; - const auto lastExpectedPresentTimestamp = displayData.lastExpectedPresentTimestamp; - const auto lastFrameInterval = displayData.lastFrameInterval; - displayData.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 bool frameIntervalIsOnCadence = - isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp, - lastFrameInterval, - Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0 - ? timeoutOpt->ns() - : kOneSecondNs), - threshold); - - const bool expectedPresentWithinTimeout = - isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp, - timeoutOpt, threshold); - - using fps_approx_ops::operator!=; - if (frameIntervalIsOnCadence && frameInterval != lastFrameInterval) { - displayData.lastExpectedPresentTimestamp = expectedPresentTime; - } - - if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) { - return NO_ERROR; - } - - displayData.lastExpectedPresentTimestamp = expectedPresentTime; - } - ATRACE_FORMAT("%s ExpectedPresentTime %" PRId64 " frameIntervalNs %d", __func__, - expectedPresentTime, frameInterval.getPeriodNsecs()); - const auto error = mComposer->notifyExpectedPresent(displayData.hwcDisplay->getId(), + ATRACE_FORMAT("%s ExpectedPresentTime in %.2fms frameInterval %.2fms", __func__, + ticks<std::milli, float>(expectedPresentTime - TimePoint::now()), + ticks<std::milli, float>(Duration::fromNs(frameInterval.getPeriodNsecs()))); + const auto error = mComposer->notifyExpectedPresent(mDisplayData[displayId].hwcDisplay->getId(), expectedPresentTime.ns(), frameInterval.getPeriodNsecs()); - if (error != hal::Error::NONE) { ALOGE("Error in notifyExpectedPresent call %s", to_string(error).c_str()); return INVALID_OPERATION; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index af62731a41..4ca528a6e7 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -303,10 +303,8 @@ public: aidl::android::hardware::graphics::common::HdrConversionStrategy, aidl::android::hardware::graphics::common::Hdr*) = 0; virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0; - virtual status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod, - TimePoint expectedPresentTime, - Fps frameInterval, - std::optional<Period> timeoutOpt) = 0; + virtual status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime, + Fps frameInterval) = 0; }; static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs, @@ -466,9 +464,8 @@ public: aidl::android::hardware::graphics::common::HdrConversionStrategy, aidl::android::hardware::graphics::common::Hdr*) override; status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override; - status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod, - TimePoint expectedPresentTime, Fps frameInterval, - std::optional<Period> timeoutOpt) override; + status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime, + Fps frameInterval) override; // for debugging ---------------------------------------------------------- void dump(std::string& out) const override; @@ -494,7 +491,6 @@ public: private: // For unit tests friend TestableSurfaceFlinger; - friend HWComposerTest; struct DisplayData { std::unique_ptr<HWC2::Display> hwcDisplay; @@ -502,11 +498,6 @@ private: sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires nsecs_t lastPresentTimestamp = 0; - std::mutex expectedPresentLock; - TimePoint lastExpectedPresentTimestamp GUARDED_BY(expectedPresentLock) = - TimePoint::fromNs(0); - Fps lastFrameInterval GUARDED_BY(expectedPresentLock) = Fps::fromValue(0); - std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences; bool validateWasSkipped; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index 5f1d5f8164..c4ff9cc6be 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -276,8 +276,8 @@ bool HidlComposer::isSupported(OptionalFeature feature) const { } } -bool HidlComposer::getDisplayConfigurationsSupported() const { - // getDisplayConfigurations is not supported on the HIDL composer. +bool HidlComposer::isVrrSupported() const { + // VRR is not supported on the HIDL composer. return false; }; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h index c768d2758d..d78bfb7c6b 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -167,7 +167,7 @@ public: ~HidlComposer() override; bool isSupported(OptionalFeature) const; - bool getDisplayConfigurationsSupported() const; + bool isVrrSupported() const; std::vector<aidl::android::hardware::graphics::composer3::Capability> getCapabilities() override; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 6a85788623..ce59a04808 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -630,6 +630,7 @@ bool Scheduler::addResyncSample(PhysicalDisplayId id, nsecs_t timestamp, } void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> fence) { + ATRACE_NAME(ftl::Concat(__func__, ' ', id.value).c_str()); const auto scheduleOpt = (ftl::FakeGuard(mDisplayLock), mDisplays.get(id)).and_then([](const Display& display) { return display.powerMode == hal::PowerMode::OFF @@ -640,7 +641,8 @@ void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> if (!scheduleOpt) return; const auto& schedule = scheduleOpt->get(); - if (const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence))) { + const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence)); + if (needMoreSignals) { schedule->enableHardwareVsync(); } else { constexpr bool kDisallow = false; diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index 7379a4605d..0d79a390a7 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -88,6 +88,7 @@ bool VSyncPredictor::validate(nsecs_t timestamp) const { (timestamp - aValidTimestamp) % idealPeriod() * kMaxPercent / idealPeriod(); if (percent >= kOutlierTolerancePercent && percent <= (kMaxPercent - kOutlierTolerancePercent)) { + ATRACE_FORMAT_INSTANT("timestamp is not aligned with model"); return false; } @@ -98,6 +99,7 @@ bool VSyncPredictor::validate(nsecs_t timestamp) const { const auto distancePercent = std::abs(*iter - timestamp) * kMaxPercent / idealPeriod(); if (distancePercent < kOutlierTolerancePercent) { // duplicate timestamp + ATRACE_FORMAT_INSTANT("duplicate timestamp"); return false; } return true; @@ -126,6 +128,8 @@ Period VSyncPredictor::minFramePeriodLocked() const { } bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { + ATRACE_CALL(); + std::lock_guard lock(mMutex); if (!validate(timestamp)) { @@ -144,6 +148,8 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { } else { mKnownTimestamp = timestamp; } + ATRACE_FORMAT_INSTANT("timestamp rejected. mKnownTimestamp was %.2fms ago", + (systemTime() - *mKnownTimestamp) / 1e6f); return false; } @@ -515,6 +521,8 @@ VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const { } void VSyncPredictor::clearTimestamps() { + ATRACE_CALL(); + if (!mTimestamps.empty()) { auto const maxRb = *std::max_element(mTimestamps.begin(), mTimestamps.end()); if (mKnownTimestamp) { diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp index 24737e4fb2..186a2d6740 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -53,6 +53,8 @@ VSyncReactor::VSyncReactor(PhysicalDisplayId id, std::unique_ptr<Clock> clock, VSyncReactor::~VSyncReactor() = default; bool VSyncReactor::addPresentFence(std::shared_ptr<FenceTime> fence) { + ATRACE_CALL(); + if (!fence) { return false; } @@ -64,6 +66,8 @@ bool VSyncReactor::addPresentFence(std::shared_ptr<FenceTime> fence) { std::lock_guard lock(mMutex); if (mExternalIgnoreFences || mInternalIgnoreFences) { + ATRACE_FORMAT_INSTANT("mExternalIgnoreFences=%d mInternalIgnoreFences=%d", + mExternalIgnoreFences, mInternalIgnoreFences); return true; } diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp index ff1c9e909a..db6a1871a7 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp @@ -19,6 +19,7 @@ #include <common/FlagManager.h> #include <ftl/fake_guard.h> +#include <gui/TraceUtils.h> #include <scheduler/Fps.h> #include <scheduler/Timer.h> @@ -179,6 +180,7 @@ void VsyncSchedule::enableHardwareVsync() { } void VsyncSchedule::enableHardwareVsyncLocked() { + ATRACE_CALL(); if (mHwVsyncState == HwVsyncState::Disabled) { getTracker().resetModel(); mRequestHardwareVsync(mId, true); @@ -187,6 +189,7 @@ void VsyncSchedule::enableHardwareVsyncLocked() { } void VsyncSchedule::disableHardwareVsync(bool disallow) { + ATRACE_CALL(); std::lock_guard<std::mutex> lock(mHwVsyncLock); switch (mHwVsyncState) { case HwVsyncState::Enabled: diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 9b417c6c96..2769e5dfc2 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -318,6 +318,53 @@ bool fileNewerThan(const std::string& path, std::chrono::minutes duration) { return duration > (Clock::now() - updateTime); } +bool isFrameIntervalOnCadence(TimePoint expectedPresentTime, TimePoint lastExpectedPresentTimestamp, + Fps lastFrameInterval, Period timeout, Duration threshold) { + if (lastFrameInterval.getPeriodNsecs() == 0) { + return false; + } + + const auto expectedPresentTimeDeltaNs = + expectedPresentTime.ns() - lastExpectedPresentTimestamp.ns(); + + if (expectedPresentTimeDeltaNs > timeout.ns()) { + return false; + } + + const auto expectedPresentPeriods = static_cast<nsecs_t>( + std::round(static_cast<float>(expectedPresentTimeDeltaNs) / + static_cast<float>(lastFrameInterval.getPeriodNsecs()))); + const auto calculatedPeriodsOutNs = lastFrameInterval.getPeriodNsecs() * expectedPresentPeriods; + const auto calculatedExpectedPresentTimeNs = + lastExpectedPresentTimestamp.ns() + calculatedPeriodsOutNs; + const auto presentTimeDelta = + std::abs(expectedPresentTime.ns() - calculatedExpectedPresentTimeNs); + return presentTimeDelta < threshold.ns(); +} + +bool isExpectedPresentWithinTimeout(TimePoint expectedPresentTime, + TimePoint lastExpectedPresentTimestamp, + std::optional<Period> timeoutOpt, Duration threshold) { + if (!timeoutOpt) { + // Always within timeout if timeoutOpt is absent and don't send hint + // for the timeout + return true; + } + + if (timeoutOpt->ns() == 0) { + // Always outside timeout if timeoutOpt is 0 and always send + // the hint for the timeout. + return false; + } + + if (expectedPresentTime.ns() < lastExpectedPresentTimestamp.ns() + timeoutOpt->ns()) { + return true; + } + + // Check if within the threshold as it can be just outside the timeout + return std::abs(expectedPresentTime.ns() - + (lastExpectedPresentTimestamp.ns() + timeoutOpt->ns())) < threshold.ns(); +} } // namespace anonymous // --------------------------------------------------------------------------- @@ -716,7 +763,7 @@ void SurfaceFlinger::bootFinished() { sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger"))); - static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) { + static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) { if (input == nullptr) { ALOGE("Failed to link to input service"); } else { @@ -1245,7 +1292,7 @@ status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToke } const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t { const auto displayOpt = FTL_FAKE_GUARD(mStateLock, ftl::find_if(mPhysicalDisplays, @@ -1432,7 +1479,7 @@ void SurfaceFlinger::initiateDisplayModeChanges() { void SurfaceFlinger::disableExpensiveRendering() { const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { ATRACE_NAME(whence); if (mPowerAdvisor->isUsingExpensiveRendering()) { for (const auto& [_, display] : mDisplays) { @@ -1474,7 +1521,7 @@ status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ui: } const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t { const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken)) .transform(&ftl::to_mapped_ref<PhysicalDisplays>) @@ -1555,7 +1602,7 @@ status_t SurfaceFlinger::getOverlaySupport(gui::OverlayProperties* outProperties status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& displayToken, DisplayModeId modeId) { const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t { const auto snapshotOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken)) .transform(&ftl::to_mapped_ref<PhysicalDisplays>) @@ -1583,7 +1630,7 @@ status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& dis status_t SurfaceFlinger::clearBootDisplayMode(const sp<IBinder>& displayToken) { const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t { if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { return getHwComposer().clearBootDisplayMode(*displayId); } else { @@ -1622,7 +1669,7 @@ status_t SurfaceFlinger::setHdrConversionStrategy( ALOGE("hdrOutputConversion is not supported by this device."); return INVALID_OPERATION; } - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) mutable -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) mutable -> status_t { using AidlHdrConversionStrategy = aidl::android::hardware::graphics::common::HdrConversionStrategy; using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag; @@ -1682,7 +1729,7 @@ status_t SurfaceFlinger::getHdrOutputConversionSupport(bool* outSupport) const { void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) { const char* const whence = __func__; - static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { + static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { getHwComposer().setAutoLowLatencyMode(*displayId, on); } else { @@ -1693,7 +1740,7 @@ void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) { const char* const whence = __func__; - static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { + static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { const auto type = on ? hal::ContentType::GAME : hal::ContentType::NONE; getHwComposer().setContentType(*displayId, type); @@ -1747,7 +1794,7 @@ status_t SurfaceFlinger::setDisplayContentSamplingEnabled(const sp<IBinder>& dis bool enable, uint8_t componentMask, uint64_t maxFrames) { const char* const whence = __func__; - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t { if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable, componentMask, maxFrames); @@ -1800,7 +1847,7 @@ status_t SurfaceFlinger::isWideColorDisplay(const sp<IBinder>& displayToken, status_t SurfaceFlinger::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) { outLayers->clear(); - auto future = mScheduler->schedule([=] { + auto future = mScheduler->schedule([=, this] { const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); mDrawingState.traverseInZOrder([&](Layer* layer) { outLayers->push_back(layer->getLayerDebugInfo(display.get())); @@ -1906,7 +1953,7 @@ status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, } const char* const whence = __func__; - return ftl::Future(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { + return ftl::Future(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { if (const auto display = getDisplayDeviceLocked(displayToken)) { const bool supportsDisplayBrightnessCommand = getHwComposer().getComposer()->isSupported( @@ -2160,19 +2207,16 @@ void SurfaceFlinger::onComposerHalVsyncIdle(hal::HWDisplayId) { void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData& data) { ATRACE_CALL(); if (const auto displayId = getHwComposer().toPhysicalDisplayId(data.display); displayId) { - const Fps fps = Fps::fromPeriodNsecs(data.vsyncPeriodNanos); - ATRACE_FORMAT("%s Fps %d", __func__, fps.getIntValue()); - static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { - { - { - const auto display = getDisplayDeviceLocked(*displayId); - FTL_FAKE_GUARD(kMainThreadContext, - display->updateRefreshRateOverlayRate(fps, - display->getActiveMode() - .fps, - /* setByHwc */ true)); - } - } + const char* const whence = __func__; + static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) { + const Fps fps = Fps::fromPeriodNsecs(getHwComposer().getComposer()->isVrrSupported() + ? data.refreshPeriodNanos + : data.vsyncPeriodNanos); + ATRACE_FORMAT("%s Fps %d", whence, fps.getIntValue()); + const auto display = getDisplayDeviceLocked(*displayId); + FTL_FAKE_GUARD(kMainThreadContext, + display->updateRefreshRateOverlayRate(fps, display->getActiveMode().fps, + /* setByHwc */ true)); })); } } @@ -2710,7 +2754,18 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime(); refreshArgs.expectedPresentTime = expectedPresentTime.ns(); 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(); @@ -4064,7 +4119,7 @@ void SurfaceFlinger::onChoreographerAttached() { void SurfaceFlinger::onVsyncGenerated(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr> modePtr, Fps renderRate) { const auto vsyncPeriod = modePtr->getVsyncRate().getPeriod(); - const auto timeout = [&]() -> std::optional<Period> { + const auto timeoutOpt = [&]() -> std::optional<Period> { const auto vrrConfig = modePtr->getVrrConfig(); if (!vrrConfig) return std::nullopt; @@ -4074,14 +4129,54 @@ void SurfaceFlinger::onVsyncGenerated(TimePoint expectedPresentTime, return Period::fromNs(notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs); }(); - const auto displayId = modePtr->getPhysicalDisplayId(); - const auto status = getHwComposer().notifyExpectedPresentIfRequired(displayId, vsyncPeriod, - expectedPresentTime, - renderRate, timeout); - if (status != NO_ERROR) { - ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, __func__, - displayId.value); + notifyExpectedPresentIfRequired(modePtr->getPhysicalDisplayId(), vsyncPeriod, + expectedPresentTime, renderRate, timeoutOpt); +} + +void SurfaceFlinger::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId, + Period vsyncPeriod, + 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) { + return; + } + data.lastExpectedPresentTimestamp = expectedPresentTime; } + + const char* const whence = __func__; + static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) { + const auto status = getHwComposer().notifyExpectedPresent(displayId, expectedPresentTime, + frameInterval); + if (status != NO_ERROR) { + ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, whence, + displayId.value); + } + })); } void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { @@ -4343,7 +4438,7 @@ status_t SurfaceFlinger::addClientLayer(LayerCreationArgs& args, const sp<IBinde if (mNumLayers >= MAX_LAYERS) { ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(), MAX_LAYERS); - static_cast<void>(mScheduler->schedule([=] { + static_cast<void>(mScheduler->schedule([=, this] { ALOGE("Dumping layer keeping > 20 children alive:"); bool leakingParentLayerFound = false; mDrawingState.traverse([&](Layer* layer) { @@ -5927,7 +6022,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: } void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) { - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD( + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD( kMainThreadContext) { const auto display = getDisplayDeviceLocked(displayToken); if (!display) { @@ -6376,7 +6471,7 @@ void SurfaceFlinger::dumpOffscreenLayersProto(perfetto::protos::LayersProto& lay } perfetto::protos::LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) { - return mScheduler->schedule([=] { return dumpDrawingStateProto(traceFlags); }).get(); + return mScheduler->schedule([=, this] { return dumpDrawingStateProto(traceFlags); }).get(); } void SurfaceFlinger::dumpOffscreenLayers(std::string& result) { @@ -7297,7 +7392,7 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { // Update the overlay on the main thread to avoid race conditions with // RefreshRateSelector::getActiveMode - static_cast<void>(mScheduler->schedule([=] { + static_cast<void>(mScheduler->schedule([=, this] { const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); if (!display) { ALOGW("%s: default display is null", __func__); @@ -7738,7 +7833,7 @@ void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, } bool childrenOnly = args.childrenOnly; - RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> { + RenderAreaFuture renderAreaFuture = ftl::defer([=, this]() -> std::unique_ptr<RenderArea> { ui::Transform layerTransform; Rect layerBufferSize; if (mLayerLifecycleManagerEnabled) { @@ -7877,7 +7972,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon( ATRACE_CALL(); auto future = mScheduler->schedule( - [=, renderAreaFuture = std::move(renderAreaFuture)]() FTL_FAKE_GUARD( + [=, this, renderAreaFuture = std::move(renderAreaFuture)]() FTL_FAKE_GUARD( kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> { ScreenCaptureResults captureResults; std::shared_ptr<RenderArea> renderArea = renderAreaFuture.get(); @@ -8302,7 +8397,7 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecs(const sp<IBinder>& displayTo return BAD_VALUE; } - auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t { + auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t { const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken)); if (!display) { ALOGE("Attempt to set desired display modes for invalid display token %p", diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 6a7b3220b3..6b4440193b 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -1490,6 +1490,19 @@ private: // Map of displayid to mirrorRoot ftl::SmallMap<int64_t, sp<SurfaceControl>, 3> mMirrorMapForDebug; + // NotifyExpectedPresentHint + 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{}; + Fps lastFrameInterval{}; + }; + std::unordered_map<PhysicalDisplayId, NotifyExpectedPresentData> mNotifyExpectedPresentMap; + + void notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod, + TimePoint expectedPresentTime, Fps frameInterval, + std::optional<Period> timeoutOpt); + void sfdo_enableRefreshRateOverlay(bool active); void sfdo_setDebugFlash(int delay); void sfdo_scheduleComposite(); diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index 9b77751d1c..cb1faee6b9 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -127,7 +127,7 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(display_protected); DUMP_READ_ONLY_FLAG(fp16_client_target); DUMP_READ_ONLY_FLAG(game_default_frame_rate); - + DUMP_READ_ONLY_FLAG(enable_layer_command_batching); #undef DUMP_READ_ONLY_FLAG #undef DUMP_SERVER_FLAG #undef DUMP_FLAG_INTERVAL @@ -201,6 +201,7 @@ FLAG_MANAGER_READ_ONLY_FLAG(enable_fro_dependent_features, "") 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, "") /// Trunk stable server flags /// FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "") diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index 3a509f6352..2e1d6aee4d 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -66,6 +66,7 @@ public: bool display_protected() const; bool fp16_client_target() const; bool game_default_frame_rate() const; + bool enable_layer_command_batching() const; protected: // overridden for unit tests diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig index 3f26448a54..1a28b81483 100644 --- a/services/surfaceflinger/surfaceflinger_flags.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags.aconfig @@ -32,6 +32,14 @@ flag { } flag { + name: "enable_layer_command_batching" + namespace: "core_graphics" + description: "This flag controls batching on createLayer/destroyLayer command with executeCommand." + bug: "290685621" + is_fixed_read_only: true +} + +flag { name: "dont_skip_on_early" namespace: "core_graphics" description: "This flag is guarding the behaviour where SurfaceFlinger is trying to opportunistically present a frame when the configuration change from late to early" diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index dea019431b..c75f90222a 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -106,6 +106,7 @@ cc_test { "SurfaceFlinger_HdrOutputControlTest.cpp", "SurfaceFlinger_HotplugTest.cpp", "SurfaceFlinger_InitializeDisplaysTest.cpp", + "SurfaceFlinger_NotifyExpectedPresentTest.cpp", "SurfaceFlinger_NotifyPowerBoostTest.cpp", "SurfaceFlinger_PowerHintTest.cpp", "SurfaceFlinger_SetDisplayStateTest.cpp", diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index d3ce4f2b90..6edecff6a6 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -82,15 +82,6 @@ struct HWComposerTest : testing::Test { EXPECT_CALL(*mHal, setVsyncEnabled(hwcDisplayId, Hwc2::IComposerClient::Vsync::DISABLE)); EXPECT_CALL(*mHal, onHotplugConnect(hwcDisplayId)); } - - void setDisplayData(HalDisplayId displayId, TimePoint lastExpectedPresentTimestamp, - Fps lastFrameInterval) { - ASSERT_TRUE(mHwc.mDisplayData.find(displayId) != mHwc.mDisplayData.end()); - auto& displayData = mHwc.mDisplayData.at(displayId); - std::scoped_lock lock{displayData.expectedPresentLock}; - displayData.lastExpectedPresentTimestamp = lastExpectedPresentTimestamp; - displayData.lastFrameInterval = lastFrameInterval; - } }; TEST_F(HWComposerTest, isHeadless) { @@ -143,7 +134,7 @@ TEST_F(HWComposerTest, getModesWithLegacyDisplayConfigs) { const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); ASSERT_TRUE(info); - EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(false)); + EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(false)); { EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _)) @@ -235,7 +226,7 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_OFF) { const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); ASSERT_TRUE(info); - EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(false)); + EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(false)); { EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _)) @@ -324,7 +315,7 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) { const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); ASSERT_TRUE(info); - EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(true)); + EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(true)); { EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _)) @@ -417,144 +408,6 @@ TEST_F(HWComposerTest, onVsyncInvalid) { EXPECT_FALSE(displayIdOpt); } -TEST_F(HWComposerTest, notifyExpectedPresentTimeout) { - constexpr hal::HWDisplayId kHwcDisplayId = 2; - expectHotplugConnect(kHwcDisplayId); - const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); - ASSERT_TRUE(info); - - auto expectedPresentTime = systemTime() + ms2ns(10); - static constexpr Fps Fps60Hz = 60_Hz; - static constexpr int32_t kFrameInterval5HzNs = static_cast<Fps>(5_Hz).getPeriodNsecs(); - static constexpr int32_t kFrameInterval60HzNs = Fps60Hz.getPeriodNsecs(); - static constexpr int32_t kFrameInterval120HzNs = static_cast<Fps>(120_Hz).getPeriodNsecs(); - 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(setDisplayData(info->id, kLastExpectedPresentTimestamp, Fps60Hz)); - - { - // Very first ExpectedPresent after idle, no previous timestamp - EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) - .WillOnce(Return(HalError::NONE)); - mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), Fps60Hz, - kTimeoutNs); - } - { - // Absent timeoutNs - expectedPresentTime += 2 * kFrameInterval5HzNs; - EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); - mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), Fps60Hz, - /*timeoutOpt*/ std::nullopt); - } - { - // Timeout is 0 - expectedPresentTime += kFrameInterval60HzNs; - EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) - .WillOnce(Return(HalError::NONE)); - mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), Fps60Hz, - Period::fromNs(0)); - } - { - // ExpectedPresent is after the timeoutNs - expectedPresentTime += 2 * kFrameInterval5HzNs; - EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) - .WillOnce(Return(HalError::NONE)); - mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), Fps60Hz, - kTimeoutNs); - } - { - // ExpectedPresent has not changed - EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); - mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), Fps60Hz, - kTimeoutNs); - } - { - // ExpectedPresent is after the last reported ExpectedPresent. - expectedPresentTime += kFrameInterval60HzNs; - EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); - mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), Fps60Hz, - kTimeoutNs); - } - { - // ExpectedPresent is before the last reported ExpectedPresent but after the timeoutNs, - // representing we changed our decision and want to present earlier than previously - // reported. - expectedPresentTime -= kFrameInterval120HzNs; - EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) - .WillOnce(Return(HalError::NONE)); - mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), Fps60Hz, - kTimeoutNs); - } -} - -TEST_F(HWComposerTest, notifyExpectedPresentRenderRateChanged) { - constexpr hal::HWDisplayId kHwcDisplayId = 2; - expectHotplugConnect(kHwcDisplayId); - const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); - ASSERT_TRUE(info); - - const auto now = systemTime(); - auto expectedPresentTime = now; - static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs()); - - ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, TimePoint::fromNs(now), Fps::fromValue(0))); - static constexpr int32_t kFrameIntervalNs120Hz = static_cast<Fps>(120_Hz).getPeriodNsecs(); - static constexpr int32_t kFrameIntervalNs96Hz = static_cast<Fps>(96_Hz).getPeriodNsecs(); - static constexpr int32_t kFrameIntervalNs80Hz = static_cast<Fps>(80_Hz).getPeriodNsecs(); - static constexpr int32_t kFrameIntervalNs60Hz = static_cast<Fps>(60_Hz).getPeriodNsecs(); - static constexpr int32_t kFrameIntervalNs40Hz = static_cast<Fps>(40_Hz).getPeriodNsecs(); - static constexpr int32_t kFrameIntervalNs30Hz = static_cast<Fps>(30_Hz).getPeriodNsecs(); - static constexpr int32_t kFrameIntervalNs24Hz = static_cast<Fps>(24_Hz).getPeriodNsecs(); - static constexpr int32_t kFrameIntervalNs20Hz = static_cast<Fps>(20_Hz).getPeriodNsecs(); - static constexpr Period kVsyncPeriod = - Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs()); - - struct FrameRateIntervalTestData { - int32_t frameIntervalNs; - bool callExpectedPresent; - }; - const std::vector<FrameRateIntervalTestData> frameIntervals = { - {kFrameIntervalNs60Hz, true}, {kFrameIntervalNs96Hz, true}, - {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs120Hz, true}, - {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs60Hz, true}, - {kFrameIntervalNs60Hz, false}, {kFrameIntervalNs30Hz, false}, - {kFrameIntervalNs24Hz, true}, {kFrameIntervalNs40Hz, true}, - {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs60Hz, true}, - {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs120Hz, true}, - }; - - for (const auto& [frameIntervalNs, callExpectedPresent] : frameIntervals) { - { - expectedPresentTime += frameIntervalNs; - if (callExpectedPresent) { - EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, - frameIntervalNs)) - .WillOnce(Return(HalError::NONE)); - } else { - EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); - } - mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), - Fps::fromPeriodNsecs(frameIntervalNs), kTimeoutNs); - } - } -} - struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> { MOCK_METHOD(void, onComposerHalHotplugEvent, (hal::HWDisplayId, DisplayHotplugEvent), (override)); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp new file mode 100644 index 0000000000..7206e2977d --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp @@ -0,0 +1,180 @@ +/* + * 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. + */ + +#undef LOG_TAG +#define LOG_TAG "LibSurfaceFlingerUnittests" + +#include "DisplayTransactionTestHelpers.h" + +namespace android { + +using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; + +class NotifyExpectedPresentTest : public DisplayTransactionTest { +public: + void SetUp() override { + mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this).inject(); + FakeHwcDisplayInjector(mDisplay->getPhysicalId(), hal::DisplayType::PHYSICAL, kIsPrimary) + .setPowerMode(hal::PowerMode::ON) + .inject(&mFlinger, mComposer); + } + +protected: + sp<DisplayDevice> mDisplay; + static constexpr bool kIsPrimary = true; + static constexpr hal::HWDisplayId HWC_DISPLAY_ID = + FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; +}; + +TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) { + const auto physicDisplayId = mDisplay->getPhysicalId(); + auto expectedPresentTime = systemTime() + ms2ns(10); + static constexpr Fps kFps60Hz = 60_Hz; + static constexpr int32_t kFrameInterval5HzNs = static_cast<Fps>(5_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameInterval60HzNs = kFps60Hz.getPeriodNsecs(); + static constexpr int32_t kFrameInterval120HzNs = static_cast<Fps>(120_Hz).getPeriodNsecs(); + 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)); + + { + // Very first ExpectedPresent after idle, no previous timestamp + EXPECT_CALL(*mComposer, + notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, + kFrameInterval60HzNs)) + .WillOnce(Return(Error::NONE)); + mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + kTimeoutNs); + } + { + // Absent timeoutNs + expectedPresentTime += 2 * kFrameInterval5HzNs; + EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); + mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + /*timeoutOpt*/ std::nullopt); + } + { + // Timeout is 0 + expectedPresentTime += kFrameInterval60HzNs; + EXPECT_CALL(*mComposer, + notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, + kFrameInterval60HzNs)) + .WillOnce(Return(Error::NONE)); + mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + Period::fromNs(0)); + } + { + // ExpectedPresent is after the timeoutNs + expectedPresentTime += 2 * kFrameInterval5HzNs; + EXPECT_CALL(*mComposer, + notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, + kFrameInterval60HzNs)) + .WillOnce(Return(Error::NONE)); + mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + kTimeoutNs); + } + { + // ExpectedPresent has not changed + EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); + mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + kTimeoutNs); + } + { + // ExpectedPresent is after the last reported ExpectedPresent. + expectedPresentTime += kFrameInterval60HzNs; + EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); + mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + kTimeoutNs); + } + { + // ExpectedPresent is before the last reported ExpectedPresent but after the timeoutNs, + // representing we changed our decision and want to present earlier than previously + // reported. + expectedPresentTime -= kFrameInterval120HzNs; + EXPECT_CALL(*mComposer, + notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, + kFrameInterval60HzNs)) + .WillOnce(Return(Error::NONE)); + mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + kTimeoutNs); + } +} + +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, + TimePoint::fromNs(now), + Fps::fromValue(0))); + static constexpr int32_t kFrameIntervalNs120Hz = static_cast<Fps>(120_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs96Hz = static_cast<Fps>(96_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs80Hz = static_cast<Fps>(80_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs60Hz = static_cast<Fps>(60_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs40Hz = static_cast<Fps>(40_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs30Hz = static_cast<Fps>(30_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs24Hz = static_cast<Fps>(24_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs20Hz = static_cast<Fps>(20_Hz).getPeriodNsecs(); + static constexpr Period kVsyncPeriod = + Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs()); + + struct FrameRateIntervalTestData { + int32_t frameIntervalNs; + bool callExpectedPresent; + }; + const std::vector<FrameRateIntervalTestData> frameIntervals = { + {kFrameIntervalNs60Hz, true}, {kFrameIntervalNs96Hz, true}, + {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs120Hz, true}, + {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs60Hz, true}, + {kFrameIntervalNs60Hz, false}, {kFrameIntervalNs30Hz, false}, + {kFrameIntervalNs24Hz, true}, {kFrameIntervalNs40Hz, true}, + {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs60Hz, true}, + {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); + } + } +} +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 8ba6bf87e2..22cb24bb6f 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -695,6 +695,21 @@ public: mFlinger->mLegacyFrontEndEnabled = false; } + void notifyExpectedPresentIfRequired(PhysicalDisplayId displayId, Period vsyncPeriod, + TimePoint expectedPresentTime, Fps frameInterval, + std::optional<Period> timeoutOpt) { + mFlinger->notifyExpectedPresentIfRequired(displayId, vsyncPeriod, expectedPresentTime, + frameInterval, timeoutOpt); + } + + void setNotifyExpectedPresentData(PhysicalDisplayId displayId, + TimePoint lastExpectedPresentTimestamp, + Fps lastFrameInterval) { + auto& displayData = mFlinger->mNotifyExpectedPresentMap[displayId]; + displayData.lastExpectedPresentTimestamp = lastExpectedPresentTimestamp; + displayData.lastFrameInterval = lastFrameInterval; + } + ~TestableSurfaceFlinger() { // All these pointer and container clears help ensure that GMock does // not report a leaked object, since the SurfaceFlinger instance may diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index d649679ac8..184dada32e 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -51,7 +51,7 @@ public: ~Composer() override; MOCK_METHOD(bool, isSupported, (OptionalFeature), (const, override)); - MOCK_METHOD(bool, getDisplayConfigurationsSupported, (), (const, override)); + MOCK_METHOD(bool, isVrrSupported, (), (const, override)); MOCK_METHOD0(getCapabilities, std::vector<aidl::android::hardware::graphics::composer3::Capability>()); MOCK_METHOD0(dumpDebugInfo, std::string()); diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h index 7c8e695d67..159b2d5305 100644 --- a/vulkan/include/vulkan/vk_android_native_buffer.h +++ b/vulkan/include/vulkan/vk_android_native_buffer.h @@ -63,7 +63,8 @@ extern "C" { /* * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 11 * - * This version of the extension deprecates the last of grallocusage + * This version of the extension deprecates the last of grallocusage and + * extends VkNativeBufferANDROID to support passing AHardwareBuffer* */ #define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 11 #define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer" @@ -111,6 +112,9 @@ typedef struct { * usage: gralloc usage requested when the buffer was allocated * usage2: gralloc usage requested when the buffer was allocated * usage3: gralloc usage requested when the buffer was allocated + * ahb: The AHardwareBuffer* from the actual ANativeWindowBuffer. Caller + * maintains ownership of resource. AHardwareBuffer pointer is only valid + * for the duration of the function call */ typedef struct { VkStructureType sType; @@ -121,6 +125,7 @@ typedef struct { int usage; /* DEPRECATED in SPEC_VERSION 6 */ VkNativeBufferUsage2ANDROID usage2; /* DEPRECATED in SPEC_VERSION 9 */ uint64_t usage3; /* ADDED in SPEC_VERSION 9 */ + struct AHardwareBuffer* ahb; /* ADDED in SPEC_VERSION 11 */ } VkNativeBufferANDROID; /* diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 4f7b0f4c57..0df5e77181 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -1970,6 +1970,8 @@ VkResult CreateSwapchainKHR(VkDevice device, &image_native_buffer.usage2.producer, &image_native_buffer.usage2.consumer); image_native_buffer.usage3 = img.buffer->usage; + image_native_buffer.ahb = + ANativeWindowBuffer_getHardwareBuffer(img.buffer.get()); image_create.pNext = &image_native_buffer; ATRACE_BEGIN("CreateImage"); @@ -2146,7 +2148,12 @@ VkResult AcquireNextImageKHR(VkDevice device, .stride = buffer->stride, .format = buffer->format, .usage = int(buffer->usage), + .usage3 = buffer->usage, + .ahb = ANativeWindowBuffer_getHardwareBuffer(buffer), }; + android_convertGralloc0To1Usage(int(buffer->usage), + &nb.usage2.producer, + &nb.usage2.consumer); VkBindImageMemoryInfo bimi = { .sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO, .pNext = &nb, @@ -2692,7 +2699,12 @@ static void InterceptBindImageMemory2( .stride = buffer->stride, .format = buffer->format, .usage = int(buffer->usage), + .usage3 = buffer->usage, + .ahb = ANativeWindowBuffer_getHardwareBuffer(buffer), }; + android_convertGralloc0To1Usage(int(buffer->usage), + &native_buffer.usage2.producer, + &native_buffer.usage2.consumer); // Reserve enough space to avoid letting re-allocation invalidate the // addresses of the elements inside. out_native_buffers->reserve(bind_info_count); diff --git a/vulkan/nulldrv/Android.bp b/vulkan/nulldrv/Android.bp index a6d540bb93..5112e14dc8 100644 --- a/vulkan/nulldrv/Android.bp +++ b/vulkan/nulldrv/Android.bp @@ -46,5 +46,8 @@ cc_library_shared { "hwvulkan_headers", "vulkan_headers", ], - shared_libs: ["liblog"], + shared_libs: [ + "liblog", + "libnativewindow", + ], } diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp index 2e87f1718b..973e71c892 100644 --- a/vulkan/nulldrv/null_driver.cpp +++ b/vulkan/nulldrv/null_driver.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <android/hardware_buffer.h> #include <hardware/hwvulkan.h> #include <errno.h> diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp index 935535f5bf..d34851e536 100644 --- a/vulkan/nulldrv/null_driver_gen.cpp +++ b/vulkan/nulldrv/null_driver_gen.cpp @@ -16,6 +16,8 @@ // WARNING: This file is generated. See ../README.md for instructions. +#include <android/hardware_buffer.h> + #include <algorithm> #include "null_driver_gen.h" |