diff options
224 files changed, 7164 insertions, 3801 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 8bcb1e5da5..4dd4f7999d 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,5 +1,6 @@ [Builtin Hooks] clang_format = true +bpfmt = true [Builtin Hooks Options] # Only turn on clang-format check for the following subfolders. @@ -26,6 +27,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp services/vibratorservice/ services/vr/ vulkan/ +bpfmt = -d [Hook Scripts] owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$" diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 783a475829..9a8ec32608 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -244,6 +244,7 @@ static const TracingCategory k_categories[] = { { OPT, "events/kmem/ion_heap_shrink/enable" }, { OPT, "events/ion/ion_stat/enable" }, { OPT, "events/gpu_mem/gpu_mem_total/enable" }, + { OPT, "events/fastrpc/fastrpc_dma_stat/enable" }, } }, { "thermal", "Thermal event", 0, { { REQ, "events/thermal/thermal_temperature/enable" }, diff --git a/cmds/cmd/Android.bp b/cmds/cmd/Android.bp index c900a24e15..c3d2601444 100644 --- a/cmds/cmd/Android.bp +++ b/cmds/cmd/Android.bp @@ -49,6 +49,7 @@ cc_binary { "liblog", "libselinux", "libbinder", + "packagemanager_aidl-cpp", ], cflags: [ diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index aff32c38c2..74dbf4b764 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -100,6 +100,7 @@ cc_defaults { "liblog", "libutils", "libbinderdebug", + "packagemanager_aidl-cpp", ], srcs: [ "DumpstateService.cpp", @@ -173,7 +174,6 @@ cc_test { test_suites: ["device-tests"], } - // =======================# // dumpstate_test_fixture # // =======================# diff --git a/cmds/dumpsys/Android.bp b/cmds/dumpsys/Android.bp index 6ab6b7f951..ceb16cb989 100644 --- a/cmds/dumpsys/Android.bp +++ b/cmds/dumpsys/Android.bp @@ -64,6 +64,10 @@ cc_binary { srcs: [ "main.cpp", ], + + shared_libs: [ + "packagemanager_aidl-cpp", + ], } cc_binary { diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp index cfd42fec30..3f7c7d6a7b 100644 --- a/cmds/surfacereplayer/replayer/Replayer.cpp +++ b/cmds/surfacereplayer/replayer/Replayer.cpp @@ -528,7 +528,7 @@ void Replayer::setTransparentRegionHint(SurfaceComposerClient::Transaction& t, void Replayer::setLayerStack(SurfaceComposerClient::Transaction& t, layer_id id, const LayerStackChange& lsc) { ALOGV("Layer %d: Setting LayerStack -- layer_stack=%d", id, lsc.layer_stack()); - t.setLayerStack(mLayers[id], lsc.layer_stack()); + t.setLayerStack(mLayers[id], ui::LayerStack::fromValue(lsc.layer_stack())); } void Replayer::setHiddenFlag(SurfaceComposerClient::Transaction& t, @@ -566,7 +566,7 @@ void Replayer::setDisplaySurface(SurfaceComposerClient::Transaction& t, void Replayer::setDisplayLayerStack(SurfaceComposerClient::Transaction& t, display_id id, const LayerStackChange& lsc) { - t.setDisplayLayerStack(mDisplays[id], lsc.layer_stack()); + t.setDisplayLayerStack(mDisplays[id], ui::LayerStack::fromValue(lsc.layer_stack())); } void Replayer::setDisplaySize(SurfaceComposerClient::Transaction& t, diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 164fc5dde3..235990a574 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -231,11 +231,3 @@ prebuilt_etc { src: "handheld_core_hardware.xml", defaults: ["frameworks_native_data_etc_defaults"], } - -prebuilt_etc { - name: "android.software.app_compat_overrides.xml", - product_specific: true, - sub_dir: "permissions", - src: "android.software.app_compat_overrides.xml", - filename_from_src: true, -} diff --git a/include/android/input.h b/include/android/input.h index 76422154f1..6d2c1b3015 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -169,6 +169,9 @@ enum { /** Drag event */ AINPUT_EVENT_TYPE_DRAG = 5, + + /** TouchMode event */ + AINPUT_EVENT_TYPE_TOUCH_MODE = 6, }; /** diff --git a/include/ftl/Flags.h b/include/ftl/Flags.h index 27c84769cb..ae708313fd 100644 --- a/include/ftl/Flags.h +++ b/include/ftl/Flags.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,79 +14,22 @@ * limitations under the License. */ -#include <android-base/stringprintf.h> +#pragma once + +#include <ftl/enum.h> +#include <ftl/string.h> -#include <array> #include <cstdint> -#include <optional> +#include <iterator> #include <string> #include <type_traits> -#include <ftl/NamedEnum.h> #include "utils/BitSet.h" -#pragma once +// TODO(b/185536303): Align with FTL style and namespace. namespace android { -namespace details { - -template <typename F> -inline constexpr auto flag_count = sizeof(F) * __CHAR_BIT__; - -template <typename F, typename T, T... I> -constexpr auto generate_flag_values(std::integer_sequence<T, I...> seq) { - constexpr size_t count = seq.size(); - - std::array<F, count> values{}; - for (size_t i = 0, v = 0; v < count; ++i) { - values[v++] = static_cast<F>(T{1} << i); - } - - return values; -} - -template <typename F> -inline constexpr auto flag_values = generate_flag_values<F>( - std::make_integer_sequence<std::underlying_type_t<F>, flag_count<F>>{}); - -template <typename F, std::size_t... I> -constexpr auto generate_flag_names(std::index_sequence<I...>) noexcept { - return std::array<std::optional<std::string_view>, sizeof...(I)>{ - {enum_value_name<F, flag_values<F>[I]>()...}}; -} - -template <typename F> -inline constexpr auto flag_names = - generate_flag_names<F>(std::make_index_sequence<flag_count<F>>{}); - -// A trait for determining whether a type is specifically an enum class or not. -template <typename T, bool = std::is_enum_v<T>> -struct is_enum_class : std::false_type {}; - -// By definition, an enum class is an enum that is not implicitly convertible to its underlying -// type. -template <typename T> -struct is_enum_class<T, true> - : std::bool_constant<!std::is_convertible_v<T, std::underlying_type_t<T>>> {}; - -template <typename T> -inline constexpr bool is_enum_class_v = is_enum_class<T>::value; -} // namespace details - -template <auto V> -constexpr auto flag_name() { - using F = decltype(V); - return details::enum_value_name<F, V>(); -} - -template <typename F> -constexpr std::optional<std::string_view> flag_name(F flag) { - using U = std::underlying_type_t<F>; - auto idx = static_cast<size_t>(__builtin_ctzl(static_cast<U>(flag))); - return details::flag_names<F>[idx]; -} - /* A class for handling flags defined by an enum or enum class in a type-safe way. */ template <typename F> class Flags { @@ -94,7 +37,7 @@ class Flags { // further to avoid this restriction but in general we want to encourage the use of enums // anyways. static_assert(std::is_enum_v<F>, "Flags type must be an enum"); - using U = typename std::underlying_type_t<F>; + using U = std::underlying_type_t<F>; public: constexpr Flags(F f) : mFlags(static_cast<U>(f)) {} @@ -106,11 +49,10 @@ public: // should force them to be explicitly constructed from their underlying types to make full use // of the type checker. template <typename T = U> - constexpr Flags(T t, typename std::enable_if_t<!details::is_enum_class_v<F>, T>* = nullptr) - : mFlags(t) {} + constexpr Flags(T t, std::enable_if_t<!ftl::is_scoped_enum_v<F>, T>* = nullptr) : mFlags(t) {} + template <typename T = U> - explicit constexpr Flags(T t, - typename std::enable_if_t<details::is_enum_class_v<F>, T>* = nullptr) + explicit constexpr Flags(T t, std::enable_if_t<ftl::is_scoped_enum_v<F>, T>* = nullptr) : mFlags(t) {} class Iterator { @@ -229,16 +171,16 @@ public: bool first = true; U unstringified = 0; for (const F f : *this) { - std::optional<std::string_view> flagString = flag_name(f); - if (flagString) { - appendFlag(result, flagString.value(), first); + if (const auto flagName = ftl::flag_name(f)) { + appendFlag(result, flagName.value(), first); } else { unstringified |= static_cast<U>(f); } } if (unstringified != 0) { - appendFlag(result, base::StringPrintf("0x%08x", unstringified), first); + constexpr auto radix = sizeof(U) == 1 ? ftl::Radix::kBin : ftl::Radix::kHex; + appendFlag(result, ftl::to_string(unstringified, radix), first); } if (first) { @@ -265,15 +207,14 @@ private: // as flags. In order to use these, add them via a `using namespace` declaration. namespace flag_operators { -template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>> +template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>> inline Flags<F> operator~(F f) { - using U = typename std::underlying_type_t<F>; - return static_cast<F>(~static_cast<U>(f)); + return static_cast<F>(~ftl::enum_cast(f)); } -template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>> + +template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>> Flags<F> operator|(F lhs, F rhs) { - using U = typename std::underlying_type_t<F>; - return static_cast<F>(static_cast<U>(lhs) | static_cast<U>(rhs)); + return static_cast<F>(ftl::enum_cast(lhs) | ftl::enum_cast(rhs)); } } // namespace flag_operators diff --git a/include/ftl/NamedEnum.h b/include/ftl/NamedEnum.h deleted file mode 100644 index 6e98feeb87..0000000000 --- a/include/ftl/NamedEnum.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <android-base/stringprintf.h> - -#include <array> -#include <cstdint> -#include <optional> -#include <string> - -#pragma once - -namespace android { - -namespace details { -template <typename E, E V> -constexpr std::optional<std::string_view> enum_value_name() { - // Should look something like (but all on one line): - // std::optional<std::string_view> - // android::details::enum_value_name() - // [E = android::test::TestEnums, V = android::test::TestEnums::ONE] - std::string_view view = __PRETTY_FUNCTION__; - size_t templateStart = view.rfind("["); - size_t templateEnd = view.rfind("]"); - if (templateStart == std::string::npos || templateEnd == std::string::npos) { - return std::nullopt; - } - - // Extract the template parameters without the enclosing braces. - // Example (cont'd): E = android::test::TestEnums, V = android::test::TestEnums::ONE - view = view.substr(templateStart + 1, templateEnd - templateStart - 1); - size_t valStart = view.rfind("V = "); - if (valStart == std::string::npos) { - return std::nullopt; - } - - // Example (cont'd): V = android::test::TestEnums::ONE - view = view.substr(valStart); - // Check invalid enum values with cast, like V = (android::test::TestEnums)8. - if (view.find('(') != std::string::npos) { - return std::nullopt; - } - size_t nameStart = view.rfind("::"); - if (nameStart == std::string::npos) { - return std::nullopt; - } - - // Chop off the initial "::" - nameStart += 2; - return view.substr(nameStart); -} - -template <typename E, typename T, T... I> -constexpr auto generate_enum_values(std::integer_sequence<T, I...> seq) { - constexpr size_t count = seq.size(); - - std::array<E, count> values{}; - for (size_t i = 0, v = 0; v < count; ++i) { - values[v++] = static_cast<E>(T{0} + i); - } - - return values; -} - -template <typename E, std::size_t N> -inline constexpr auto enum_values = - generate_enum_values<E>(std::make_integer_sequence<std::underlying_type_t<E>, N>{}); - -template <typename E, std::size_t N, std::size_t... I> -constexpr auto generate_enum_names(std::index_sequence<I...>) noexcept { - return std::array<std::optional<std::string_view>, sizeof...(I)>{ - {enum_value_name<E, enum_values<E, N>[I]>()...}}; -} - -template <typename E, std::size_t N> -inline constexpr auto enum_names = generate_enum_names<E, N>(std::make_index_sequence<N>{}); - -} // namespace details - -class NamedEnum { -public: - // By default allowed enum value range is 0 ~ 7. - template <typename E> - static constexpr size_t max = 8; - - template <auto V> - static constexpr auto enum_name() { - using E = decltype(V); - return details::enum_value_name<E, V>(); - } - - template <typename E> - static constexpr std::optional<std::string_view> enum_name(E val) { - auto idx = static_cast<size_t>(val); - return idx < max<E> ? details::enum_names<E, max<E>>[idx] : std::nullopt; - } - - // Helper function for parsing enum value to string. - // Example : enum class TestEnums { ZERO = 0x0 }; - // NamedEnum::string(TestEnums::ZERO) returns string of "ZERO". - // Note the default maximum enum is 8, if the enum ID to be parsed if greater than 8 like 16, - // it should be declared to specialized the maximum enum by below: - // template <> constexpr size_t NamedEnum::max<TestEnums> = 16; - // If the enum class definition is sparse and contains enum values starting from a large value, - // Do not specialize it to a large number to avoid performance issues. - // The recommended maximum enum number to specialize is 64. - template <typename E> - static const std::string string(E val, const char* fallbackFormat = "%02d") { - std::string result; - std::optional<std::string_view> enumString = enum_name(val); - result += enumString ? enumString.value() : base::StringPrintf(fallbackFormat, val); - return result; - } -}; - -} // namespace android diff --git a/include/ftl/cast.h b/include/ftl/cast.h new file mode 100644 index 0000000000..ff1b58ad56 --- /dev/null +++ b/include/ftl/cast.h @@ -0,0 +1,84 @@ +/* + * Copyright 2021 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 <limits> +#include <type_traits> + +#include <ftl/details/cast.h> + +namespace android::ftl { + +enum class CastSafety { kSafe, kUnderflow, kOverflow }; + +// Returns whether static_cast<R>(v) is safe, or would result in underflow or overflow. +// +// static_assert(ftl::cast_safety<uint8_t>(-1) == ftl::CastSafety::kUnderflow); +// static_assert(ftl::cast_safety<int8_t>(128u) == ftl::CastSafety::kOverflow); +// +// static_assert(ftl::cast_safety<uint32_t>(-.1f) == ftl::CastSafety::kUnderflow); +// static_assert(ftl::cast_safety<int32_t>(static_cast<float>(INT32_MAX)) == +// ftl::CastSafety::kOverflow); +// +// static_assert(ftl::cast_safety<float>(-DBL_MAX) == ftl::CastSafety::kUnderflow); +// +template <typename R, typename T> +constexpr CastSafety cast_safety(T v) { + static_assert(std::is_arithmetic_v<T>); + static_assert(std::is_arithmetic_v<R>); + + constexpr bool kFromSigned = std::is_signed_v<T>; + constexpr bool kToSigned = std::is_signed_v<R>; + + using details::max_exponent; + + // If the R range contains the T range, then casting is always safe. + if constexpr ((kFromSigned == kToSigned && max_exponent<R> >= max_exponent<T>) || + (!kFromSigned && kToSigned && max_exponent<R> > max_exponent<T>)) { + return CastSafety::kSafe; + } + + using C = std::common_type_t<R, T>; + + if constexpr (kFromSigned) { + using L = details::safe_limits<R, T>; + + if constexpr (kToSigned) { + // Signed to signed. + if (v < L::lowest()) return CastSafety::kUnderflow; + return v <= L::max() ? CastSafety::kSafe : CastSafety::kOverflow; + } else { + // Signed to unsigned. + if (v < 0) return CastSafety::kUnderflow; + return static_cast<C>(v) <= static_cast<C>(L::max()) ? CastSafety::kSafe + : CastSafety::kOverflow; + } + } else { + using L = std::numeric_limits<R>; + + if constexpr (kToSigned) { + // Unsigned to signed. + return static_cast<C>(v) <= static_cast<C>(L::max()) ? CastSafety::kSafe + : CastSafety::kOverflow; + } else { + // Unsigned to unsigned. + return v <= L::max() ? CastSafety::kSafe : CastSafety::kOverflow; + } + } +} + +} // namespace android::ftl diff --git a/include/ftl/details/cast.h b/include/ftl/details/cast.h new file mode 100644 index 0000000000..87b9f1e20a --- /dev/null +++ b/include/ftl/details/cast.h @@ -0,0 +1,57 @@ +/* + * Copyright 2021 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 <limits> +#include <type_traits> + +namespace android::ftl::details { + +// Exponent whose power of 2 is the (exclusive) upper bound of T. +template <typename T, typename L = std::numeric_limits<T>> +constexpr int max_exponent = std::is_floating_point_v<T> ? L::max_exponent : L::digits; + +// Extension of std::numeric_limits<T> that reduces the maximum for integral types T such that it +// has an exact representation for floating-point types F. For example, the maximum int32_t value +// is 2'147'483'647, but casting it to float commonly rounds up to 2'147'483'650.f, which cannot +// be safely converted back lest the signed overflow invokes undefined behavior. This pitfall is +// avoided by clearing the lower (31 - 24 =) 7 bits of precision to 2'147'483'520. Note that the +// minimum is representable. +template <typename T, typename F> +struct safe_limits : std::numeric_limits<T> { + static constexpr T max() { + using Base = std::numeric_limits<T>; + + if constexpr (std::is_integral_v<T> && std::is_floating_point_v<F>) { + // Assume the mantissa is 24 bits for float, or 53 bits for double. + using Float = std::numeric_limits<F>; + static_assert(Float::is_iec559); + + // If the integer is wider than the mantissa, clear the excess bits of precision. + constexpr int kShift = Base::digits - Float::digits; + if constexpr (kShift > 0) { + using U = std::make_unsigned_t<T>; + constexpr U kOne = static_cast<U>(1); + return static_cast<U>(Base::max()) & ~((kOne << kShift) - kOne); + } + } + + return Base::max(); + } +}; + +} // namespace android::ftl::details diff --git a/include/ftl/enum.h b/include/ftl/enum.h new file mode 100644 index 0000000000..dfe3a0976b --- /dev/null +++ b/include/ftl/enum.h @@ -0,0 +1,299 @@ +/* + * Copyright 2021 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 <limits> +#include <optional> +#include <string_view> +#include <type_traits> +#include <utility> + +#include <ftl/string.h> + +// Returns the name of enumerator E::V (i.e. "V") as std::optional<std::string_view> by parsing the +// compiler-generated string literal for the signature of this function. The function is defined in +// the global namespace with a short name and inferred return type to reduce bloat in the read-only +// data segment. +template <typename E, E V> +constexpr auto ftl_enum() { + static_assert(std::is_enum_v<E>); + + using R = std::optional<std::string_view>; + using namespace std::literals; + + // The "pretty" signature has the following format: + // + // auto ftl_enum() [E = android::test::Enum, V = android::test::Enum::kValue] + // + std::string_view view = __PRETTY_FUNCTION__; + const auto template_begin = view.rfind('['); + const auto template_end = view.rfind(']'); + if (template_begin == view.npos || template_end == view.npos) return R{}; + + // Extract the template parameters without the enclosing brackets. Example (cont'd): + // + // E = android::test::Enum, V = android::test::Enum::kValue + // + view = view.substr(template_begin + 1, template_end - template_begin - 1); + const auto value_begin = view.rfind("V = "sv); + if (value_begin == view.npos) return R{}; + + // Example (cont'd): + // + // V = android::test::Enum::kValue + // + view = view.substr(value_begin); + const auto name_begin = view.rfind("::"sv); + if (name_begin == view.npos) return R{}; + + // Chop off the leading "::". + const auto name = view.substr(name_begin + 2); + + // A value that is not enumerated has the format "Enum)42". + return name.find(')') == view.npos ? R{name} : R{}; +} + +namespace android::ftl { + +// Trait for determining whether a type is specifically a scoped enum or not. By definition, a +// scoped enum is one that is not implicitly convertible to its underlying type. +// +// TODO: Replace with std::is_scoped_enum in C++23. +// +template <typename T, bool = std::is_enum_v<T>> +struct is_scoped_enum : std::false_type {}; + +template <typename T> +struct is_scoped_enum<T, true> : std::negation<std::is_convertible<T, std::underlying_type_t<T>>> { +}; + +template <typename T> +inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value; + +// Shorthand for casting an enumerator to its integral value. +// +// enum class E { A, B, C }; +// static_assert(ftl::enum_cast(E::B) == 1); +// +template <typename E> +constexpr auto enum_cast(E v) { + return static_cast<std::underlying_type_t<E>>(v); +} + +// Traits for retrieving an enum's range. An enum specifies its range by defining enumerators named +// ftl_first and ftl_last. If omitted, ftl_first defaults to 0, whereas ftl_last defaults to N - 1 +// where N is the bit width of the underlying type, but only if that type is unsigned, assuming the +// enumerators are flags. Also, note that unscoped enums must define both bounds, as casting out-of- +// range values results in undefined behavior if the underlying type is not fixed. +// +// enum class E { A, B, C, F = 5, ftl_last = F }; +// +// static_assert(ftl::enum_begin_v<E> == E::A); +// static_assert(ftl::enum_last_v<E> == E::F); +// static_assert(ftl::enum_size_v<E> == 6); +// +// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; +// +// static_assert(ftl::enum_begin_v<F> == F{0}); +// static_assert(ftl::enum_last_v<F> == F{15}); +// static_assert(ftl::enum_size_v<F> == 16); +// +template <typename E, typename = void> +struct enum_begin { + static_assert(is_scoped_enum_v<E>, "Missing ftl_first enumerator"); + static constexpr E value{0}; +}; + +template <typename E> +struct enum_begin<E, std::void_t<decltype(E::ftl_first)>> { + static constexpr E value = E::ftl_first; +}; + +template <typename E> +inline constexpr E enum_begin_v = enum_begin<E>::value; + +template <typename E, typename = void> +struct enum_end { + using U = std::underlying_type_t<E>; + static_assert(is_scoped_enum_v<E> && std::is_unsigned_v<U>, "Missing ftl_last enumerator"); + + static constexpr E value{std::numeric_limits<U>::digits}; +}; + +template <typename E> +struct enum_end<E, std::void_t<decltype(E::ftl_last)>> { + static constexpr E value = E{enum_cast(E::ftl_last) + 1}; +}; + +template <typename E> +inline constexpr E enum_end_v = enum_end<E>::value; + +template <typename E> +inline constexpr E enum_last_v = E{enum_cast(enum_end_v<E>) - 1}; + +template <typename E> +struct enum_size { + static constexpr auto kBegin = enum_cast(enum_begin_v<E>); + static constexpr auto kEnd = enum_cast(enum_end_v<E>); + static_assert(kBegin < kEnd, "Invalid range"); + + static constexpr std::size_t value = kEnd - kBegin; + static_assert(value <= 64, "Excessive range size"); +}; + +template <typename E> +inline constexpr std::size_t enum_size_v = enum_size<E>::value; + +namespace details { + +template <auto V> +struct Identity { + static constexpr auto value = V; +}; + +template <typename E> +using make_enum_sequence = std::make_integer_sequence<std::underlying_type_t<E>, enum_size_v<E>>; + +template <typename E, template <E> class = Identity, typename = make_enum_sequence<E>> +struct EnumRange; + +template <typename E, template <E> class F, typename T, T... Vs> +struct EnumRange<E, F, std::integer_sequence<T, Vs...>> { + static constexpr auto kBegin = enum_cast(enum_begin_v<E>); + static constexpr auto kSize = enum_size_v<E>; + + using R = decltype(F<E{}>::value); + const R values[kSize] = {F<static_cast<E>(Vs + kBegin)>::value...}; + + constexpr const auto* begin() const { return values; } + constexpr const auto* end() const { return values + kSize; } +}; + +template <auto V> +struct EnumName { + static constexpr auto value = ftl_enum<decltype(V), V>(); +}; + +template <auto I> +struct FlagName { + using E = decltype(I); + using U = std::underlying_type_t<E>; + + static constexpr E V{U{1} << enum_cast(I)}; + static constexpr auto value = ftl_enum<E, V>(); +}; + +} // namespace details + +// Returns an iterable over the range of an enum. +// +// enum class E { A, B, C, F = 5, ftl_last = F }; +// +// std::string string; +// for (E v : ftl::enum_range<E>()) { +// string += ftl::enum_name(v).value_or("?"); +// } +// +// assert(string == "ABC??F"); +// +template <typename E> +constexpr auto enum_range() { + return details::EnumRange<E>{}; +} + +// Returns a stringified enumerator at compile time. +// +// enum class E { A, B, C }; +// static_assert(ftl::enum_name<E::B>() == "B"); +// +template <auto V> +constexpr std::string_view enum_name() { + constexpr auto kName = ftl_enum<decltype(V), V>(); + static_assert(kName, "Unknown enumerator"); + return *kName; +} + +// Returns a stringified enumerator, possibly at compile time. +// +// enum class E { A, B, C, F = 5, ftl_last = F }; +// +// static_assert(ftl::enum_name(E::C).value_or("?") == "C"); +// static_assert(ftl::enum_name(E{3}).value_or("?") == "?"); +// +template <typename E> +constexpr std::optional<std::string_view> enum_name(E v) { + const auto value = enum_cast(v); + + constexpr auto kBegin = enum_cast(enum_begin_v<E>); + constexpr auto kLast = enum_cast(enum_last_v<E>); + if (value < kBegin || value > kLast) return {}; + + constexpr auto kRange = details::EnumRange<E, details::EnumName>{}; + return kRange.values[value - kBegin]; +} + +// Returns a stringified flag enumerator, possibly at compile time. +// +// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; +// +// static_assert(ftl::flag_name(F::Z).value_or("?") == "Z"); +// static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?"); +// +template <typename E> +constexpr std::optional<std::string_view> flag_name(E v) { + const auto value = enum_cast(v); + + // TODO: Replace with std::popcount and std::countr_zero in C++20. + if (__builtin_popcountl(value) != 1) return {}; + + constexpr auto kRange = details::EnumRange<E, details::FlagName>{}; + return kRange.values[__builtin_ctzl(value)]; +} + +// Returns a stringified enumerator, or its integral value if not named. +// +// enum class E { A, B, C, F = 5, ftl_last = F }; +// +// assert(ftl::enum_string(E::C) == "C"); +// assert(ftl::enum_string(E{3}) == "3"); +// +template <typename E> +inline std::string enum_string(E v) { + if (const auto name = enum_name(v)) { + return std::string(*name); + } + return to_string(enum_cast(v)); +} + +// Returns a stringified flag enumerator, or its integral value if not named. +// +// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; +// +// assert(ftl::flag_string(F::Z) == "Z"); +// assert(ftl::flag_string(F{7}) == "0b111"); +// +template <typename E> +inline std::string flag_string(E v) { + if (const auto name = flag_name(v)) { + return std::string(*name); + } + constexpr auto radix = sizeof(E) == 1 ? Radix::kBin : Radix::kHex; + return to_string(enum_cast(v), radix); +} + +} // namespace android::ftl diff --git a/include/ftl/small_map.h b/include/ftl/small_map.h index 84c15ebdca..bcaba82934 100644 --- a/include/ftl/small_map.h +++ b/include/ftl/small_map.h @@ -19,6 +19,7 @@ #include <ftl/initializer_list.h> #include <ftl/small_vector.h> +#include <algorithm> #include <functional> #include <optional> #include <type_traits> @@ -28,7 +29,10 @@ namespace android::ftl { // Associative container with unique, unordered keys. Unlike std::unordered_map, key-value pairs are // stored in contiguous storage for cache efficiency. The map is allocated statically until its size -// exceeds N, at which point mappings are relocated to dynamic memory. +// exceeds N, at which point mappings are relocated to dynamic memory. The try_emplace operation has +// a non-standard analogue try_replace that destructively emplaces. The API also defines an in-place +// counterpart to insert_or_assign: emplace_or_replace. Lookup is done not via a subscript operator, +// but immutable getters that can optionally transform the value. // // SmallMap<K, V, 0> unconditionally allocates on the heap. // @@ -43,16 +47,19 @@ namespace android::ftl { // assert(!map.dynamic()); // // assert(map.contains(123)); -// assert(map.find(42, [](const std::string& s) { return s.size(); }) == 3u); +// assert(map.get(42, [](const std::string& s) { return s.size(); }) == 3u); // -// const auto opt = map.find(-1); +// const auto opt = map.get(-1); // assert(opt); // // std::string& ref = *opt; // assert(ref.empty()); // ref = "xyz"; // -// assert(map == SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc"))); +// map.emplace_or_replace(0, "vanilla", 2u, 3u); +// assert(map.dynamic()); +// +// assert(map == SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc"))); // template <typename K, typename V, std::size_t N> class SmallMap final { @@ -80,12 +87,7 @@ class SmallMap final { // The syntax for listing pairs is as follows: // // ftl::SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?'); - // // static_assert(std::is_same_v<decltype(map), ftl::SmallMap<int, std::string, 3>>); - // assert(map.size() == 3u); - // assert(map.contains(-1) && map.find(-1)->get().empty()); - // assert(map.contains(42) && map.find(42)->get() == "???"); - // assert(map.contains(123) && map.find(123)->get() == "abc"); // // The types of the key and value are deduced if the first pair contains exactly two arguments: // @@ -95,7 +97,7 @@ class SmallMap final { template <typename U, std::size_t... Sizes, typename... Types> SmallMap(InitializerList<U, std::index_sequence<Sizes...>, Types...>&& list) : map_(std::move(list)) { - // TODO: Enforce unique keys. + deduplicate(); } size_type max_size() const { return map_.max_size(); } @@ -115,27 +117,27 @@ class SmallMap final { // Returns whether a mapping exists for the given key. bool contains(const key_type& key) const { - return find(key, [](const mapped_type&) {}); + return get(key, [](const mapped_type&) {}); } // Returns a reference to the value for the given key, or std::nullopt if the key was not found. // // ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); // - // const auto opt = map.find('c'); + // const auto opt = map.get('c'); // assert(opt == 'C'); // // char d = 'd'; - // const auto ref = map.find('d').value_or(std::ref(d)); + // const auto ref = map.get('d').value_or(std::ref(d)); // ref.get() = 'D'; // assert(d == 'D'); // - auto find(const key_type& key) const -> std::optional<std::reference_wrapper<const mapped_type>> { - return find(key, [](const mapped_type& v) { return std::cref(v); }); + auto get(const key_type& key) const -> std::optional<std::reference_wrapper<const mapped_type>> { + return get(key, [](const mapped_type& v) { return std::cref(v); }); } - auto find(const key_type& key) -> std::optional<std::reference_wrapper<mapped_type>> { - return find(key, [](mapped_type& v) { return std::ref(v); }); + auto get(const key_type& key) -> std::optional<std::reference_wrapper<mapped_type>> { + return get(key, [](mapped_type& v) { return std::ref(v); }); } // Returns the result R of a unary operation F on (a constant or mutable reference to) the value @@ -144,11 +146,11 @@ class SmallMap final { // // ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); // - // assert(map.find('c', [](char c) { return std::toupper(c); }) == 'Z'); - // assert(map.find('c', [](char& c) { c = std::toupper(c); })); + // assert(map.get('c', [](char c) { return std::toupper(c); }) == 'Z'); + // assert(map.get('c', [](char& c) { c = std::toupper(c); })); // template <typename F, typename R = std::invoke_result_t<F, const mapped_type&>> - auto find(const key_type& key, F f) const + auto get(const key_type& key, F f) const -> std::conditional_t<std::is_void_v<R>, bool, std::optional<R>> { for (auto& [k, v] : *this) { if (k == key) { @@ -165,12 +167,96 @@ class SmallMap final { } template <typename F> - auto find(const key_type& key, F f) { - return std::as_const(*this).find( + auto get(const key_type& key, F f) { + return std::as_const(*this).get( key, [&f](const mapped_type& v) { return f(const_cast<mapped_type&>(v)); }); } + // Returns an iterator to an existing mapping for the given key, or the end() iterator otherwise. + const_iterator find(const key_type& key) const { return const_cast<SmallMap&>(*this).find(key); } + iterator find(const key_type& key) { return find(key, begin()); } + + // Inserts a mapping unless it exists. Returns an iterator to the inserted or existing mapping, + // and whether the mapping was inserted. + // + // On emplace, if the map reaches its static or dynamic capacity, then all iterators are + // invalidated. Otherwise, only the end() iterator is invalidated. + // + template <typename... Args> + std::pair<iterator, bool> try_emplace(const key_type& key, Args&&... args) { + if (const auto it = find(key); it != end()) { + return {it, false}; + } + + auto& ref = map_.emplace_back(std::piecewise_construct, std::forward_as_tuple(key), + std::forward_as_tuple(std::forward<Args>(args)...)); + return {&ref, true}; + } + + // Replaces a mapping if it exists, and returns an iterator to it. Returns the end() iterator + // otherwise. + // + // The value is replaced via move constructor, so type V does not need to define copy/move + // assignment, e.g. its data members may be const. + // + // The arguments may directly or indirectly refer to the mapping being replaced. + // + // Iterators to the replaced mapping point to its replacement, and others remain valid. + // + template <typename... Args> + iterator try_replace(const key_type& key, Args&&... args) { + const auto it = find(key); + if (it == end()) return it; + map_.replace(it, std::piecewise_construct, std::forward_as_tuple(key), + std::forward_as_tuple(std::forward<Args>(args)...)); + return it; + } + + // In-place counterpart of std::unordered_map's insert_or_assign. Returns true on emplace, or + // false on replace. + // + // The value is emplaced and replaced via move constructor, so type V does not need to define + // copy/move assignment, e.g. its data members may be const. + // + // On emplace, if the map reaches its static or dynamic capacity, then all iterators are + // invalidated. Otherwise, only the end() iterator is invalidated. On replace, iterators + // to the replaced mapping point to its replacement, and others remain valid. + // + template <typename... Args> + std::pair<iterator, bool> emplace_or_replace(const key_type& key, Args&&... args) { + const auto [it, ok] = try_emplace(key, std::forward<Args>(args)...); + if (ok) return {it, ok}; + map_.replace(it, std::piecewise_construct, std::forward_as_tuple(key), + std::forward_as_tuple(std::forward<Args>(args)...)); + return {it, ok}; + } + + // Removes a mapping if it exists, and returns whether it did. + // + // The last() and end() iterators, as well as those to the erased mapping, are invalidated. + // + bool erase(const key_type& key) { return erase(key, begin()); } + private: + iterator find(const key_type& key, iterator first) { + return std::find_if(first, end(), [&key](const auto& pair) { return pair.first == key; }); + } + + bool erase(const key_type& key, iterator first) { + const auto it = find(key, first); + if (it == end()) return false; + map_.unstable_erase(it); + return true; + } + + void deduplicate() { + for (auto it = begin(); it != end();) { + if (const auto key = it->first; ++it != end()) { + while (erase(key, it)); + } + } + } + Map map_; }; @@ -186,7 +272,7 @@ bool operator==(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) { for (const auto& [k, v] : lhs) { const auto& lv = v; - if (!rhs.find(k, [&lv](const auto& rv) { return lv == rv; }).value_or(false)) { + if (!rhs.get(k, [&lv](const auto& rv) { return lv == rv; }).value_or(false)) { return false; } } diff --git a/include/ftl/small_vector.h b/include/ftl/small_vector.h index cb0ae359eb..0341435813 100644 --- a/include/ftl/small_vector.h +++ b/include/ftl/small_vector.h @@ -348,7 +348,7 @@ class SmallVector<T, 0> final : ArrayTraits<T>, using Impl::pop_back; void unstable_erase(iterator it) { - if (it != last()) std::iter_swap(it, last()); + if (it != last()) replace(it, std::move(back())); pop_back(); } diff --git a/include/ftl/string.h b/include/ftl/string.h new file mode 100644 index 0000000000..2d96b06a2f --- /dev/null +++ b/include/ftl/string.h @@ -0,0 +1,101 @@ +/* + * Copyright 2021 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 <cassert> +#include <charconv> +#include <limits> +#include <string> +#include <string_view> +#include <type_traits> + +namespace android::ftl { + +enum class Radix { kBin = 2, kDec = 10, kHex = 16 }; + +template <typename T> +struct to_chars_length { + static_assert(std::is_integral_v<T>); + // Maximum binary digits, plus minus sign and radix prefix. + static constexpr std::size_t value = std::numeric_limits<std::make_unsigned_t<T>>::digits + 3; +}; + +template <typename T> +constexpr std::size_t to_chars_length_v = to_chars_length<T>::value; + +template <typename T = std::int64_t> +using to_chars_buffer_t = char[to_chars_length_v<T>]; + +// Lightweight (not allocating nor sprintf-based) alternative to std::to_string for integers, with +// optional radix. See also ftl::to_string below. +// +// ftl::to_chars_buffer_t<> buffer; +// +// assert(ftl::to_chars(buffer, 123u) == "123"); +// assert(ftl::to_chars(buffer, -42, ftl::Radix::kBin) == "-0b101010"); +// assert(ftl::to_chars(buffer, 0xcafe, ftl::Radix::kHex) == "0xcafe"); +// assert(ftl::to_chars(buffer, '*', ftl::Radix::kHex) == "0x2a"); +// +template <typename T, std::size_t N> +std::string_view to_chars(char (&buffer)[N], T v, Radix radix = Radix::kDec) { + static_assert(N >= to_chars_length_v<T>); + + auto begin = buffer + 2; + const auto [end, err] = std::to_chars(begin, buffer + N, v, static_cast<int>(radix)); + assert(err == std::errc()); + + if (radix == Radix::kDec) { + // TODO: Replace with {begin, end} in C++20. + return {begin, static_cast<std::size_t>(end - begin)}; + } + + const auto prefix = radix == Radix::kBin ? 'b' : 'x'; + if constexpr (std::is_unsigned_v<T>) { + buffer[0] = '0'; + buffer[1] = prefix; + } else { + if (*begin == '-') { + *buffer = '-'; + } else { + --begin; + } + + *begin-- = prefix; + *begin = '0'; + } + + // TODO: Replace with {buffer, end} in C++20. + return {buffer, static_cast<std::size_t>(end - buffer)}; +} + +// Lightweight (not sprintf-based) alternative to std::to_string for integers, with optional radix. +// +// assert(ftl::to_string(123u) == "123"); +// assert(ftl::to_string(-42, ftl::Radix::kBin) == "-0b101010"); +// assert(ftl::to_string(0xcafe, ftl::Radix::kHex) == "0xcafe"); +// assert(ftl::to_string('*', ftl::Radix::kHex) == "0x2a"); +// +template <typename T> +inline std::string to_string(T v, Radix radix = Radix::kDec) { + to_chars_buffer_t<T> buffer; + return std::string(to_chars(buffer, v, radix)); +} + +std::string to_string(bool) = delete; +std::string to_string(bool, Radix) = delete; + +} // namespace android::ftl diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h index a6213f3ddd..9148fee532 100644 --- a/include/input/DisplayViewport.h +++ b/include/input/DisplayViewport.h @@ -18,7 +18,8 @@ #define _LIBINPUT_DISPLAY_VIEWPORT_H #include <android-base/stringprintf.h> -#include <ftl/NamedEnum.h> +#include <ftl/enum.h> +#include <ftl/string.h> #include <gui/constants.h> #include <input/Input.h> @@ -44,6 +45,8 @@ enum class ViewportType : int32_t { INTERNAL = 1, EXTERNAL = 2, VIRTUAL = 3, + + ftl_last = VIRTUAL }; /* @@ -132,9 +135,8 @@ struct DisplayViewport { "physicalFrame=[%d, %d, %d, %d], " "deviceSize=[%d, %d], " "isActive=[%d]", - NamedEnum::string(type).c_str(), displayId, uniqueId.c_str(), - physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() - : "<none>", + ftl::enum_string(type).c_str(), displayId, uniqueId.c_str(), + physicalPort ? ftl::to_string(*physicalPort).c_str() : "<none>", orientation, logicalLeft, logicalTop, logicalRight, logicalBottom, physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth, deviceHeight, isActive); diff --git a/include/input/Input.h b/include/input/Input.h index 4adaa5b1c5..f170f0fa23 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -369,8 +369,6 @@ struct PointerCoords { float getAxisValue(int32_t axis) const; status_t setAxisValue(int32_t axis, float value); - void scale(float globalScale); - // Scale the pointer coordinates according to a global scale and a // window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR // axes, however the window scaling will not. @@ -526,13 +524,17 @@ public: inline int32_t getAction() const { return mAction; } - inline int32_t getActionMasked() const { return mAction & AMOTION_EVENT_ACTION_MASK; } + static int32_t getActionMasked(int32_t action) { return action & AMOTION_EVENT_ACTION_MASK; } + + inline int32_t getActionMasked() const { return getActionMasked(mAction); } - inline int32_t getActionIndex() const { - return (mAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) - >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + static int32_t getActionIndex(int32_t action) { + return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> + AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; } + inline int32_t getActionIndex() const { return getActionIndex(mAction); } + inline void setAction(int32_t action) { mAction = action; } inline int32_t getFlags() const { return mFlags; } @@ -888,6 +890,25 @@ protected: float mX, mY; }; +/* + * Touch mode events. + */ +class TouchModeEvent : public InputEvent { +public: + virtual ~TouchModeEvent() {} + + virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_TOUCH_MODE; } + + inline bool isInTouchMode() const { return mIsInTouchMode; } + + void initialize(int32_t id, bool isInTouchMode); + + void initialize(const TouchModeEvent& from); + +protected: + bool mIsInTouchMode; +}; + /** * Base class for verified events. * Do not create a VerifiedInputEvent explicitly. @@ -952,6 +973,7 @@ public: virtual FocusEvent* createFocusEvent() = 0; virtual CaptureEvent* createCaptureEvent() = 0; virtual DragEvent* createDragEvent() = 0; + virtual TouchModeEvent* createTouchModeEvent() = 0; }; /* @@ -968,6 +990,7 @@ public: virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; } virtual CaptureEvent* createCaptureEvent() override { return &mCaptureEvent; } virtual DragEvent* createDragEvent() override { return &mDragEvent; } + virtual TouchModeEvent* createTouchModeEvent() override { return &mTouchModeEvent; } private: KeyEvent mKeyEvent; @@ -975,6 +998,7 @@ private: FocusEvent mFocusEvent; CaptureEvent mCaptureEvent; DragEvent mDragEvent; + TouchModeEvent mTouchModeEvent; }; /* @@ -990,6 +1014,7 @@ public: virtual FocusEvent* createFocusEvent() override; virtual CaptureEvent* createCaptureEvent() override; virtual DragEvent* createDragEvent() override; + virtual TouchModeEvent* createTouchModeEvent() override; void recycle(InputEvent* event); @@ -1001,6 +1026,7 @@ private: std::queue<std::unique_ptr<FocusEvent>> mFocusEventPool; std::queue<std::unique_ptr<CaptureEvent>> mCaptureEventPool; std::queue<std::unique_ptr<DragEvent>> mDragEventPool; + std::queue<std::unique_ptr<TouchModeEvent>> mTouchModeEventPool; }; /* diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 7f0324a4a8..22aae196c6 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -84,6 +84,9 @@ enum class InputDeviceSensorType : int32_t { GAME_ROTATION_VECTOR = ASENSOR_TYPE_GAME_ROTATION_VECTOR, GYROSCOPE_UNCALIBRATED = ASENSOR_TYPE_GYROSCOPE_UNCALIBRATED, SIGNIFICANT_MOTION = ASENSOR_TYPE_SIGNIFICANT_MOTION, + + ftl_first = ACCELEROMETER, + ftl_last = SIGNIFICANT_MOTION }; enum class InputDeviceSensorAccuracy : int32_t { @@ -105,6 +108,8 @@ enum class InputDeviceLightType : int32_t { PLAYER_ID = 1, RGB = 2, MULTI_COLOR = 3, + + ftl_last = MULTI_COLOR }; struct InputDeviceSensorInfo { diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index a790b5637f..7632b30bd2 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -72,6 +72,9 @@ struct InputMessage { CAPTURE, DRAG, TIMELINE, + TOUCH_MODE, + + ftl_last = TOUCH_MODE }; struct Header { @@ -206,6 +209,15 @@ struct InputMessage { inline size_t size() const { return sizeof(Timeline); } } timeline; + + struct TouchMode { + int32_t eventId; + // The following 2 fields take up 4 bytes total + bool isInTouchMode; + uint8_t empty[3]; + + inline size_t size() const { return sizeof(TouchMode); } + } touchMode; } __attribute__((aligned(8))) body; bool isValid(size_t actualSize) const; @@ -388,6 +400,15 @@ public: */ status_t publishDragEvent(uint32_t seq, int32_t eventId, float x, float y, bool isExiting); + /* Publishes a touch mode event to the input channel. + * + * Returns OK on success. + * Returns WOULD_BLOCK if the channel is full. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Other errors probably indicate that the channel is broken. + */ + status_t publishTouchModeEvent(uint32_t seq, int32_t eventId, bool isInTouchMode); + struct Finished { uint32_t seq; bool handled; @@ -658,6 +679,7 @@ private: static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg); static void initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg); static void initializeDragEvent(DragEvent* event, const InputMessage* msg); + static void initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg); static void addSample(MotionEvent* event, const InputMessage* msg); static bool canAddSample(const Batch& batch, const InputMessage* msg); static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time); diff --git a/include/private/surface_control_private.h b/include/private/surface_control_private.h index 37a476e679..7e6c51587d 100644 --- a/include/private/surface_control_private.h +++ b/include/private/surface_control_private.h @@ -29,8 +29,8 @@ typedef struct ASurfaceControlStats ASurfaceControlStats; /** * Callback to be notified when surface stats for a specific surface control are available. */ -typedef void (*ASurfaceControl_SurfaceStatsListener)(void* context, - ASurfaceControl* control, ASurfaceControlStats* stats); +typedef void (*ASurfaceControl_SurfaceStatsListener)(void* context, int32_t id, + ASurfaceControlStats* stats); /** * Registers a callback to be invoked when surface stats from a specific surface are available. @@ -42,7 +42,7 @@ typedef void (*ASurfaceControl_SurfaceStatsListener)(void* context, * * \param func The callback to be invoked when surface stats are available. */ -void ASurfaceControl_registerSurfaceStatsListener(ASurfaceControl* control, void* context, +void ASurfaceControl_registerSurfaceStatsListener(ASurfaceControl* control, int32_t id, void* context, ASurfaceControl_SurfaceStatsListener func); /** diff --git a/libs/battery/Android.bp b/libs/battery/Android.bp new file mode 100644 index 0000000000..c860324359 --- /dev/null +++ b/libs/battery/Android.bp @@ -0,0 +1,37 @@ +package { + // See: http://go/android-license-faq + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_library { + name: "libbattery", + srcs: [ + "LongArrayMultiStateCounter.cpp", + ], + shared_libs: [ + "liblog", + ], + cflags: [ + "-Werror", + "-Wall", + "-Wextra", + ], + export_include_dirs: ["."], +} + +cc_test { + name: "libbattery_test", + srcs: [ + "MultiStateCounterTest.cpp", + "LongArrayMultiStateCounterTest.cpp", + ], + static_libs: ["libbattery"], + shared_libs: [ + "liblog", + ], + cflags: [ + "-Werror", + "-Wall", + "-Wextra", + ], +} diff --git a/libs/battery/LongArrayMultiStateCounter.cpp b/libs/battery/LongArrayMultiStateCounter.cpp new file mode 100644 index 0000000000..125cfaffa4 --- /dev/null +++ b/libs/battery/LongArrayMultiStateCounter.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * Android BPF library - public API + * + * 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 "LongArrayMultiStateCounter.h" +#include <log/log.h> + +namespace android { +namespace battery { + +template <> +bool LongArrayMultiStateCounter::delta(const std::vector<uint64_t>& previousValue, + const std::vector<uint64_t>& newValue, + std::vector<uint64_t>* outValue) const { + size_t size = previousValue.size(); + if (newValue.size() != size) { + ALOGE("Incorrect array size: %d, should be %d", (int)newValue.size(), (int)size); + return false; + } + + bool is_delta_valid = true; + for (int i = size - 1; i >= 0; i--) { + if (newValue[i] >= previousValue[i]) { + (*outValue)[i] = newValue[i] - previousValue[i]; + } else { + (*outValue)[i] = 0; + is_delta_valid = false; + } + } + return is_delta_valid; +} + +template <> +void LongArrayMultiStateCounter::add(std::vector<uint64_t>* value1, + const std::vector<uint64_t>& value2, const uint64_t numerator, + const uint64_t denominator) const { + if (numerator != denominator) { + for (int i = value2.size() - 1; i >= 0; i--) { + // The caller ensures that denominator != 0 + (*value1)[i] += value2[i] * numerator / denominator; + } + } else { + for (int i = value2.size() - 1; i >= 0; i--) { + (*value1)[i] += value2[i]; + } + } +} + +template <> +std::string LongArrayMultiStateCounter::valueToString(const std::vector<uint64_t>& v) const { + std::stringstream s; + s << "{"; + bool first = true; + for (uint64_t n : v) { + if (!first) { + s << ", "; + } + s << n; + first = false; + } + s << "}"; + return s.str(); +} + +} // namespace battery +} // namespace android diff --git a/libs/battery/LongArrayMultiStateCounter.h b/libs/battery/LongArrayMultiStateCounter.h new file mode 100644 index 0000000000..f3439f6a0c --- /dev/null +++ b/libs/battery/LongArrayMultiStateCounter.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * Android BPF library - public API + * + * 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 <vector> +#include "MultiStateCounter.h" + +namespace android { +namespace battery { + +typedef MultiStateCounter<std::vector<uint64_t>> LongArrayMultiStateCounter; + +} // namespace battery +} // namespace android diff --git a/libs/battery/LongArrayMultiStateCounterTest.cpp b/libs/battery/LongArrayMultiStateCounterTest.cpp new file mode 100644 index 0000000000..e4e6b2a49f --- /dev/null +++ b/libs/battery/LongArrayMultiStateCounterTest.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * Android BPF library - public API + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include "LongArrayMultiStateCounter.h" + +namespace android { +namespace battery { + +class LongArrayMultiStateCounterTest : public testing::Test {}; + +TEST_F(LongArrayMultiStateCounterTest, stateChange) { + LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4)); + testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000); + testCounter.setState(0, 1000); + testCounter.setState(1, 2000); + testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000); + + // Time was split in half between the two states, so the counts will be split 50:50 too + EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(0)); + EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(1)); +} + +TEST_F(LongArrayMultiStateCounterTest, accumulation) { + LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4)); + testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000); + testCounter.setState(0, 1000); + testCounter.setState(1, 2000); + testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000); + testCounter.setState(0, 4000); + testCounter.updateValue(std::vector<uint64_t>({200, 300, 400, 500}), 8000); + + // The first delta is split 50:50: + // 0: {50, 100, 150, 200} + // 1: {50, 100, 150, 200} + // The second delta is split 4:1 + // 0: {80, 80, 80, 80} + // 1: {20, 20, 20, 20} + EXPECT_EQ(std::vector<uint64_t>({130, 180, 230, 280}), testCounter.getCount(0)); + EXPECT_EQ(std::vector<uint64_t>({70, 120, 170, 220}), testCounter.getCount(1)); +} + +TEST_F(LongArrayMultiStateCounterTest, toString) { + LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4)); + testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000); + testCounter.setState(0, 1000); + testCounter.setState(1, 2000); + testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000); + + EXPECT_STREQ("[0: {50, 100, 150, 200}, 1: {50, 100, 150, 200}] updated: 3000 currentState: 1", + testCounter.toString().c_str()); +} + +} // namespace battery +} // namespace android diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h new file mode 100644 index 0000000000..e1ee07ccf1 --- /dev/null +++ b/libs/battery/MultiStateCounter.h @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * Android BPF library - public API + * + * 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 <inttypes.h> +#include <log/log.h> +#include <time.h> +#include <sstream> +#include <string> + +/** + * An object that can track changes of some value over time, taking into account an additional + * dimension: the object's state. As the tracked value changes, the deltas are distributed + * among the object states in accordance with the time spent in those states. + */ +namespace android { +namespace battery { + +typedef uint16_t state_t; + +template <class T> +class MultiStateCounter { + uint16_t stateCount; + state_t currentState; + time_t lastStateChangeTimestamp; + T emptyValue; + T lastValue; + time_t lastUpdateTimestamp; + T deltaValue; + bool isEnabled; + + struct State { + time_t timeInStateSinceUpdate; + T counter; + }; + + State* states; + +public: + MultiStateCounter(uint16_t stateCount, const T& emptyValue); + + virtual ~MultiStateCounter(); + + void setEnabled(bool enabled, time_t timestamp); + + void setState(state_t state, time_t timestamp); + + void setValue(state_t state, const T& value); + + void updateValue(const T& value, time_t timestamp); + + void reset(); + + uint16_t getStateCount(); + + const T& getCount(state_t state); + + std::string toString(); + +private: + /** + * Subtracts previousValue from newValue and returns the result in outValue. + * Returns true iff the combination of previousValue and newValue is valid + * (newValue >= prevValue) + */ + bool delta(const T& previousValue, const T& newValue, T* outValue) const; + + /** + * Adds value2 to value1 and stores the result in value1. Denominator is + * guaranteed to be non-zero. + */ + void add(T* value1, const T& value2, const uint64_t numerator, + const uint64_t denominator) const; + + std::string valueToString(const T& value) const; +}; + +// ---------------------- MultiStateCounter Implementation ------------------------- +// Since MultiStateCounter is a template, the implementation must be inlined. + +template <class T> +MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue) + : stateCount(stateCount), + currentState(0), + lastStateChangeTimestamp(-1), + emptyValue(emptyValue), + lastValue(emptyValue), + lastUpdateTimestamp(-1), + deltaValue(emptyValue), + isEnabled(true) { + states = new State[stateCount]; + for (int i = 0; i < stateCount; i++) { + states[i].timeInStateSinceUpdate = 0; + states[i].counter = emptyValue; + } +} + +template <class T> +MultiStateCounter<T>::~MultiStateCounter() { + delete[] states; +}; + +template <class T> +void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) { + if (enabled == isEnabled) { + return; + } + + if (!enabled) { + // Confirm the current state for the side-effect of updating the time-in-state + // counter for the current state. + setState(currentState, timestamp); + } + + isEnabled = enabled; + + if (lastStateChangeTimestamp >= 0) { + lastStateChangeTimestamp = timestamp; + } +} + +template <class T> +void MultiStateCounter<T>::setState(state_t state, time_t timestamp) { + if (isEnabled && lastStateChangeTimestamp >= 0) { + if (timestamp >= lastStateChangeTimestamp) { + states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp; + } else { + ALOGE("setState is called with an earlier timestamp: %lu, previous timestamp: %lu\n", + (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp); + // The accumulated durations have become unreliable. For example, if the timestamp + // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas, + // we would get 4000, which is greater than (last - first). This could lead to + // counts exceeding 100%. + for (int i = 0; i < stateCount; i++) { + states[i].timeInStateSinceUpdate = 0; + } + } + } + currentState = state; + lastStateChangeTimestamp = timestamp; +} + +template <class T> +void MultiStateCounter<T>::setValue(state_t state, const T& value) { + states[state].counter = value; +} + +template <class T> +void MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) { + // If the counter is disabled, we ignore the update, except when the counter got disabled after + // the previous update, in which case we still need to pick up the residual delta. + if (isEnabled || lastUpdateTimestamp < lastStateChangeTimestamp) { + // Confirm the current state for the side-effect of updating the time-in-state + // counter for the current state. + setState(currentState, timestamp); + + if (lastUpdateTimestamp >= 0) { + if (timestamp > lastUpdateTimestamp) { + if (delta(lastValue, value, &deltaValue)) { + time_t timeSinceUpdate = timestamp - lastUpdateTimestamp; + for (int i = 0; i < stateCount; i++) { + time_t timeInState = states[i].timeInStateSinceUpdate; + if (timeInState) { + add(&states[i].counter, deltaValue, timeInState, timeSinceUpdate); + states[i].timeInStateSinceUpdate = 0; + } + } + } else { + std::stringstream str; + str << "updateValue is called with a value " << valueToString(value) + << ", which is lower than the previous value " << valueToString(lastValue) + << "\n"; + ALOGE("%s", str.str().c_str()); + } + } else if (timestamp < lastUpdateTimestamp) { + ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n", + (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp); + } + } + } + lastValue = value; + lastUpdateTimestamp = timestamp; +} + +template <class T> +void MultiStateCounter<T>::reset() { + lastStateChangeTimestamp = -1; + lastUpdateTimestamp = -1; + for (int i = 0; i < stateCount; i++) { + states[i].timeInStateSinceUpdate = 0; + states[i].counter = emptyValue; + } +} + +template <class T> +uint16_t MultiStateCounter<T>::getStateCount() { + return stateCount; +} + +template <class T> +const T& MultiStateCounter<T>::getCount(state_t state) { + return states[state].counter; +} + +template <class T> +std::string MultiStateCounter<T>::toString() { + std::stringstream str; + str << "["; + for (int i = 0; i < stateCount; i++) { + if (i != 0) { + str << ", "; + } + str << i << ": " << valueToString(states[i].counter); + if (states[i].timeInStateSinceUpdate > 0) { + str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate; + } + } + str << "]"; + if (lastUpdateTimestamp >= 0) { + str << " updated: " << lastUpdateTimestamp; + } + if (lastStateChangeTimestamp >= 0) { + str << " currentState: " << currentState; + if (lastStateChangeTimestamp > lastUpdateTimestamp) { + str << " stateChanged: " << lastStateChangeTimestamp; + } + } else { + str << " currentState: none"; + } + + return str.str(); +} + +} // namespace battery +} // namespace android diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp new file mode 100644 index 0000000000..319ba76a4f --- /dev/null +++ b/libs/battery/MultiStateCounterTest.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * Android BPF library - public API + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include "MultiStateCounter.h" + +namespace android { +namespace battery { + +typedef MultiStateCounter<double> DoubleMultiStateCounter; + +template <> +bool DoubleMultiStateCounter::delta(const double& previousValue, const double& newValue, + double* outValue) const { + *outValue = newValue - previousValue; + return *outValue >= 0; +} + +template <> +void DoubleMultiStateCounter::add(double* value1, const double& value2, const uint64_t numerator, + const uint64_t denominator) const { + if (numerator != denominator) { + // The caller ensures that denominator != 0 + *value1 += value2 * numerator / denominator; + } else { + *value1 += value2; + } +} + +template <> +std::string DoubleMultiStateCounter::valueToString(const double& v) const { + return std::to_string(v); +} + +class MultiStateCounterTest : public testing::Test {}; + +TEST_F(MultiStateCounterTest, constructor) { + DoubleMultiStateCounter testCounter(3, 0); + testCounter.updateValue(0, 0); + testCounter.setState(1, 0); + testCounter.updateValue(3.14, 3000); + + EXPECT_DOUBLE_EQ(0, testCounter.getCount(0)); + EXPECT_DOUBLE_EQ(3.14, testCounter.getCount(1)); + EXPECT_DOUBLE_EQ(0, testCounter.getCount(2)); +} + +TEST_F(MultiStateCounterTest, stateChange) { + DoubleMultiStateCounter testCounter(3, 0); + testCounter.updateValue(0, 0); + testCounter.setState(1, 0); + testCounter.setState(2, 1000); + testCounter.updateValue(6.0, 3000); + + EXPECT_DOUBLE_EQ(0, testCounter.getCount(0)); + EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1)); + EXPECT_DOUBLE_EQ(4.0, testCounter.getCount(2)); +} + +TEST_F(MultiStateCounterTest, setEnabled) { + DoubleMultiStateCounter testCounter(3, 0); + testCounter.updateValue(0, 0); + testCounter.setState(1, 0); + testCounter.setEnabled(false, 1000); + testCounter.setState(2, 2000); + testCounter.updateValue(6.0, 3000); + + // In state 1: accumulated 1000 before disabled, that's 6.0 * 1000/3000 = 2.0 + // In state 2: 0, since it is still disabled + EXPECT_DOUBLE_EQ(0, testCounter.getCount(0)); + EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1)); + EXPECT_DOUBLE_EQ(0, testCounter.getCount(2)); + + // Should have no effect since the counter is disabled + testCounter.setState(0, 3500); + + // Should have no effect since the counter is disabled + testCounter.updateValue(10.0, 4000); + + EXPECT_DOUBLE_EQ(0, testCounter.getCount(0)); + EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1)); + EXPECT_DOUBLE_EQ(0, testCounter.getCount(2)); + + testCounter.setState(2, 4500); + + // Enable the counter to partially accumulate deltas for the current state, 2 + testCounter.setEnabled(true, 5000); + testCounter.setEnabled(false, 6000); + testCounter.setEnabled(true, 7000); + testCounter.updateValue(20.0, 8000); + + // The delta is 10.0 over 5000-3000=2000. + // Counter has been enabled in state 2 for (6000-5000)+(8000-7000) = 2000, + // so its share is (20.0-10.0) * 2000/(8000-4000) = 5.0 + EXPECT_DOUBLE_EQ(0, testCounter.getCount(0)); + EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1)); + EXPECT_DOUBLE_EQ(5.0, testCounter.getCount(2)); + + testCounter.reset(); + testCounter.setState(0, 0); + testCounter.updateValue(0, 0); + testCounter.setState(1, 2000); + testCounter.setEnabled(false, 3000); + testCounter.updateValue(200, 5000); + + // 200 over 5000 = 40 per second + // Counter was in state 0 from 0 to 2000, so 2 sec, so the count should be 40 * 2 = 80 + // It stayed in state 1 from 2000 to 3000, at which point the counter was disabled, + // so the count for state 1 should be 40 * 1 = 40. + // The remaining 2 seconds from 3000 to 5000 don't count because the counter was disabled. + EXPECT_DOUBLE_EQ(80.0, testCounter.getCount(0)); + EXPECT_DOUBLE_EQ(40.0, testCounter.getCount(1)); + EXPECT_DOUBLE_EQ(0, testCounter.getCount(2)); +} + +TEST_F(MultiStateCounterTest, reset) { + DoubleMultiStateCounter testCounter(3, 0); + testCounter.updateValue(0, 0); + testCounter.setState(1, 0); + testCounter.updateValue(2.72, 3000); + + testCounter.reset(); + + EXPECT_DOUBLE_EQ(0, testCounter.getCount(0)); + EXPECT_DOUBLE_EQ(0, testCounter.getCount(1)); + EXPECT_DOUBLE_EQ(0, testCounter.getCount(2)); + + // Assert that we can still continue accumulating after a reset + testCounter.updateValue(0, 4000); + testCounter.updateValue(3.14, 5000); + + EXPECT_DOUBLE_EQ(0, testCounter.getCount(0)); + EXPECT_DOUBLE_EQ(3.14, testCounter.getCount(1)); + EXPECT_DOUBLE_EQ(0, testCounter.getCount(2)); +} + +TEST_F(MultiStateCounterTest, timeAdjustment_setState) { + DoubleMultiStateCounter testCounter(3, 0); + testCounter.updateValue(0, 0); + testCounter.setState(1, 0); + testCounter.setState(2, 2000); + + // Time moves back + testCounter.setState(1, 1000); + testCounter.updateValue(6.0, 3000); + + EXPECT_DOUBLE_EQ(0, testCounter.getCount(0)); + + // We were in state 1 from 0 to 2000, which was erased because the time moved back. + // Then from 1000 to 3000, so we expect the count to be 6 * (2000/3000) + EXPECT_DOUBLE_EQ(4.0, testCounter.getCount(1)); + + // No time was effectively accumulated for state 2, because the timestamp moved back + // while we were in state 2. + EXPECT_DOUBLE_EQ(0, testCounter.getCount(2)); +} + +TEST_F(MultiStateCounterTest, timeAdjustment_updateValue) { + DoubleMultiStateCounter testCounter(1, 0); + testCounter.updateValue(0, 0); + testCounter.setState(0, 0); + testCounter.updateValue(6.0, 2000); + + // Time moves back. The negative delta from 2000 to 1000 is ignored + testCounter.updateValue(8.0, 1000); + testCounter.updateValue(11.0, 3000); + + // The total accumulated count is: + // 6.0 // For the period 0-2000 + // +(11.0-8.0) // For the period 1000-3000 + EXPECT_DOUBLE_EQ(9.0, testCounter.getCount(0)); +} + +TEST_F(MultiStateCounterTest, toString) { + DoubleMultiStateCounter testCounter(2, 0); + + EXPECT_STREQ("[0: 0.000000, 1: 0.000000] currentState: none", testCounter.toString().c_str()); + + testCounter.updateValue(0, 0); + testCounter.setState(1, 0); + testCounter.setState(1, 2000); + EXPECT_STREQ("[0: 0.000000, 1: 0.000000 timeInStateSinceUpdate: 2000]" + " updated: 0 currentState: 1 stateChanged: 2000", + testCounter.toString().c_str()); + + testCounter.updateValue(3.14, 3000); + + EXPECT_STREQ("[0: 0.000000, 1: 3.140000] updated: 3000 currentState: 1", + testCounter.toString().c_str()); +} + +} // namespace battery +} // namespace android diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index b86a85ff58..06594d77d3 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -64,9 +64,6 @@ libbinder_device_interface_sources = [ "PermissionCache.cpp", "PermissionController.cpp", ] -libbinder_no_vendor_interface_sources = [ - ":packagemanager_aidl", -] cc_library { name: "libbinder", @@ -127,7 +124,7 @@ cc_library { "TextOutput.cpp", "Utils.cpp", ":libbinder_aidl", - ] + libbinder_no_vendor_interface_sources, + ], target: { android: { @@ -139,7 +136,7 @@ cc_library { }, }, vendor: { - exclude_srcs: libbinder_device_interface_sources + libbinder_no_vendor_interface_sources, + exclude_srcs: libbinder_device_interface_sources, }, darwin: { enabled: false, @@ -289,8 +286,11 @@ filegroup { path: "aidl", } -filegroup { +aidl_interface { name: "packagemanager_aidl", + unstable: true, + local_include_dir: "aidl", + host_supported: true, srcs: [ "aidl/android/content/pm/IPackageChangeObserver.aidl", "aidl/android/content/pm/IPackageManagerNative.aidl", @@ -299,7 +299,6 @@ filegroup { "aidl/android/content/pm/ApexStagedEvent.aidl", "aidl/android/content/pm/StagedApexInfo.aidl", ], - path: "aidl", } aidl_interface { diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index 2524c5f6d2..5a80ad067c 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -15,11 +15,13 @@ cc_test { }, srcs: [ "Flags_test.cpp", + "cast_test.cpp", + "enum_test.cpp", "future_test.cpp", - "NamedEnum_test.cpp", "small_map_test.cpp", "small_vector_test.cpp", "static_vector_test.cpp", + "string_test.cpp", ], cflags: [ "-Wall", diff --git a/libs/ftl/Flags_test.cpp b/libs/ftl/Flags_test.cpp index 8c00b5299b..d241fa272a 100644 --- a/libs/ftl/Flags_test.cpp +++ b/libs/ftl/Flags_test.cpp @@ -23,7 +23,7 @@ namespace android::test { using namespace android::flag_operators; -enum class TestFlags { ONE = 0x1, TWO = 0x2, THREE = 0x4 }; +enum class TestFlags : uint8_t { ONE = 0x1, TWO = 0x2, THREE = 0x4 }; TEST(Flags, Test) { Flags<TestFlags> flags = TestFlags::ONE; @@ -165,7 +165,7 @@ TEST(Flags, String_KnownValues) { TEST(Flags, String_UnknownValues) { auto flags = Flags<TestFlags>(0b1011); - ASSERT_EQ(flags.string(), "ONE | TWO | 0x00000008"); + ASSERT_EQ(flags.string(), "ONE | TWO | 0b1000"); } TEST(FlagsIterator, IteratesOverAllFlags) { @@ -210,18 +210,4 @@ TEST(FlagsIterator, PreFixIncrement) { ASSERT_EQ(++iter, flags.end()); } -TEST(FlagNames, RuntimeFlagName) { - TestFlags f = TestFlags::ONE; - ASSERT_EQ(flag_name(f), "ONE"); -} - -TEST(FlagNames, RuntimeUnknownFlagName) { - TestFlags f = static_cast<TestFlags>(0x8); - ASSERT_EQ(flag_name(f), std::nullopt); -} - -TEST(FlagNames, CompileTimeFlagName) { - static_assert(flag_name<TestFlags::TWO>() == "TWO"); -} - -} // namespace android::test
\ No newline at end of file +} // namespace android::test diff --git a/libs/ftl/NamedEnum_test.cpp b/libs/ftl/NamedEnum_test.cpp deleted file mode 100644 index dff2b8aaa1..0000000000 --- a/libs/ftl/NamedEnum_test.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <gtest/gtest.h> -#include <ftl/NamedEnum.h> - -namespace android { - -// Test enum class maximum enum value smaller than default maximum of 8. -enum class TestEnums { ZERO = 0x0, ONE = 0x1, TWO = 0x2, THREE = 0x3, SEVEN = 0x7 }; -// Big enum contains enum values greater than default maximum of 8. -enum class TestBigEnums { ZERO = 0x0, FIFTEEN = 0xF }; - -// Declared to specialize the maximum enum since the enum size exceeds 8 by default. -template <> -constexpr size_t NamedEnum::max<TestBigEnums> = 16; - -namespace test { -using android::TestBigEnums; -using android::TestEnums; - -TEST(NamedEnum, RuntimeNamedEnum) { - TestEnums e = TestEnums::ZERO; - ASSERT_EQ(NamedEnum::enum_name(e), "ZERO"); - - e = TestEnums::ONE; - ASSERT_EQ(NamedEnum::enum_name(e), "ONE"); - - e = TestEnums::THREE; - ASSERT_EQ(NamedEnum::enum_name(e), "THREE"); - - e = TestEnums::SEVEN; - ASSERT_EQ(NamedEnum::enum_name(e), "SEVEN"); -} - -// Test big enum -TEST(NamedEnum, RuntimeBigNamedEnum) { - TestBigEnums e = TestBigEnums::ZERO; - ASSERT_EQ(NamedEnum::enum_name(e), "ZERO"); - - e = TestBigEnums::FIFTEEN; - ASSERT_EQ(NamedEnum::enum_name(e), "FIFTEEN"); -} - -TEST(NamedEnum, RuntimeNamedEnumAsString) { - TestEnums e = TestEnums::ZERO; - ASSERT_EQ(NamedEnum::string(e), "ZERO"); - - e = TestEnums::ONE; - ASSERT_EQ(NamedEnum::string(e), "ONE"); - - e = TestEnums::THREE; - ASSERT_EQ(NamedEnum::string(e), "THREE"); - - e = TestEnums::SEVEN; - ASSERT_EQ(NamedEnum::string(e), "SEVEN"); -} - -TEST(NamedEnum, RuntimeBigNamedEnumAsString) { - TestBigEnums e = TestBigEnums::ZERO; - ASSERT_EQ(NamedEnum::string(e), "ZERO"); - - e = TestBigEnums::FIFTEEN; - ASSERT_EQ(NamedEnum::string(e), "FIFTEEN"); -} - -TEST(NamedEnum, RuntimeUnknownNamedEnum) { - TestEnums e = static_cast<TestEnums>(0x5); - ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt); - e = static_cast<TestEnums>(0x9); - ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt); -} - -TEST(NamedEnum, RuntimeUnknownNamedEnumAsString) { - TestEnums e = static_cast<TestEnums>(0x5); - ASSERT_EQ(NamedEnum::string(e), "05"); - e = static_cast<TestEnums>(0x9); - ASSERT_EQ(NamedEnum::string(e, "0x%08x"), "0x00000009"); -} - -TEST(NamedEnum, CompileTimeFlagName) { - static_assert(NamedEnum::enum_name<TestEnums::TWO>() == "TWO"); - static_assert(NamedEnum::enum_name<TestEnums::THREE>() == "THREE"); -} - -} // namespace test - -} // namespace android diff --git a/libs/ftl/cast_test.cpp b/libs/ftl/cast_test.cpp new file mode 100644 index 0000000000..2abcb8fe66 --- /dev/null +++ b/libs/ftl/cast_test.cpp @@ -0,0 +1,200 @@ +/* + * Copyright 2021 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/cast.h> +#include <gtest/gtest.h> + +#include <cfloat> +#include <cmath> +#include <limits> + +namespace android::test { + +using ftl::cast_safety; +using ftl::CastSafety; + +template <typename T> +constexpr T min = std::numeric_limits<T>::lowest(); + +template <typename T> +constexpr T max = std::numeric_limits<T>::max(); + +template <typename T> +constexpr T inf = std::numeric_limits<T>::infinity(); + +template <typename T> +constexpr T NaN = std::numeric_limits<T>::quiet_NaN(); + +// Keep in sync with example usage in header file. + +static_assert(cast_safety<uint8_t>(-1) == CastSafety::kUnderflow); +static_assert(cast_safety<int8_t>(128u) == CastSafety::kOverflow); + +static_assert(cast_safety<uint32_t>(-.1f) == CastSafety::kUnderflow); +static_assert(cast_safety<int32_t>(static_cast<float>(INT32_MAX)) == CastSafety::kOverflow); + +static_assert(cast_safety<float>(-DBL_MAX) == CastSafety::kUnderflow); + +// Unsigned to unsigned. + +static_assert(cast_safety<uint8_t>(0u) == CastSafety::kSafe); +static_assert(cast_safety<uint16_t>(max<uint8_t>) == CastSafety::kSafe); +static_assert(cast_safety<uint8_t>(static_cast<uint32_t>(max<uint8_t>)) == CastSafety::kSafe); + +static_assert(cast_safety<uint32_t>(max<uint64_t>) == CastSafety::kOverflow); +static_assert(cast_safety<uint8_t>(static_cast<uint32_t>(max<uint8_t>) + 1) == + CastSafety::kOverflow); + +// Unsigned to signed. + +static_assert(cast_safety<int16_t>(0u) == CastSafety::kSafe); +static_assert(cast_safety<int16_t>(max<uint8_t>) == CastSafety::kSafe); +static_assert(cast_safety<int16_t>(max<uint16_t>) == CastSafety::kOverflow); + +static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>) - 1) == CastSafety::kSafe); +static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>)) == CastSafety::kSafe); +static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>) + 1) == + CastSafety::kOverflow); + +// Signed to unsigned. + +static_assert(cast_safety<uint16_t>(0) == CastSafety::kSafe); +static_assert(cast_safety<uint16_t>(max<int8_t>) == CastSafety::kSafe); +static_assert(cast_safety<uint16_t>(max<int16_t>) == CastSafety::kSafe); + +static_assert(cast_safety<uint32_t>(-1) == CastSafety::kUnderflow); +static_assert(cast_safety<uint32_t>(max<int64_t>) == CastSafety::kOverflow); + +static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>) - 1) == CastSafety::kSafe); +static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>)) == CastSafety::kSafe); +static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>) + 1) == + CastSafety::kOverflow); + +// Signed to signed. + +static_assert(cast_safety<int8_t>(-129) == CastSafety::kUnderflow); +static_assert(cast_safety<int8_t>(-128) == CastSafety::kSafe); +static_assert(cast_safety<int8_t>(127) == CastSafety::kSafe); +static_assert(cast_safety<int8_t>(128) == CastSafety::kOverflow); + +static_assert(cast_safety<int32_t>(static_cast<int64_t>(min<int32_t>)) == CastSafety::kSafe); +static_assert(cast_safety<int32_t>(static_cast<int64_t>(max<int32_t>)) == CastSafety::kSafe); + +static_assert(cast_safety<int16_t>(min<int32_t>) == CastSafety::kUnderflow); +static_assert(cast_safety<int32_t>(max<int64_t>) == CastSafety::kOverflow); + +// Float to float. + +static_assert(cast_safety<double>(max<float>) == CastSafety::kSafe); +static_assert(cast_safety<double>(min<float>) == CastSafety::kSafe); + +static_assert(cast_safety<float>(min<double>) == CastSafety::kUnderflow); +static_assert(cast_safety<float>(max<double>) == CastSafety::kOverflow); + +TEST(CastSafety, FloatToFloat) { + EXPECT_EQ(cast_safety<float>(std::nexttoward(static_cast<double>(min<float>), min<double>)), + CastSafety::kUnderflow); + EXPECT_EQ(cast_safety<float>(std::nexttoward(static_cast<double>(max<float>), max<double>)), + CastSafety::kOverflow); +} + +// Unsigned to float. + +static_assert(cast_safety<float>(0u) == CastSafety::kSafe); +static_assert(cast_safety<float>(max<uint64_t>) == CastSafety::kSafe); + +static_assert(cast_safety<double>(0u) == CastSafety::kSafe); +static_assert(cast_safety<double>(max<uint64_t>) == CastSafety::kSafe); + +// Signed to float. + +static_assert(cast_safety<float>(min<int64_t>) == CastSafety::kSafe); +static_assert(cast_safety<float>(max<int64_t>) == CastSafety::kSafe); + +static_assert(cast_safety<double>(min<int64_t>) == CastSafety::kSafe); +static_assert(cast_safety<double>(max<int64_t>) == CastSafety::kSafe); + +// Float to unsigned. + +static_assert(cast_safety<uint32_t>(0.f) == CastSafety::kSafe); +static_assert(cast_safety<uint32_t>(min<float>) == CastSafety::kUnderflow); +static_assert(cast_safety<uint32_t>(max<float>) == CastSafety::kOverflow); +static_assert(cast_safety<uint32_t>(-.1f) == CastSafety::kUnderflow); + +static_assert(cast_safety<uint16_t>(-inf<float>) == CastSafety::kUnderflow); +static_assert(cast_safety<uint32_t>(inf<float>) == CastSafety::kOverflow); +static_assert(cast_safety<uint64_t>(NaN<float>) == CastSafety::kOverflow); + +static_assert(cast_safety<uint32_t>(static_cast<float>(max<int32_t>)) == CastSafety::kSafe); +static_assert(cast_safety<uint32_t>(static_cast<float>(max<uint32_t>)) == CastSafety::kOverflow); +static_assert(cast_safety<uint32_t>(static_cast<double>(max<int32_t>)) == CastSafety::kSafe); +static_assert(cast_safety<uint32_t>(static_cast<double>(max<uint32_t>)) == CastSafety::kSafe); + +static_assert(cast_safety<uint64_t>(0.0) == CastSafety::kSafe); +static_assert(cast_safety<uint64_t>(min<double>) == CastSafety::kUnderflow); +static_assert(cast_safety<uint64_t>(max<double>) == CastSafety::kOverflow); +static_assert(cast_safety<uint64_t>(-.1) == CastSafety::kUnderflow); + +static_assert(cast_safety<uint64_t>(static_cast<float>(max<int64_t>)) == CastSafety::kSafe); +static_assert(cast_safety<uint64_t>(static_cast<float>(max<uint64_t>)) == CastSafety::kOverflow); +static_assert(cast_safety<uint64_t>(static_cast<double>(max<int64_t>)) == CastSafety::kSafe); +static_assert(cast_safety<uint64_t>(static_cast<double>(max<uint64_t>)) == CastSafety::kOverflow); + +// Float to signed. + +static_assert(cast_safety<int32_t>(0.f) == CastSafety::kSafe); +static_assert(cast_safety<int32_t>(min<float>) == CastSafety::kUnderflow); +static_assert(cast_safety<int32_t>(max<float>) == CastSafety::kOverflow); + +static_assert(cast_safety<int16_t>(-inf<double>) == CastSafety::kUnderflow); +static_assert(cast_safety<int32_t>(inf<double>) == CastSafety::kOverflow); +static_assert(cast_safety<int64_t>(NaN<double>) == CastSafety::kOverflow); + +static_assert(cast_safety<int32_t>(static_cast<float>(min<int32_t>)) == CastSafety::kSafe); +static_assert(cast_safety<int32_t>(static_cast<float>(max<int32_t>)) == CastSafety::kOverflow); +static_assert(cast_safety<int32_t>(static_cast<double>(min<int32_t>)) == CastSafety::kSafe); +static_assert(cast_safety<int32_t>(static_cast<double>(max<int32_t>)) == CastSafety::kSafe); + +static_assert(cast_safety<int64_t>(0.0) == CastSafety::kSafe); +static_assert(cast_safety<int64_t>(min<double>) == CastSafety::kUnderflow); +static_assert(cast_safety<int64_t>(max<double>) == CastSafety::kOverflow); + +static_assert(cast_safety<int64_t>(static_cast<float>(min<int64_t>)) == CastSafety::kSafe); +static_assert(cast_safety<int64_t>(static_cast<float>(max<int64_t>)) == CastSafety::kOverflow); +static_assert(cast_safety<int64_t>(static_cast<double>(min<int64_t>)) == CastSafety::kSafe); +static_assert(cast_safety<int64_t>(static_cast<double>(max<int64_t>)) == CastSafety::kOverflow); + +TEST(CastSafety, FloatToSigned) { + constexpr int32_t kMax = ftl::details::safe_limits<int32_t, float>::max(); + static_assert(kMax == 2'147'483'520); + EXPECT_EQ(kMax, static_cast<int32_t>(std::nexttowardf(max<int32_t>, 0))); + + EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(min<int32_t>, 0)), CastSafety::kSafe); + EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(max<int32_t>, 0)), CastSafety::kSafe); + EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(min<int64_t>, 0)), CastSafety::kSafe); + EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(max<int64_t>, 0)), CastSafety::kSafe); + + EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(min<int32_t>, min<float>)), + CastSafety::kUnderflow); + EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(max<int32_t>, max<float>)), + CastSafety::kOverflow); + EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(min<int64_t>, min<double>)), + CastSafety::kUnderflow); + EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(max<int64_t>, max<double>)), + CastSafety::kOverflow); +} + +} // namespace android::test diff --git a/libs/ftl/enum_test.cpp b/libs/ftl/enum_test.cpp new file mode 100644 index 0000000000..1fd43abbc6 --- /dev/null +++ b/libs/ftl/enum_test.cpp @@ -0,0 +1,164 @@ +/* + * Copyright 2021 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/enum.h> +#include <gtest/gtest.h> + +namespace android::test { + +// Keep in sync with example usage in header file. +namespace { + +enum class E { A, B, C, F = 5, ftl_last = F }; + +static_assert(ftl::enum_begin_v<E> == E::A); +static_assert(ftl::enum_last_v<E> == E::F); +static_assert(ftl::enum_size_v<E> == 6); + +static_assert(ftl::enum_name<E::B>() == "B"); +static_assert(ftl::enum_name<E::ftl_last>() == "F"); +static_assert(ftl::enum_name(E::C).value_or("?") == "C"); +static_assert(ftl::enum_name(E{3}).value_or("?") == "?"); + +enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; + +static_assert(ftl::enum_begin_v<F> == F{0}); +static_assert(ftl::enum_last_v<F> == F{15}); +static_assert(ftl::enum_size_v<F> == 16); + +static_assert(ftl::flag_name(F::Z).value_or("?") == "Z"); +static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?"); + +// If a scoped enum is unsigned, its implicit range corresponds to its bit indices. +enum class Flags : std::uint8_t { + kNone = 0, + kFlag1 = 0b0000'0010, + kFlag4 = 0b0001'0000, + kFlag7 = 0b1000'0000, + kMask = kFlag1 | kFlag4 | kFlag7, + kAll = 0b1111'1111 +}; + +static_assert(ftl::enum_begin_v<Flags> == Flags{0}); +static_assert(ftl::enum_last_v<Flags> == Flags{7}); +static_assert(ftl::enum_size_v<Flags> == 8); + +static_assert(ftl::enum_name<Flags::kNone>() == "kNone"); +static_assert(ftl::enum_name<Flags::kFlag4>() == "kFlag4"); +static_assert(ftl::enum_name<Flags::kFlag7>() == "kFlag7"); + +// Though not flags, the enumerators are within the implicit range of bit indices. +enum class Planet : std::uint8_t { + kMercury, + kVenus, + kEarth, + kMars, + kJupiter, + kSaturn, + kUranus, + kNeptune +}; + +constexpr Planet kPluto{ftl::enum_cast(Planet::kNeptune) + 1}; // Honorable mention. + +static_assert(ftl::enum_begin_v<Planet> == Planet::kMercury); +static_assert(ftl::enum_last_v<Planet> == Planet::kNeptune); +static_assert(ftl::enum_size_v<Planet> == 8); + +static_assert(ftl::enum_name<Planet::kMercury>() == "kMercury"); +static_assert(ftl::enum_name<Planet::kSaturn>() == "kSaturn"); + +// Unscoped enum must define explicit range, even if the underlying type is fixed. +enum Temperature : int { + kRoom = 20, + kFridge = 4, + kFreezer = -18, + + ftl_first = kFreezer, + ftl_last = kRoom +}; + +static_assert(ftl::enum_begin_v<Temperature> == kFreezer); +static_assert(ftl::enum_last_v<Temperature> == kRoom); +static_assert(ftl::enum_size_v<Temperature> == 39); + +static_assert(ftl::enum_name<kFreezer>() == "kFreezer"); +static_assert(ftl::enum_name<kFridge>() == "kFridge"); +static_assert(ftl::enum_name<kRoom>() == "kRoom"); + +} // namespace + +TEST(Enum, Range) { + std::string string; + for (E v : ftl::enum_range<E>()) { + string += ftl::enum_name(v).value_or("?"); + } + EXPECT_EQ(string, "ABC??F"); +} + +TEST(Enum, Name) { + { + EXPECT_EQ(ftl::flag_name(Flags::kFlag1), "kFlag1"); + EXPECT_EQ(ftl::flag_name(Flags::kFlag7), "kFlag7"); + + EXPECT_EQ(ftl::flag_name(Flags::kNone), std::nullopt); + EXPECT_EQ(ftl::flag_name(Flags::kMask), std::nullopt); + EXPECT_EQ(ftl::flag_name(Flags::kAll), std::nullopt); + } + { + EXPECT_EQ(ftl::enum_name(Planet::kEarth), "kEarth"); + EXPECT_EQ(ftl::enum_name(Planet::kNeptune), "kNeptune"); + + EXPECT_EQ(ftl::enum_name(kPluto), std::nullopt); + } + { + EXPECT_EQ(ftl::enum_name(kRoom), "kRoom"); + EXPECT_EQ(ftl::enum_name(kFridge), "kFridge"); + EXPECT_EQ(ftl::enum_name(kFreezer), "kFreezer"); + + EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(-30)), std::nullopt); + EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(0)), std::nullopt); + EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(100)), std::nullopt); + } +} + +TEST(Enum, String) { + { + EXPECT_EQ(ftl::flag_string(Flags::kFlag1), "kFlag1"); + EXPECT_EQ(ftl::flag_string(Flags::kFlag7), "kFlag7"); + + EXPECT_EQ(ftl::flag_string(Flags::kNone), "0b0"); + EXPECT_EQ(ftl::flag_string(Flags::kMask), "0b10010010"); + EXPECT_EQ(ftl::flag_string(Flags::kAll), "0b11111111"); + } + { + EXPECT_EQ(ftl::enum_string(Planet::kEarth), "kEarth"); + EXPECT_EQ(ftl::enum_string(Planet::kNeptune), "kNeptune"); + + EXPECT_EQ(ftl::enum_string(kPluto), "8"); + } + { + EXPECT_EQ(ftl::enum_string(kRoom), "kRoom"); + EXPECT_EQ(ftl::enum_string(kFridge), "kFridge"); + EXPECT_EQ(ftl::enum_string(kFreezer), "kFreezer"); + + EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(-30)), "-30"); + EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(0)), "0"); + EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(100)), "100"); + } +} + +} // namespace android::test diff --git a/libs/ftl/small_map_test.cpp b/libs/ftl/small_map_test.cpp index 323b9f91e7..2e81022f38 100644 --- a/libs/ftl/small_map_test.cpp +++ b/libs/ftl/small_map_test.cpp @@ -18,6 +18,9 @@ #include <gtest/gtest.h> #include <cctype> +#include <string> + +using namespace std::string_literals; namespace android::test { @@ -35,16 +38,19 @@ TEST(SmallMap, Example) { EXPECT_TRUE(map.contains(123)); - EXPECT_EQ(map.find(42, [](const std::string& s) { return s.size(); }), 3u); + EXPECT_EQ(map.get(42, [](const std::string& s) { return s.size(); }), 3u); - const auto opt = map.find(-1); + const auto opt = map.get(-1); ASSERT_TRUE(opt); std::string& ref = *opt; EXPECT_TRUE(ref.empty()); ref = "xyz"; - EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc"))); + map.emplace_or_replace(0, "vanilla", 2u, 3u); + EXPECT_TRUE(map.dynamic()); + + EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc"))); } TEST(SmallMap, Construct) { @@ -90,42 +96,253 @@ TEST(SmallMap, Construct) { } } +TEST(SmallMap, UniqueKeys) { + { + // Duplicate mappings are discarded. + const SmallMap map = ftl::init::map<int, float>(1)(2)(3)(2)(3)(1)(3)(2)(1); + + EXPECT_EQ(map.size(), 3u); + EXPECT_EQ(map.max_size(), 9u); + + using Map = decltype(map); + EXPECT_EQ(map, Map(ftl::init::map(1, 0.f)(2, 0.f)(3, 0.f))); + } + { + // Duplicate mappings may be reordered. + const SmallMap map = ftl::init::map('a', 'A')( + 'b', 'B')('b')('b')('c', 'C')('a')('d')('c')('e', 'E')('d', 'D')('a')('f', 'F'); + + EXPECT_EQ(map.size(), 6u); + EXPECT_EQ(map.max_size(), 12u); + + using Map = decltype(map); + EXPECT_EQ(map, Map(ftl::init::map('a', 'A')('b', 'B')('c', 'C')('d', 'D')('e', 'E')('f', 'F'))); + } +} + TEST(SmallMap, Find) { { // Constant reference. - const ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); + const SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); - const auto opt = map.find('b'); + const auto opt = map.get('b'); EXPECT_EQ(opt, 'B'); const char d = 'D'; - const auto ref = map.find('d').value_or(std::cref(d)); + const auto ref = map.get('d').value_or(std::cref(d)); EXPECT_EQ(ref.get(), 'D'); } { // Mutable reference. - ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); + SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); - const auto opt = map.find('c'); + const auto opt = map.get('c'); EXPECT_EQ(opt, 'C'); char d = 'd'; - const auto ref = map.find('d').value_or(std::ref(d)); + const auto ref = map.get('d').value_or(std::ref(d)); ref.get() = 'D'; EXPECT_EQ(d, 'D'); } { // Constant unary operation. - const ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); - EXPECT_EQ(map.find('c', [](char c) { return std::toupper(c); }), 'Z'); + const SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); + EXPECT_EQ(map.get('c', [](char c) { return std::toupper(c); }), 'Z'); } { // Mutable unary operation. - ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); - EXPECT_TRUE(map.find('c', [](char& c) { c = std::toupper(c); })); + SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); + EXPECT_TRUE(map.get('c', [](char& c) { c = std::toupper(c); })); EXPECT_EQ(map, SmallMap(ftl::init::map('c', 'Z')('b', 'y')('a', 'x'))); } } +TEST(SmallMap, TryEmplace) { + SmallMap<int, std::string, 3> map; + using Pair = decltype(map)::value_type; + + { + const auto [it, ok] = map.try_emplace(123, "abc"); + ASSERT_TRUE(ok); + EXPECT_EQ(*it, Pair(123, "abc"s)); + } + { + const auto [it, ok] = map.try_emplace(42, 3u, '?'); + ASSERT_TRUE(ok); + EXPECT_EQ(*it, Pair(42, "???"s)); + } + { + const auto [it, ok] = map.try_emplace(-1); + ASSERT_TRUE(ok); + EXPECT_EQ(*it, Pair(-1, std::string())); + EXPECT_FALSE(map.dynamic()); + } + { + // Insertion fails if mapping exists. + const auto [it, ok] = map.try_emplace(42, "!!!"); + EXPECT_FALSE(ok); + EXPECT_EQ(*it, Pair(42, "???")); + EXPECT_FALSE(map.dynamic()); + } + { + // Insertion at capacity promotes the map. + const auto [it, ok] = map.try_emplace(999, "xyz"); + ASSERT_TRUE(ok); + EXPECT_EQ(*it, Pair(999, "xyz")); + EXPECT_TRUE(map.dynamic()); + } + + EXPECT_EQ(map, SmallMap(ftl::init::map(-1, ""s)(42, "???"s)(123, "abc"s)(999, "xyz"s))); +} + +namespace { + +// The mapped type does not require a copy/move assignment operator. +struct String { + template <typename... Args> + String(Args... args) : str(args...) {} + const std::string str; + + bool operator==(const String& other) const { return other.str == str; } +}; + +} // namespace + +TEST(SmallMap, TryReplace) { + SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B"); + using Pair = decltype(map)::value_type; + + { + // Replacing fails unless mapping exists. + const auto it = map.try_replace(3, "c"); + EXPECT_EQ(it, map.end()); + } + { + // Replacement arguments can refer to the replaced mapping. + const auto ref = map.get(2, [](const auto& s) { return s.str[0]; }); + ASSERT_TRUE(ref); + + // Construct std::string from one character. + const auto it = map.try_replace(2, 1u, static_cast<char>(std::tolower(*ref))); + ASSERT_NE(it, map.end()); + EXPECT_EQ(*it, Pair(2, "b")); + } + + EXPECT_FALSE(map.dynamic()); + EXPECT_TRUE(map.try_emplace(3, "abc").second); + EXPECT_TRUE(map.try_emplace(4, "d").second); + EXPECT_TRUE(map.dynamic()); + + { + // Replacing fails unless mapping exists. + const auto it = map.try_replace(5, "e"); + EXPECT_EQ(it, map.end()); + } + { + // Replacement arguments can refer to the replaced mapping. + const auto ref = map.get(3); + ASSERT_TRUE(ref); + + // Construct std::string from substring. + const auto it = map.try_replace(3, ref->get().str, 2u, 1u); + ASSERT_NE(it, map.end()); + EXPECT_EQ(*it, Pair(3, "c")); + } + + EXPECT_EQ(map, SmallMap(ftl::init::map(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s))); +} + +TEST(SmallMap, EmplaceOrReplace) { + SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B"); + using Pair = decltype(map)::value_type; + + { + // New mapping is emplaced. + const auto [it, emplace] = map.emplace_or_replace(3, "c"); + EXPECT_TRUE(emplace); + EXPECT_EQ(*it, Pair(3, "c")); + } + { + // Replacement arguments can refer to the replaced mapping. + const auto ref = map.get(2, [](const auto& s) { return s.str[0]; }); + ASSERT_TRUE(ref); + + // Construct std::string from one character. + const auto [it, emplace] = map.emplace_or_replace(2, 1u, static_cast<char>(std::tolower(*ref))); + EXPECT_FALSE(emplace); + EXPECT_EQ(*it, Pair(2, "b")); + } + + EXPECT_FALSE(map.dynamic()); + EXPECT_FALSE(map.emplace_or_replace(3, "abc").second); // Replace. + EXPECT_TRUE(map.emplace_or_replace(4, "d").second); // Emplace. + EXPECT_TRUE(map.dynamic()); + + { + // New mapping is emplaced. + const auto [it, emplace] = map.emplace_or_replace(5, "e"); + EXPECT_TRUE(emplace); + EXPECT_EQ(*it, Pair(5, "e")); + } + { + // Replacement arguments can refer to the replaced mapping. + const auto ref = map.get(3); + ASSERT_TRUE(ref); + + // Construct std::string from substring. + const auto [it, emplace] = map.emplace_or_replace(3, ref->get().str, 2u, 1u); + EXPECT_FALSE(emplace); + EXPECT_EQ(*it, Pair(3, "c")); + } + + EXPECT_EQ(map, SmallMap(ftl::init::map(5, "e"s)(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s))); +} + +TEST(SmallMap, Erase) { + { + SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3')(4, '4'); + EXPECT_FALSE(map.dynamic()); + + EXPECT_FALSE(map.erase(0)); // Key not found. + + EXPECT_TRUE(map.erase(2)); + EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4'))); + + EXPECT_TRUE(map.erase(1)); + EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4'))); + + EXPECT_TRUE(map.erase(4)); + EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3'))); + + EXPECT_TRUE(map.erase(3)); + EXPECT_FALSE(map.erase(3)); // Key not found. + + EXPECT_TRUE(map.empty()); + EXPECT_FALSE(map.dynamic()); + } + { + SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3'); + map.try_emplace(4, '4'); + EXPECT_TRUE(map.dynamic()); + + EXPECT_FALSE(map.erase(0)); // Key not found. + + EXPECT_TRUE(map.erase(2)); + EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4'))); + + EXPECT_TRUE(map.erase(1)); + EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4'))); + + EXPECT_TRUE(map.erase(4)); + EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3'))); + + EXPECT_TRUE(map.erase(3)); + EXPECT_FALSE(map.erase(3)); // Key not found. + + EXPECT_TRUE(map.empty()); + EXPECT_TRUE(map.dynamic()); + } +} + } // namespace android::test diff --git a/libs/ftl/string_test.cpp b/libs/ftl/string_test.cpp new file mode 100644 index 0000000000..f3d85c8319 --- /dev/null +++ b/libs/ftl/string_test.cpp @@ -0,0 +1,187 @@ +/* + * Copyright 2021 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/string.h> +#include <gtest/gtest.h> + +#include <algorithm> +#include <cstdint> +#include <iterator> +#include <limits> +#include <sstream> +#include <type_traits> + +namespace android::test { + +// Keep in sync with example usage in header file. +TEST(String, ToChars) { + ftl::to_chars_buffer_t<> buffer; + + EXPECT_EQ(ftl::to_chars(buffer, 123u), "123"); + EXPECT_EQ(ftl::to_chars(buffer, -42, ftl::Radix::kBin), "-0b101010"); + EXPECT_EQ(ftl::to_chars(buffer, 0xcafe, ftl::Radix::kHex), "0xcafe"); + EXPECT_EQ(ftl::to_chars(buffer, '*', ftl::Radix::kHex), "0x2a"); +} + +namespace { + +template <typename F, typename T> +void ToCharsTest() { + constexpr auto kRadix = F::kRadix; + + using Limits = std::numeric_limits<T>; + constexpr auto kMin = Limits::min(); + constexpr auto kMax = Limits::max(); + constexpr auto kNeg = static_cast<T>(-42); + constexpr auto kPos = static_cast<T>(123); + + ftl::to_chars_buffer_t<T> buffer; + + EXPECT_EQ(ftl::to_chars(buffer, kMin, kRadix), F{}(kMin)); + EXPECT_EQ(ftl::to_chars(buffer, kMax, kRadix), F{}(kMax)); + EXPECT_EQ(ftl::to_chars(buffer, kNeg, kRadix), F{}(kNeg)); + EXPECT_EQ(ftl::to_chars(buffer, kPos, kRadix), F{}(kPos)); +} + +template <typename...> +struct Types {}; + +template <typename F, typename Types> +struct ToCharsTests; + +template <typename F, typename T, typename... Ts> +struct ToCharsTests<F, Types<T, Ts...>> { + static void test() { + ToCharsTest<F, T>(); + ToCharsTests<F, Types<Ts...>>::test(); + } +}; + +template <typename F> +struct ToCharsTests<F, Types<>> { + static void test() {} +}; + +template <typename T, typename U = std::make_unsigned_t<T>> +U to_unsigned(std::ostream& stream, T v) { + if (std::is_same_v<T, U>) return v; + + if (v < 0) { + stream << '-'; + return std::numeric_limits<U>::max() - static_cast<U>(v) + 1; + } else { + return static_cast<U>(v); + } +} + +struct Bin { + static constexpr auto kRadix = ftl::Radix::kBin; + + template <typename T> + std::string operator()(T v) const { + std::ostringstream stream; + auto u = to_unsigned(stream, v); + stream << "0b"; + + if (u == 0) { + stream << 0; + } else { + std::ostringstream digits; + do { + digits << (u & 1); + } while (u >>= 1); + + const auto str = digits.str(); + std::copy(str.rbegin(), str.rend(), std::ostream_iterator<char>(stream)); + } + + return stream.str(); + } +}; + +struct Dec { + static constexpr auto kRadix = ftl::Radix::kDec; + + template <typename T> + std::string operator()(T v) const { + return std::to_string(v); + } +}; + +struct Hex { + static constexpr auto kRadix = ftl::Radix::kHex; + + template <typename T> + std::string operator()(T v) const { + std::ostringstream stream; + const auto u = to_unsigned(stream, v); + stream << "0x" << std::hex << std::nouppercase; + stream << (sizeof(T) == 1 ? static_cast<unsigned>(u) : u); + return stream.str(); + } +}; + +using IntegerTypes = + Types<char, unsigned char, signed char, std::uint8_t, std::uint16_t, std::uint32_t, + std::uint64_t, std::int8_t, std::int16_t, std::int32_t, std::int64_t>; + +} // namespace + +TEST(String, ToCharsBin) { + ToCharsTests<Bin, IntegerTypes>::test(); + + { + const std::uint8_t x = 0b1111'1111; + ftl::to_chars_buffer_t<decltype(x)> buffer; + EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kBin), "0b11111111"); + } + { + const std::int16_t x = -0b1000'0000'0000'0000; + ftl::to_chars_buffer_t<decltype(x)> buffer; + EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kBin), "-0b1000000000000000"); + } +} + +TEST(String, ToCharsDec) { + ToCharsTests<Dec, IntegerTypes>::test(); + + { + const std::uint32_t x = UINT32_MAX; + ftl::to_chars_buffer_t<decltype(x)> buffer; + EXPECT_EQ(ftl::to_chars(buffer, x), "4294967295"); + } + { + const std::int32_t x = INT32_MIN; + ftl::to_chars_buffer_t<decltype(x)> buffer; + EXPECT_EQ(ftl::to_chars(buffer, x), "-2147483648"); + } +} + +TEST(String, ToCharsHex) { + ToCharsTests<Hex, IntegerTypes>::test(); + + { + const std::uint16_t x = 0xfade; + ftl::to_chars_buffer_t<decltype(x)> buffer; + EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kHex), "0xfade"); + } + { + ftl::to_chars_buffer_t<> buffer; + EXPECT_EQ(ftl::to_chars(buffer, INT64_MIN, ftl::Radix::kHex), "-0x8000000000000000"); + } +} + +} // namespace android::test diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 326da3a7b5..2d1f5a1694 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -65,11 +65,13 @@ cc_library_static { host_supported: true, srcs: [ ":guiconstants_aidl", + "android/gui/DisplayInfo.aidl", "android/gui/FocusRequest.aidl", "android/gui/InputApplicationInfo.aidl", "android/gui/IWindowInfosListener.aidl", "android/gui/IWindowInfosReportedListener.aidl", "android/gui/WindowInfo.aidl", + "DisplayInfo.cpp", "WindowInfo.cpp", ], @@ -90,7 +92,7 @@ cc_library_static { ], aidl: { - export_aidl_headers: true + export_aidl_headers: true, }, include_dirs: [ @@ -135,8 +137,8 @@ cc_library_static { ], aidl: { - export_aidl_headers: true - } + export_aidl_headers: true, + }, } cc_library_shared { diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index d1f57b03bc..379b090c5b 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -508,21 +508,7 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { } } - auto mergeTransaction = - [&t, currentFrameNumber = bufferItem.mFrameNumber]( - std::tuple<uint64_t, SurfaceComposerClient::Transaction> pendingTransaction) { - auto& [targetFrameNumber, transaction] = pendingTransaction; - if (currentFrameNumber < targetFrameNumber) { - return false; - } - t->merge(std::move(transaction)); - return true; - }; - - mPendingTransactions.erase(std::remove_if(mPendingTransactions.begin(), - mPendingTransactions.end(), mergeTransaction), - mPendingTransactions.end()); - + mergePendingTransactions(t, bufferItem.mFrameNumber); if (applyTransaction) { t->setApplyToken(mApplyToken).apply(); } @@ -726,6 +712,32 @@ void BLASTBufferQueue::mergeWithNextTransaction(SurfaceComposerClient::Transacti } } +void BLASTBufferQueue::applyPendingTransactions(uint64_t frameNumber) { + std::lock_guard _lock{mMutex}; + + SurfaceComposerClient::Transaction t; + mergePendingTransactions(&t, frameNumber); + t.setApplyToken(mApplyToken).apply(); +} + +void BLASTBufferQueue::mergePendingTransactions(SurfaceComposerClient::Transaction* t, + uint64_t frameNumber) { + auto mergeTransaction = + [&t, currentFrameNumber = frameNumber]( + std::tuple<uint64_t, SurfaceComposerClient::Transaction> pendingTransaction) { + auto& [targetFrameNumber, transaction] = pendingTransaction; + if (currentFrameNumber < targetFrameNumber) { + return false; + } + t->merge(std::move(transaction)); + return true; + }; + + mPendingTransactions.erase(std::remove_if(mPendingTransactions.begin(), + mPendingTransactions.end(), mergeTransaction), + mPendingTransactions.end()); +} + // Maintains a single worker thread per process that services a list of runnables. class AsyncWorker : public Singleton<AsyncWorker> { private: @@ -856,4 +868,9 @@ uint32_t BLASTBufferQueue::getLastTransformHint() const { } } +uint64_t BLASTBufferQueue::getLastAcquiredFrameNum() { + std::unique_lock _lock{mMutex}; + return mLastAcquiredFrameNumber; +} + } // namespace android diff --git a/libs/gui/DisplayInfo.cpp b/libs/gui/DisplayInfo.cpp new file mode 100644 index 0000000000..52d9540eeb --- /dev/null +++ b/libs/gui/DisplayInfo.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2021 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 "DisplayInfo" + +#include <binder/Parcel.h> +#include <gui/DisplayInfo.h> +#include <private/gui/ParcelUtils.h> + +#include <log/log.h> + +namespace android::gui { + +// --- DisplayInfo --- + +status_t DisplayInfo::readFromParcel(const android::Parcel* parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + float dsdx, dtdx, tx, dtdy, dsdy, ty; + SAFE_PARCEL(parcel->readInt32, &displayId); + SAFE_PARCEL(parcel->readInt32, &logicalWidth); + SAFE_PARCEL(parcel->readInt32, &logicalHeight); + SAFE_PARCEL(parcel->readFloat, &dsdx); + SAFE_PARCEL(parcel->readFloat, &dtdx); + SAFE_PARCEL(parcel->readFloat, &tx); + SAFE_PARCEL(parcel->readFloat, &dtdy); + SAFE_PARCEL(parcel->readFloat, &dsdy); + SAFE_PARCEL(parcel->readFloat, &ty); + + transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1}); + + return OK; +} + +status_t DisplayInfo::writeToParcel(android::Parcel* parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + SAFE_PARCEL(parcel->writeInt32, displayId); + SAFE_PARCEL(parcel->writeInt32, logicalWidth); + SAFE_PARCEL(parcel->writeInt32, logicalHeight); + SAFE_PARCEL(parcel->writeFloat, transform.dsdx()); + SAFE_PARCEL(parcel->writeFloat, transform.dtdx()); + SAFE_PARCEL(parcel->writeFloat, transform.tx()); + SAFE_PARCEL(parcel->writeFloat, transform.dtdy()); + SAFE_PARCEL(parcel->writeFloat, transform.dsdy()); + SAFE_PARCEL(parcel->writeFloat, transform.ty()); + + return OK; +} + +} // namespace android::gui diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 2a980bd118..3bf63062f6 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -124,11 +124,11 @@ public: return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply); } - status_t captureDisplay(uint64_t displayOrLayerStack, + status_t captureDisplay(DisplayId displayId, const sp<IScreenCaptureListener>& captureListener) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - SAFE_PARCEL(data.writeUint64, displayOrLayerStack); + SAFE_PARCEL(data.writeUint64, displayId.value); SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener)); return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, data, &reply); @@ -282,9 +282,14 @@ public: NO_ERROR) { std::vector<uint64_t> rawIds; if (reply.readUint64Vector(&rawIds) == NO_ERROR) { - std::vector<PhysicalDisplayId> displayIds(rawIds.size()); - std::transform(rawIds.begin(), rawIds.end(), displayIds.begin(), - [](uint64_t rawId) { return PhysicalDisplayId(rawId); }); + std::vector<PhysicalDisplayId> displayIds; + displayIds.reserve(rawIds.size()); + + for (const uint64_t rawId : rawIds) { + if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(rawId)) { + displayIds.push_back(*id); + } + } return displayIds; } } @@ -1344,12 +1349,15 @@ status_t BnSurfaceComposer::onTransact( } case CAPTURE_DISPLAY_BY_ID: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - uint64_t displayOrLayerStack = 0; + uint64_t value; + SAFE_PARCEL(data.readUint64, &value); + const auto id = DisplayId::fromValue(value); + if (!id) return BAD_VALUE; + sp<IScreenCaptureListener> captureListener; - SAFE_PARCEL(data.readUint64, &displayOrLayerStack); SAFE_PARCEL(data.readStrongBinder, &captureListener); - return captureDisplay(displayOrLayerStack, captureListener); + return captureDisplay(*id, captureListener); } case CAPTURE_LAYERS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); @@ -1416,9 +1424,9 @@ status_t BnSurfaceComposer::onTransact( } case GET_PHYSICAL_DISPLAY_TOKEN: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - PhysicalDisplayId displayId(data.readUint64()); - sp<IBinder> display = getPhysicalDisplayToken(displayId); - reply->writeStrongBinder(display); + const auto id = DisplayId::fromValue<PhysicalDisplayId>(data.readUint64()); + if (!id) return BAD_VALUE; + reply->writeStrongBinder(getPhysicalDisplayToken(*id)); return NO_ERROR; } case GET_DISPLAY_STATE: { diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 8a7a8711bf..1fd9d13902 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -41,7 +41,6 @@ layer_state_t::layer_state_t() z(0), w(0), h(0), - layerStack(0), alpha(0), flags(0), mask(0), @@ -86,7 +85,7 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeInt32, z); SAFE_PARCEL(output.writeUint32, w); SAFE_PARCEL(output.writeUint32, h); - SAFE_PARCEL(output.writeUint32, layerStack); + SAFE_PARCEL(output.writeUint32, layerStack.id); SAFE_PARCEL(output.writeFloat, alpha); SAFE_PARCEL(output.writeUint32, flags); SAFE_PARCEL(output.writeUint32, mask); @@ -188,7 +187,7 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readInt32, &z); SAFE_PARCEL(input.readUint32, &w); SAFE_PARCEL(input.readUint32, &h); - SAFE_PARCEL(input.readUint32, &layerStack); + SAFE_PARCEL(input.readUint32, &layerStack.id); SAFE_PARCEL(input.readFloat, &alpha); SAFE_PARCEL(input.readUint32, &flags); @@ -316,21 +315,14 @@ status_t ComposerState::read(const Parcel& input) { return state.read(input); } -DisplayState::DisplayState() - : what(0), - layerStack(0), - flags(0), - layerStackSpaceRect(Rect::EMPTY_RECT), - orientedDisplaySpaceRect(Rect::EMPTY_RECT), - width(0), - height(0) {} +DisplayState::DisplayState() = default; status_t DisplayState::write(Parcel& output) const { SAFE_PARCEL(output.writeStrongBinder, token); SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(surface)); SAFE_PARCEL(output.writeUint32, what); - SAFE_PARCEL(output.writeUint32, layerStack); SAFE_PARCEL(output.writeUint32, flags); + SAFE_PARCEL(output.writeUint32, layerStack.id); SAFE_PARCEL(output.writeUint32, toRotationInt(orientation)); SAFE_PARCEL(output.write, layerStackSpaceRect); SAFE_PARCEL(output.write, orientedDisplaySpaceRect); @@ -346,8 +338,8 @@ status_t DisplayState::read(const Parcel& input) { surface = interface_cast<IGraphicBufferProducer>(tmpBinder); SAFE_PARCEL(input.readUint32, &what); - SAFE_PARCEL(input.readUint32, &layerStack); SAFE_PARCEL(input.readUint32, &flags); + SAFE_PARCEL(input.readUint32, &layerStack.id); uint32_t tmpUint = 0; SAFE_PARCEL(input.readUint32, &tmpUint); orientation = ui::toRotation(tmpUint); @@ -563,10 +555,13 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eDestinationFrameChanged; destinationFrame = other.destinationFrame; } + if (other.what & eProducerDisconnect) { + what |= eProducerDisconnect; + } if ((other.what & what) != other.what) { ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " - "other.what=0x%" PRIu64 " what=0x%" PRIu64, - other.what, what); + "other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64, + other.what, what, (other.what & what) ^ other.what); } } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index a2037ac614..1bca6f9167 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1108,7 +1108,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAlpha } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLayerStack( - const sp<SurfaceControl>& sc, uint32_t layerStack) { + const sp<SurfaceControl>& sc, ui::LayerStack layerStack) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; @@ -1773,7 +1773,7 @@ status_t SurfaceComposerClient::Transaction::setDisplaySurface(const sp<IBinder> } void SurfaceComposerClient::Transaction::setDisplayLayerStack(const sp<IBinder>& token, - uint32_t layerStack) { + ui::LayerStack layerStack) { DisplayState& s(getDisplayState(token)); s.layerStack = layerStack; s.what |= DisplayState::eLayerStackChanged; @@ -2197,12 +2197,12 @@ status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs, return s->captureDisplay(captureArgs, captureListener); } -status_t ScreenshotClient::captureDisplay(uint64_t displayOrLayerStack, +status_t ScreenshotClient::captureDisplay(DisplayId displayId, const sp<IScreenCaptureListener>& captureListener) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); if (s == nullptr) return NO_INIT; - return s->captureDisplay(displayOrLayerStack, captureListener); + return s->captureDisplay(displayId, captureListener); } status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs, diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp index b2ef7aabc9..5f3a726336 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -54,12 +54,11 @@ bool WindowInfo::operator==(const WindowInfo& info) const { info.frameLeft == frameLeft && info.frameTop == frameTop && info.frameRight == frameRight && info.frameBottom == frameBottom && info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor && - info.transform == transform && info.displayOrientation == displayOrientation && - info.displayWidth == displayWidth && info.displayHeight == displayHeight && - info.touchableRegion.hasSameRects(touchableRegion) && info.visible == visible && - info.trustedOverlay == trustedOverlay && info.focusable == focusable && - info.touchOcclusionMode == touchOcclusionMode && info.hasWallpaper == hasWallpaper && - info.paused == paused && info.ownerPid == ownerPid && info.ownerUid == ownerUid && + info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) && + info.visible == visible && info.trustedOverlay == trustedOverlay && + info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode && + info.hasWallpaper == hasWallpaper && info.paused == paused && + info.ownerPid == ownerPid && info.ownerUid == ownerUid && info.packageName == packageName && info.inputFeatures == inputFeatures && info.displayId == displayId && info.portalToDisplayId == portalToDisplayId && info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop && @@ -97,9 +96,6 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->writeFloat(transform.dtdy()) ?: parcel->writeFloat(transform.dsdy()) ?: parcel->writeFloat(transform.ty()) ?: - parcel->writeUint32(displayOrientation) ?: - parcel->writeInt32(displayWidth) ?: - parcel->writeInt32(displayHeight) ?: parcel->writeBool(visible) ?: parcel->writeBool(focusable) ?: parcel->writeBool(hasWallpaper) ?: @@ -155,9 +151,6 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { parcel->readFloat(&dtdy) ?: parcel->readFloat(&dsdy) ?: parcel->readFloat(&ty) ?: - parcel->readUint32(&displayOrientation) ?: - parcel->readInt32(&displayWidth) ?: - parcel->readInt32(&displayHeight) ?: parcel->readBool(&visible) ?: parcel->readBool(&focusable) ?: parcel->readBool(&hasWallpaper) ?: diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp index c00a4389ad..c32b9ab398 100644 --- a/libs/gui/WindowInfosListenerReporter.cpp +++ b/libs/gui/WindowInfosListenerReporter.cpp @@ -19,6 +19,7 @@ namespace android { +using gui::DisplayInfo; using gui::IWindowInfosReportedListener; using gui::WindowInfo; using gui::WindowInfosListener; @@ -65,7 +66,7 @@ status_t WindowInfosListenerReporter::removeWindowInfosListener( } binder::Status WindowInfosListenerReporter::onWindowInfosChanged( - const std::vector<WindowInfo>& windowInfos, + const std::vector<WindowInfo>& windowInfos, const std::vector<DisplayInfo>& displayInfos, const sp<IWindowInfosReportedListener>& windowInfosReportedListener) { std::unordered_set<sp<WindowInfosListener>, ISurfaceComposer::SpHash<WindowInfosListener>> windowInfosListeners; @@ -78,7 +79,7 @@ binder::Status WindowInfosListenerReporter::onWindowInfosChanged( } for (auto listener : windowInfosListeners) { - listener->onWindowInfosChanged(windowInfos); + listener->onWindowInfosChanged(windowInfos, displayInfos); } if (windowInfosReportedListener) { diff --git a/libs/ui/Size.cpp b/libs/gui/android/gui/DisplayInfo.aidl index d2996d164d..30c088525d 100644 --- a/libs/ui/Size.cpp +++ b/libs/gui/android/gui/DisplayInfo.aidl @@ -1,11 +1,11 @@ /* - * Copyright 2019 The Android Open Source Project + * Copyright 2021, 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 + * 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, @@ -14,11 +14,6 @@ * limitations under the License. */ -#include <ui/Size.h> +package android.gui; -namespace android::ui { - -const Size Size::INVALID{-1, -1}; -const Size Size::EMPTY{0, 0}; - -} // namespace android::ui +parcelable DisplayInfo cpp_header "gui/DisplayInfo.h"; diff --git a/libs/gui/android/gui/IWindowInfosListener.aidl b/libs/gui/android/gui/IWindowInfosListener.aidl index d4553ca82d..a5b2762318 100644 --- a/libs/gui/android/gui/IWindowInfosListener.aidl +++ b/libs/gui/android/gui/IWindowInfosListener.aidl @@ -16,11 +16,12 @@ package android.gui; +import android.gui.DisplayInfo; import android.gui.IWindowInfosReportedListener; import android.gui.WindowInfo; /** @hide */ oneway interface IWindowInfosListener { - void onWindowInfosChanged(in WindowInfo[] windowInfos, in @nullable IWindowInfosReportedListener windowInfosReportedListener); + void onWindowInfosChanged(in WindowInfo[] windowInfos, in DisplayInfo[] displayInfos, in @nullable IWindowInfosReportedListener windowInfosReportedListener); } diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 6c5b2aa53c..1f517f65d6 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -94,12 +94,12 @@ public: uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount); void setNextTransaction(SurfaceComposerClient::Transaction *t); void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber); + void applyPendingTransactions(uint64_t frameNumber); void setTransactionCompleteCallback(uint64_t frameNumber, std::function<void(int64_t)>&& transactionCompleteCallback); void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format, SurfaceComposerClient::Transaction* outTransaction = nullptr); - void flushShadowQueue() {} status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless); status_t setFrameTimelineInfo(const FrameTimelineInfo& info); @@ -107,6 +107,7 @@ public: void setSidebandStream(const sp<NativeHandle>& stream); uint32_t getLastTransformHint() const; + uint64_t getLastAcquiredFrameNum(); virtual ~BLASTBufferQueue(); @@ -125,6 +126,8 @@ private: bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex); bool maxBuffersAcquired(bool includeExtraAcquire) const REQUIRES(mMutex); static PixelFormat convertBufferFormat(PixelFormat& format); + void mergePendingTransactions(SurfaceComposerClient::Transaction* t, uint64_t frameNumber) + REQUIRES(mMutex); std::string mName; // Represents the queued buffer count from buffer queue, diff --git a/libs/gui/include/gui/DisplayInfo.h b/libs/gui/include/gui/DisplayInfo.h new file mode 100644 index 0000000000..74f33a2a87 --- /dev/null +++ b/libs/gui/include/gui/DisplayInfo.h @@ -0,0 +1,46 @@ +/* + * Copyright 2021 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 <binder/Parcel.h> +#include <binder/Parcelable.h> +#include <gui/constants.h> +#include <ui/Transform.h> + +namespace android::gui { + +/* + * Describes information about a display that can have windows in it. + * + * This should only be used by InputFlinger to support raw coordinates in logical display space. + */ +struct DisplayInfo : public Parcelable { + int32_t displayId = ADISPLAY_ID_NONE; + + // Logical display dimensions. + int32_t logicalWidth = 0; + int32_t logicalHeight = 0; + + // The display transform. This takes display coordinates to logical display coordinates. + ui::Transform transform; + + status_t writeToParcel(android::Parcel*) const override; + + status_t readFromParcel(const android::Parcel*) override; +}; + +} // namespace android::gui
\ No newline at end of file diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index ad7bcb7d12..b7cd082da0 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -116,6 +116,11 @@ public: using EventRegistrationFlags = Flags<EventRegistration>; + template <typename T> + struct SpHash { + size_t operator()(const sp<T>& k) const { return std::hash<T*>()(k.get()); } + }; + /* * Create a connection with SurfaceFlinger. */ @@ -239,24 +244,17 @@ public: * The subregion can be optionally rotated. It will also be scaled to * match the size of the output buffer. */ - virtual status_t captureDisplay(const DisplayCaptureArgs& args, - const sp<IScreenCaptureListener>& captureListener) = 0; + virtual status_t captureDisplay(const DisplayCaptureArgs&, + const sp<IScreenCaptureListener>&) = 0; - virtual status_t captureDisplay(uint64_t displayOrLayerStack, - const sp<IScreenCaptureListener>& captureListener) = 0; - - template <class AA> - struct SpHash { - size_t operator()(const sp<AA>& k) const { return std::hash<AA*>()(k.get()); } - }; + virtual status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) = 0; /** * Capture a subtree of the layer hierarchy, potentially ignoring the root node. * This requires READ_FRAME_BUFFER permission. This function will fail if there * is a secure window on screen */ - virtual status_t captureLayers(const LayerCaptureArgs& args, - const sp<IScreenCaptureListener>& captureListener) = 0; + virtual status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) = 0; /* Clears the frame statistics for animations. * diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 8dc8b9a87b..f14127c9de 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -35,6 +35,7 @@ #include <math/vec3.h> #include <ui/BlurRegion.h> #include <ui/GraphicTypes.h> +#include <ui/LayerStack.h> #include <ui/Rect.h> #include <ui/Region.h> #include <ui/Rotation.h> @@ -143,7 +144,7 @@ struct layer_state_t { int32_t z; uint32_t w; uint32_t h; - uint32_t layerStack; + ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK; float alpha; uint32_t flags; uint32_t mask; @@ -267,11 +268,12 @@ struct DisplayState { DisplayState(); void merge(const DisplayState& other); - uint32_t what; + uint32_t what = 0; + uint32_t flags = 0; sp<IBinder> token; sp<IGraphicBufferProducer> surface; - uint32_t layerStack; - uint32_t flags; + + ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK; // These states define how layers are projected onto the physical display. // @@ -285,10 +287,11 @@ struct DisplayState { // will be additionally rotated by 90 degrees around the origin clockwise and translated by (W, // 0). ui::Rotation orientation = ui::ROTATION_0; - Rect layerStackSpaceRect; - Rect orientedDisplaySpaceRect; + Rect layerStackSpaceRect = Rect::EMPTY_RECT; + Rect orientedDisplaySpaceRect = Rect::EMPTY_RECT; - uint32_t width, height; + uint32_t width = 0; + uint32_t height = 0; status_t write(Parcel& output) const; status_t read(const Parcel& input); diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 0f8a32b7fa..baa6878414 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -456,7 +456,7 @@ public: int backgroundBlurRadius); Transaction& setBlurRegions(const sp<SurfaceControl>& sc, const std::vector<BlurRegion>& regions); - Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack); + Transaction& setLayerStack(const sp<SurfaceControl>&, ui::LayerStack); Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p); /// Reparents the current layer to the new parent handle. The new parent must not be null. @@ -566,7 +566,7 @@ public: status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); - void setDisplayLayerStack(const sp<IBinder>& token, uint32_t layerStack); + void setDisplayLayerStack(const sp<IBinder>& token, ui::LayerStack); void setDisplayFlags(const sp<IBinder>& token, uint32_t flags); @@ -638,12 +638,9 @@ private: class ScreenshotClient { public: - static status_t captureDisplay(const DisplayCaptureArgs& captureArgs, - const sp<IScreenCaptureListener>& captureListener); - static status_t captureDisplay(uint64_t displayOrLayerStack, - const sp<IScreenCaptureListener>& captureListener); - static status_t captureLayers(const LayerCaptureArgs& captureArgs, - const sp<IScreenCaptureListener>& captureListener); + static status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&); + static status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&); + static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&); }; // --------------------------------------------------------------------------- diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index f090c63228..54a372c23e 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -71,8 +71,9 @@ struct WindowInfo : public Parcelable { SLIPPERY = 0x20000000, LAYOUT_ATTACHED_IN_DECOR = 0x40000000, DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000, - }; // Window types from WindowManager.LayoutParams + }; + // Window types from WindowManager.LayoutParams enum class Type : int32_t { UNKNOWN = 0, FIRST_APPLICATION_WINDOW = 1, @@ -87,43 +88,55 @@ struct WindowInfo : public Parcelable { APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3, APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4, LAST_SUB_WINDOW = 1999, - FIRST_SYSTEM_WINDOW = 2000, - STATUS_BAR = FIRST_SYSTEM_WINDOW, - SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1, - PHONE = FIRST_SYSTEM_WINDOW + 2, - SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3, - KEYGUARD = FIRST_SYSTEM_WINDOW + 4, - TOAST = FIRST_SYSTEM_WINDOW + 5, - SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6, - PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7, - SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8, - KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9, - SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10, - INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11, - INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12, - WALLPAPER = FIRST_SYSTEM_WINDOW + 13, - STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14, - SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15, - DRAG = FIRST_SYSTEM_WINDOW + 16, - STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17, - POINTER = FIRST_SYSTEM_WINDOW + 18, - NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19, - VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20, - BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21, - INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22, - NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24, - MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27, - ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32, - DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34, - ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39, - NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40, + +#define FIRST_SYSTEM_WINDOW_ 2000 + + STATUS_BAR = FIRST_SYSTEM_WINDOW_, + SEARCH_BAR = FIRST_SYSTEM_WINDOW_ + 1, + PHONE = FIRST_SYSTEM_WINDOW_ + 2, + SYSTEM_ALERT = FIRST_SYSTEM_WINDOW_ + 3, + KEYGUARD = FIRST_SYSTEM_WINDOW_ + 4, + TOAST = FIRST_SYSTEM_WINDOW_ + 5, + SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW_ + 6, + PRIORITY_PHONE = FIRST_SYSTEM_WINDOW_ + 7, + SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW_ + 8, + KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW_ + 9, + SYSTEM_ERROR = FIRST_SYSTEM_WINDOW_ + 10, + INPUT_METHOD = FIRST_SYSTEM_WINDOW_ + 11, + INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW_ + 12, + WALLPAPER = FIRST_SYSTEM_WINDOW_ + 13, + STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW_ + 14, + SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW_ + 15, + DRAG = FIRST_SYSTEM_WINDOW_ + 16, + STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW_ + 17, + POINTER = FIRST_SYSTEM_WINDOW_ + 18, + NAVIGATION_BAR = FIRST_SYSTEM_WINDOW_ + 19, + VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW_ + 20, + BOOT_PROGRESS = FIRST_SYSTEM_WINDOW_ + 21, + INPUT_CONSUMER = FIRST_SYSTEM_WINDOW_ + 22, + NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW_ + 24, + MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW_ + 27, + ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW_ + 32, + DOCK_DIVIDER = FIRST_SYSTEM_WINDOW_ + 34, + ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW_ + 39, + NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW_ + 40, + + FIRST_SYSTEM_WINDOW = FIRST_SYSTEM_WINDOW_, LAST_SYSTEM_WINDOW = 2999, + +#undef FIRST_SYSTEM_WINDOW_ + + // Small range to limit LUT size. + ftl_first = FIRST_SYSTEM_WINDOW, + ftl_last = FIRST_SYSTEM_WINDOW + 15 }; - enum class Feature { - DISABLE_TOUCH_PAD_GESTURES = 0x00000001, - NO_INPUT_CHANNEL = 0x00000002, - DISABLE_USER_ACTIVITY = 0x00000004, + enum class Feature : uint32_t { + DISABLE_TOUCH_PAD_GESTURES = 1u << 0, + NO_INPUT_CHANNEL = 1u << 1, + DISABLE_USER_ACTIVITY = 1u << 2, + DROP_INPUT = 1u << 3, + DROP_INPUT_IF_OBSCURED = 1u << 4, }; /* These values are filled in by the WM and passed through SurfaceFlinger @@ -168,13 +181,6 @@ struct WindowInfo : public Parcelable { // Transform applied to individual windows. ui::Transform transform; - // Display orientation as ui::Transform::RotationFlags. Used for compatibility raw coordinates. - uint32_t displayOrientation = ui::Transform::ROT_0; - - // Display size in its natural rotation. Used to rotate raw coordinates for compatibility. - int32_t displayWidth = 0; - int32_t displayHeight = 0; - /* * This is filled in by the WM relative to the frame and then translated * to absolute coordinates by SurfaceFlinger once the frame is computed. @@ -265,4 +271,4 @@ protected: WindowInfo mInfo; }; -} // namespace android::gui
\ No newline at end of file +} // namespace android::gui diff --git a/libs/gui/include/gui/WindowInfosListener.h b/libs/gui/include/gui/WindowInfosListener.h index 8a70b9bb57..a18a498c5e 100644 --- a/libs/gui/include/gui/WindowInfosListener.h +++ b/libs/gui/include/gui/WindowInfosListener.h @@ -16,6 +16,7 @@ #pragma once +#include <gui/DisplayInfo.h> #include <gui/WindowInfo.h> #include <utils/RefBase.h> @@ -23,6 +24,7 @@ namespace android::gui { class WindowInfosListener : public virtual RefBase { public: - virtual void onWindowInfosChanged(const std::vector<WindowInfo>& /*windowInfos*/) = 0; + virtual void onWindowInfosChanged(const std::vector<WindowInfo>&, + const std::vector<DisplayInfo>&) = 0; }; } // namespace android::gui
\ No newline at end of file diff --git a/libs/gui/include/gui/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h index 7cb96e0a30..157a804264 100644 --- a/libs/gui/include/gui/WindowInfosListenerReporter.h +++ b/libs/gui/include/gui/WindowInfosListenerReporter.h @@ -21,7 +21,6 @@ #include <binder/IBinder.h> #include <gui/ISurfaceComposer.h> #include <gui/WindowInfosListener.h> -#include <utils/Mutex.h> #include <unordered_set> namespace android { @@ -30,7 +29,8 @@ class ISurfaceComposer; class WindowInfosListenerReporter : public gui::BnWindowInfosListener { public: static sp<WindowInfosListenerReporter> getInstance(); - binder::Status onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos, + binder::Status onWindowInfosChanged(const std::vector<gui::WindowInfo>&, + const std::vector<gui::DisplayInfo>&, const sp<gui::IWindowInfosReportedListener>&) override; status_t addWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener, diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index 3d26c3d858..6dd1073879 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -27,6 +27,7 @@ cc_test { "BufferQueue_test.cpp", "CpuConsumer_test.cpp", "EndToEndNativeInputTest.cpp", + "DisplayInfo_test.cpp", "DisplayedContentSampling_test.cpp", "FillBuffer.cpp", "GLTest.cpp", @@ -62,7 +63,7 @@ cc_test { "libinput", "libui", "libutils", - "libnativewindow" + "libnativewindow", ], header_libs: ["libsurfaceflinger_headers"], @@ -117,7 +118,7 @@ cc_test { "libgui", "libui", "libutils", - "libbufferhubqueue", // TODO(b/70046255): Remove these once BufferHub is integrated into libgui. + "libbufferhubqueue", // TODO(b/70046255): Remove these once BufferHub is integrated into libgui. "libpdx_default_transport", ], @@ -146,5 +147,5 @@ cc_test { "liblog", "libui", "libutils", - ] + ], } diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 9082d275a2..26d902d849 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -128,7 +128,7 @@ protected: mDisplayToken = mClient->getInternalDisplayToken(); ASSERT_NE(nullptr, mDisplayToken.get()); Transaction t; - t.setDisplayLayerStack(mDisplayToken, 0); + t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK); t.apply(); t.clear(); @@ -142,7 +142,7 @@ protected: mDisplayHeight, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceBufferState, /*parent*/ nullptr); - t.setLayerStack(mSurfaceControl, 0) + t.setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK) .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max()) .show(mSurfaceControl) .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB) @@ -490,7 +490,7 @@ TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) { ISurfaceComposerClient::eFXSurfaceEffect); ASSERT_NE(nullptr, bg.get()); Transaction t; - t.setLayerStack(bg, 0) + t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK) .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight)) .setColor(bg, half3{0, 0, 0}) .setLayer(bg, 0) @@ -545,7 +545,7 @@ TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToBufferSize) { ISurfaceComposerClient::eFXSurfaceEffect); ASSERT_NE(nullptr, bg.get()); Transaction t; - t.setLayerStack(bg, 0) + t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK) .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight)) .setColor(bg, half3{0, 0, 0}) .setLayer(bg, 0) @@ -612,7 +612,7 @@ TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToWindowSize) { ISurfaceComposerClient::eFXSurfaceEffect); ASSERT_NE(nullptr, bg.get()); Transaction t; - t.setLayerStack(bg, 0) + t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK) .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight)) .setColor(bg, half3{0, 0, 0}) .setLayer(bg, 0) @@ -733,7 +733,7 @@ TEST_F(BLASTBufferQueueTest, OutOfOrderTransactionTest) { ISurfaceComposerClient::eFXSurfaceBufferState); ASSERT_NE(nullptr, bgSurface.get()); Transaction t; - t.setLayerStack(bgSurface, 0) + t.setLayerStack(bgSurface, ui::DEFAULT_LAYER_STACK) .show(bgSurface) .setDataspace(bgSurface, ui::Dataspace::V0_SRGB) .setLayer(bgSurface, std::numeric_limits<int32_t>::max() - 1) diff --git a/libs/gui/tests/DisplayInfo_test.cpp b/libs/gui/tests/DisplayInfo_test.cpp new file mode 100644 index 0000000000..df3329cd52 --- /dev/null +++ b/libs/gui/tests/DisplayInfo_test.cpp @@ -0,0 +1,49 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <binder/Parcel.h> + +#include <gui/DisplayInfo.h> + +namespace android { + +using gui::DisplayInfo; + +namespace test { + +TEST(DisplayInfo, Parcelling) { + DisplayInfo info; + info.displayId = 42; + info.logicalWidth = 99; + info.logicalHeight = 78; + info.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1}); + + Parcel p; + info.writeToParcel(&p); + p.setDataPosition(0); + + DisplayInfo info2; + info2.readFromParcel(&p); + ASSERT_EQ(info.displayId, info2.displayId); + ASSERT_EQ(info.logicalWidth, info2.logicalWidth); + ASSERT_EQ(info.logicalHeight, info2.logicalHeight); + ASSERT_EQ(info.transform, info2.transform); +} + +} // namespace test +} // namespace android diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index a02970c9bc..d1ad478dd1 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -752,21 +752,19 @@ public: } status_t setActiveColorMode(const sp<IBinder>& /*display*/, ColorMode /*colorMode*/) override { return NO_ERROR; } - status_t captureDisplay(const DisplayCaptureArgs& /* captureArgs */, - const sp<IScreenCaptureListener>& /* captureListener */) override { - return NO_ERROR; - } void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {} void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {} - status_t captureDisplay(uint64_t /*displayOrLayerStack*/, - const sp<IScreenCaptureListener>& /* captureListener */) override { + + status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override { return NO_ERROR; } - virtual status_t captureLayers( - const LayerCaptureArgs& /* captureArgs */, - const sp<IScreenCaptureListener>& /* captureListener */) override { + status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) override { return NO_ERROR; } + status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override { + return NO_ERROR; + } + status_t clearAnimationFrameStats() override { return NO_ERROR; } status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override { return NO_ERROR; diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp index a4f436cdba..dcdf76fe35 100644 --- a/libs/gui/tests/WindowInfo_test.cpp +++ b/libs/gui/tests/WindowInfo_test.cpp @@ -60,9 +60,6 @@ TEST(WindowInfo, Parcelling) { i.globalScaleFactor = 0.3; i.alpha = 0.7; i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1}); - i.displayOrientation = ui::Transform::ROT_0; - i.displayWidth = 1000; - i.displayHeight = 2000; i.visible = false; i.focusable = false; i.hasWallpaper = false; @@ -100,8 +97,6 @@ TEST(WindowInfo, Parcelling) { ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor); ASSERT_EQ(i.alpha, i2.alpha); ASSERT_EQ(i.transform, i2.transform); - ASSERT_EQ(i.displayWidth, i2.displayWidth); - ASSERT_EQ(i.displayHeight, i2.displayHeight); ASSERT_EQ(i.visible, i2.visible); ASSERT_EQ(i.focusable, i2.focusable); ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper); diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 35209f7a07..037849eac4 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -170,6 +170,9 @@ const char* inputEventTypeToString(int32_t type) { case AINPUT_EVENT_TYPE_DRAG: { return "DRAG"; } + case AINPUT_EVENT_TYPE_TOUCH_MODE: { + return "TOUCH_MODE"; + } } return "UNKNOWN"; } @@ -330,10 +333,6 @@ void PointerCoords::scale(float globalScaleFactor, float windowXScale, float win scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_Y, windowYScale); } -void PointerCoords::scale(float globalScaleFactor) { - scale(globalScaleFactor, globalScaleFactor, globalScaleFactor); -} - void PointerCoords::applyOffset(float xOffset, float yOffset) { setAxisValue(AMOTION_EVENT_AXIS_X, getX() + xOffset); setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset); @@ -835,9 +834,9 @@ std::string MotionEvent::actionToString(int32_t action) { case AMOTION_EVENT_ACTION_OUTSIDE: return "OUTSIDE"; case AMOTION_EVENT_ACTION_POINTER_DOWN: - return "POINTER_DOWN"; + return StringPrintf("POINTER_DOWN(%" PRId32 ")", MotionEvent::getActionIndex(action)); case AMOTION_EVENT_ACTION_POINTER_UP: - return "POINTER_UP"; + return StringPrintf("POINTER_UP(%" PRId32 ")", MotionEvent::getActionIndex(action)); case AMOTION_EVENT_ACTION_HOVER_MOVE: return "HOVER_MOVE"; case AMOTION_EVENT_ACTION_SCROLL: @@ -899,6 +898,19 @@ void DragEvent::initialize(const DragEvent& from) { mY = from.mY; } +// --- TouchModeEvent --- + +void TouchModeEvent::initialize(int32_t id, bool isInTouchMode) { + InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, + ADISPLAY_ID_NONE, INVALID_HMAC); + mIsInTouchMode = isInTouchMode; +} + +void TouchModeEvent::initialize(const TouchModeEvent& from) { + InputEvent::initialize(from); + mIsInTouchMode = from.mIsInTouchMode; +} + // --- PooledInputEventFactory --- PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) : @@ -953,6 +965,15 @@ DragEvent* PooledInputEventFactory::createDragEvent() { return event; } +TouchModeEvent* PooledInputEventFactory::createTouchModeEvent() { + if (mTouchModeEventPool.empty()) { + return new TouchModeEvent(); + } + TouchModeEvent* event = mTouchModeEventPool.front().release(); + mTouchModeEventPool.pop(); + return event; +} + void PooledInputEventFactory::recycle(InputEvent* event) { switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 220c8e1e6e..69ae9a02ec 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -21,7 +21,7 @@ #include <ctype.h> #include <android-base/stringprintf.h> -#include <ftl/NamedEnum.h> +#include <ftl/enum.h> #include <input/InputDevice.h> #include <input/InputEventLabels.h> @@ -228,7 +228,7 @@ void InputDeviceInfo::addMotionRange(const MotionRange& range) { void InputDeviceInfo::addSensorInfo(const InputDeviceSensorInfo& info) { if (mSensors.find(info.type) != mSensors.end()) { ALOGW("Sensor type %s already exists, will be replaced by new sensor added.", - NamedEnum::string(info.type).c_str()); + ftl::enum_string(info.type).c_str()); } mSensors.insert_or_assign(info.type, info); } diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index ea8b9a7ec8..91ab008161 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -30,10 +30,10 @@ static constexpr bool DEBUG_TRANSPORT_ACTIONS = false; #include <android-base/stringprintf.h> #include <binder/Parcel.h> #include <cutils/properties.h> +#include <ftl/enum.h> #include <log/log.h> #include <utils/Trace.h> -#include <ftl/NamedEnum.h> #include <input/InputTransport.h> using android::base::StringPrintf; @@ -116,6 +116,7 @@ bool InputMessage::isValid(size_t actualSize) const { case Type::FOCUS: case Type::CAPTURE: case Type::DRAG: + case Type::TOUCH_MODE: return true; case Type::TIMELINE: { const nsecs_t gpuCompletedTime = @@ -151,6 +152,8 @@ size_t InputMessage::size() const { return sizeof(Header) + body.drag.size(); case Type::TIMELINE: return sizeof(Header) + body.timeline.size(); + case Type::TOUCH_MODE: + return sizeof(Header) + body.touchMode.size(); } return sizeof(Header); } @@ -293,6 +296,10 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.timeline.graphicsTimeline = body.timeline.graphicsTimeline; break; } + case InputMessage::Type::TOUCH_MODE: { + msg->body.touchMode.eventId = body.touchMode.eventId; + msg->body.touchMode.isInTouchMode = body.touchMode.isInTouchMode; + } } } @@ -665,6 +672,22 @@ status_t InputPublisher::publishDragEvent(uint32_t seq, int32_t eventId, float x return mChannel->sendMessage(&msg); } +status_t InputPublisher::publishTouchModeEvent(uint32_t seq, int32_t eventId, bool isInTouchMode) { + if (ATRACE_ENABLED()) { + std::string message = + StringPrintf("publishTouchModeEvent(inputChannel=%s, isInTouchMode=%s)", + mChannel->getName().c_str(), toString(isInTouchMode)); + ATRACE_NAME(message.c_str()); + } + + InputMessage msg; + msg.header.type = InputMessage::Type::TOUCH_MODE; + msg.header.seq = seq; + msg.body.touchMode.eventId = eventId; + msg.body.touchMode.isInTouchMode = isInTouchMode; + return mChannel->sendMessage(&msg); +} + android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() { if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__); @@ -691,7 +714,7 @@ android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveC } ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer", - mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str()); + mChannel->getName().c_str(), ftl::enum_string(msg.header.type).c_str()); return android::base::Error(UNKNOWN_ERROR); } @@ -833,7 +856,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum case InputMessage::Type::TIMELINE: { LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by " "InputConsumer!", - NamedEnum::string(mMsg.header.type).c_str()); + ftl::enum_string(mMsg.header.type).c_str()); break; } @@ -866,6 +889,16 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum *outEvent = dragEvent; break; } + + case InputMessage::Type::TOUCH_MODE: { + TouchModeEvent* touchModeEvent = factory->createTouchModeEvent(); + if (!touchModeEvent) return NO_MEMORY; + + initializeTouchModeEvent(touchModeEvent, &mMsg); + *outSeq = mMsg.header.seq; + *outEvent = touchModeEvent; + break; + } } } return OK; @@ -1370,6 +1403,10 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); } +void InputConsumer::initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg) { + event->initialize(msg->body.touchMode.eventId, msg->body.touchMode.isInTouchMode); +} + void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { uint32_t pointerCount = msg->body.motion.pointerCount; PointerCoords pointerCoords[pointerCount]; @@ -1412,14 +1449,14 @@ std::string InputConsumer::dump() const { out = out + "mChannel = " + mChannel->getName() + "\n"; out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n"; if (mMsgDeferred) { - out = out + "mMsg : " + NamedEnum::string(mMsg.header.type) + "\n"; + out = out + "mMsg : " + ftl::enum_string(mMsg.header.type) + "\n"; } out += "Batches:\n"; for (const Batch& batch : mBatches) { out += " Batch:\n"; for (const InputMessage& msg : batch.samples) { out += android::base::StringPrintf(" Message %" PRIu32 ": %s ", msg.header.seq, - NamedEnum::string(msg.header.type).c_str()); + ftl::enum_string(msg.header.type).c_str()); switch (msg.header.type) { case InputMessage::Type::KEY: { out += android::base::StringPrintf("action=%s keycode=%" PRId32, @@ -1476,6 +1513,11 @@ std::string InputConsumer::dump() const { presentTime); break; } + case InputMessage::Type::TOUCH_MODE: { + out += android::base::StringPrintf("isInTouchMode=%s", + toString(msg.body.touchMode.isInTouchMode)); + break; + } } out += "\n"; } diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index c365ab070e..7c25cda9ac 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -16,10 +16,8 @@ #define LOG_TAG "KeyLayoutMap" -#include <stdlib.h> - #include <android/keycodes.h> -#include <ftl/NamedEnum.h> +#include <ftl/enum.h> #include <input/InputEventLabels.h> #include <input/KeyLayoutMap.h> #include <input/Keyboard.h> @@ -28,6 +26,10 @@ #include <utils/Timers.h> #include <utils/Tokenizer.h> +#include <cstdlib> +#include <string_view> +#include <unordered_map> + // Enables debug output for the parser. #define DEBUG_PARSER 0 @@ -39,37 +41,39 @@ namespace android { +namespace { -static const char* WHITESPACE = " \t\r"; - -#define SENSOR_ENTRY(type) NamedEnum::string(type), type -static const std::unordered_map<std::string, InputDeviceSensorType> SENSOR_LIST = - {{SENSOR_ENTRY(InputDeviceSensorType::ACCELEROMETER)}, - {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD)}, - {SENSOR_ENTRY(InputDeviceSensorType::ORIENTATION)}, - {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE)}, - {SENSOR_ENTRY(InputDeviceSensorType::LIGHT)}, - {SENSOR_ENTRY(InputDeviceSensorType::PRESSURE)}, - {SENSOR_ENTRY(InputDeviceSensorType::TEMPERATURE)}, - {SENSOR_ENTRY(InputDeviceSensorType::PROXIMITY)}, - {SENSOR_ENTRY(InputDeviceSensorType::GRAVITY)}, - {SENSOR_ENTRY(InputDeviceSensorType::LINEAR_ACCELERATION)}, - {SENSOR_ENTRY(InputDeviceSensorType::ROTATION_VECTOR)}, - {SENSOR_ENTRY(InputDeviceSensorType::RELATIVE_HUMIDITY)}, - {SENSOR_ENTRY(InputDeviceSensorType::AMBIENT_TEMPERATURE)}, - {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED)}, - {SENSOR_ENTRY(InputDeviceSensorType::GAME_ROTATION_VECTOR)}, - {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE_UNCALIBRATED)}, - {SENSOR_ENTRY(InputDeviceSensorType::SIGNIFICANT_MOTION)}}; - -// --- KeyLayoutMap --- - -KeyLayoutMap::KeyLayoutMap() { -} +constexpr const char* WHITESPACE = " \t\r"; -KeyLayoutMap::~KeyLayoutMap() { +template <InputDeviceSensorType S> +constexpr auto sensorPair() { + return std::make_pair(ftl::enum_name<S>(), S); } +static const std::unordered_map<std::string_view, InputDeviceSensorType> SENSOR_LIST = + {sensorPair<InputDeviceSensorType::ACCELEROMETER>(), + sensorPair<InputDeviceSensorType::MAGNETIC_FIELD>(), + sensorPair<InputDeviceSensorType::ORIENTATION>(), + sensorPair<InputDeviceSensorType::GYROSCOPE>(), + sensorPair<InputDeviceSensorType::LIGHT>(), + sensorPair<InputDeviceSensorType::PRESSURE>(), + sensorPair<InputDeviceSensorType::TEMPERATURE>(), + sensorPair<InputDeviceSensorType::PROXIMITY>(), + sensorPair<InputDeviceSensorType::GRAVITY>(), + sensorPair<InputDeviceSensorType::LINEAR_ACCELERATION>(), + sensorPair<InputDeviceSensorType::ROTATION_VECTOR>(), + sensorPair<InputDeviceSensorType::RELATIVE_HUMIDITY>(), + sensorPair<InputDeviceSensorType::AMBIENT_TEMPERATURE>(), + sensorPair<InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED>(), + sensorPair<InputDeviceSensorType::GAME_ROTATION_VECTOR>(), + sensorPair<InputDeviceSensorType::GYROSCOPE_UNCALIBRATED>(), + sensorPair<InputDeviceSensorType::SIGNIFICANT_MOTION>()}; + +} // namespace + +KeyLayoutMap::KeyLayoutMap() = default; +KeyLayoutMap::~KeyLayoutMap() = default; + base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::loadContents(const std::string& filename, const char* contents) { Tokenizer* tokenizer; @@ -160,8 +164,8 @@ base::Result<std::pair<InputDeviceSensorType, int32_t>> KeyLayoutMap::mapSensor( const Sensor& sensor = it->second; #if DEBUG_MAPPING - ALOGD("mapSensor: absCode=%d, sensorType=0x%0x, sensorDataIndex=0x%x.", absCode, - NamedEnum::string(sensor.sensorType), sensor.sensorDataIndex); + ALOGD("mapSensor: absCode=%d, sensorType=%s, sensorDataIndex=0x%x.", absCode, + ftl::enum_string(sensor.sensorType).c_str(), sensor.sensorDataIndex); #endif return std::make_pair(sensor.sensorType, sensor.sensorDataIndex); } @@ -513,7 +517,7 @@ status_t KeyLayoutMap::Parser::parseLed() { } static std::optional<InputDeviceSensorType> getSensorType(const char* token) { - auto it = SENSOR_LIST.find(std::string(token)); + auto it = SENSOR_LIST.find(token); if (it == SENSOR_LIST.end()) { return std::nullopt; } @@ -581,8 +585,8 @@ status_t KeyLayoutMap::Parser::parseSensor() { int32_t sensorDataIndex = indexOpt.value(); #if DEBUG_PARSER - ALOGD("Parsed sensor: abs code=%d, sensorType=%d, sensorDataIndex=%d.", code, - NamedEnum::string(sensorType).c_str(), sensorDataIndex); + ALOGD("Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code, + ftl::enum_string(sensorType).c_str(), sensorDataIndex); #endif Sensor sensor; @@ -591,4 +595,5 @@ status_t KeyLayoutMap::Parser::parseSensor() { map.emplace(code, sensor); return NO_ERROR; } -}; + +} // namespace android diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 5d1f2c3bfc..8db5bf1289 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -56,6 +56,7 @@ protected: void PublishAndConsumeFocusEvent(); void PublishAndConsumeCaptureEvent(); void PublishAndConsumeDragEvent(); + void PublishAndConsumeTouchModeEvent(); }; TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { @@ -413,6 +414,46 @@ void InputPublisherAndConsumerTest::PublishAndConsumeDragEvent() { << "finished signal's consume time should be greater than publish time"; } +void InputPublisherAndConsumerTest::PublishAndConsumeTouchModeEvent() { + status_t status; + + constexpr uint32_t seq = 15; + int32_t eventId = InputEvent::nextId(); + constexpr bool touchModeEnabled = true; + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + + status = mPublisher->publishTouchModeEvent(seq, eventId, touchModeEnabled); + ASSERT_EQ(OK, status) << "publisher publishTouchModeEvent should return OK"; + + uint32_t consumeSeq; + InputEvent* event; + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); + ASSERT_EQ(OK, status) << "consumer consume should return OK"; + + ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; + ASSERT_EQ(AINPUT_EVENT_TYPE_TOUCH_MODE, event->getType()) + << "consumer should have returned a touch mode event"; + + const TouchModeEvent& touchModeEvent = static_cast<const TouchModeEvent&>(*event); + EXPECT_EQ(seq, consumeSeq); + EXPECT_EQ(eventId, touchModeEvent.getId()); + EXPECT_EQ(touchModeEnabled, touchModeEvent.isInTouchMode()); + + status = mConsumer->sendFinishedSignal(seq, true); + ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; + + Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse(); + ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK"; + ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result)); + const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result); + ASSERT_EQ(seq, finish.seq) + << "receiveConsumerResponse should have returned the original sequence number"; + ASSERT_TRUE(finish.handled) + << "receiveConsumerResponse should have set handled to consumer's reply"; + ASSERT_GE(finish.consumeTime, publishTime) + << "finished signal's consume time should be greater than publish time"; +} + TEST_F(InputPublisherAndConsumerTest, SendTimeline) { const int32_t inputEventId = 20; std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline; @@ -449,6 +490,10 @@ TEST_F(InputPublisherAndConsumerTest, PublishDragEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent()); } +TEST_F(InputPublisherAndConsumerTest, PublishTouchModeEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent()); +} + TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) { status_t status; const size_t pointerCount = 1; @@ -520,6 +565,7 @@ TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent()); } } // namespace android diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 59fed1fb41..18289a5f11 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -102,6 +102,10 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Timeline, eventId, 0); CHECK_OFFSET(InputMessage::Body::Timeline, empty, 4); CHECK_OFFSET(InputMessage::Body::Timeline, graphicsTimeline, 8); + + CHECK_OFFSET(InputMessage::Body::TouchMode, eventId, 0); + CHECK_OFFSET(InputMessage::Body::TouchMode, isInTouchMode, 4); + CHECK_OFFSET(InputMessage::Body::TouchMode, empty, 5); } void TestHeaderSize() { @@ -123,6 +127,7 @@ void TestBodySize() { static_assert(sizeof(InputMessage::Body::Focus) == 8); static_assert(sizeof(InputMessage::Body::Capture) == 8); static_assert(sizeof(InputMessage::Body::Drag) == 16); + static_assert(sizeof(InputMessage::Body::TouchMode) == 8); // Timeline static_assert(GraphicsTimeline::SIZE == 2); static_assert(sizeof(InputMessage::Body::Timeline) == 24); diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp index 2dd6c4fcaa..ba71960751 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -192,6 +192,7 @@ Choreographer::~Choreographer() { // Only poke DisplayManagerGlobal to unregister if we previously registered // callbacks. if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) { + gChoreographers.registeredToDisplayManager = false; JNIEnv* env = getJniEnv(); if (env == nullptr) { ALOGW("JNI environment is unavailable, skipping choreographer cleanup"); @@ -305,8 +306,9 @@ void Choreographer::scheduleLatestConfigRequest() { // Fortunately, these events are small so sending packets across the // socket should be atomic across processes. DisplayEventReceiver::Event event; - event.header = DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL, - PhysicalDisplayId(0), systemTime()}; + event.header = + DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL, + PhysicalDisplayId::fromPort(0), systemTime()}; injectEvent(event); } } diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h index 6eaa84e225..bac44c978a 100644 --- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h +++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h @@ -272,10 +272,11 @@ public: status_t attachToContext(uint32_t tex); sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace, - float* outTransformMatrix, bool* outQueueEmpty, + float* outTransformMatrix, uint32_t* outTransform, + bool* outQueueEmpty, SurfaceTexture_createReleaseFence createFence, SurfaceTexture_fenceWait fenceWait, - void* fencePassThroughHandle); + void* fencePassThroughHandle, ARect* currentCrop); /** * takeConsumerOwnership attaches a SurfaceTexture that is currently in the diff --git a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h index 85fe42f6fd..e85009c206 100644 --- a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h +++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h @@ -84,10 +84,11 @@ typedef int (*ASurfaceTexture_fenceWait)(int fence, void* fencePassThroughHandle */ AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid, android_dataspace* outDataspace, - float* outTransformMatrix, bool* outNewContent, + float* outTransformMatrix, uint32_t* outTransform, + bool* outNewContent, ASurfaceTexture_createReleaseFence createFence, ASurfaceTexture_fenceWait fenceWait, - void* fencePassThroughHandle); + void* fencePassThroughHandle, ARect* currentCrop); } // namespace android diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp index 62db6d069f..3535e67895 100644 --- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp +++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp @@ -464,10 +464,11 @@ void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const { } sp<GraphicBuffer> SurfaceTexture::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace, - float* outTransformMatrix, bool* outQueueEmpty, + float* outTransformMatrix, uint32_t* outTransform, + bool* outQueueEmpty, SurfaceTexture_createReleaseFence createFence, SurfaceTexture_fenceWait fenceWait, - void* fencePassThroughHandle) { + void* fencePassThroughHandle, ARect* currentCrop) { Mutex::Autolock _l(mMutex); sp<GraphicBuffer> buffer; @@ -484,6 +485,8 @@ sp<GraphicBuffer> SurfaceTexture::dequeueBuffer(int* outSlotid, android_dataspac buffer = mImageConsumer.dequeueBuffer(outSlotid, outDataspace, outQueueEmpty, *this, createFence, fenceWait, fencePassThroughHandle); memcpy(outTransformMatrix, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); + *outTransform = mCurrentTransform; + *currentCrop = mCurrentCrop; return buffer; } diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp index c214ab7718..cc0a12d2c6 100644 --- a/libs/nativedisplay/surfacetexture/surface_texture.cpp +++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp @@ -194,15 +194,18 @@ void ASurfaceTexture_releaseConsumerOwnership(ASurfaceTexture* texture) { AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid, android_dataspace* outDataspace, - float* outTransformMatrix, bool* outNewContent, + float* outTransformMatrix, uint32_t* outTransform, + bool* outNewContent, ASurfaceTexture_createReleaseFence createFence, - ASurfaceTexture_fenceWait fenceWait, void* handle) { + ASurfaceTexture_fenceWait fenceWait, void* handle, + ARect* currentCrop) { sp<GraphicBuffer> buffer; *outNewContent = false; bool queueEmpty; do { buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, outTransformMatrix, - &queueEmpty, createFence, fenceWait, handle); + outTransform, &queueEmpty, createFence, fenceWait, + handle, currentCrop); if (!queueEmpty) { *outNewContent = true; } diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h index d93a84cd25..d5e7cb299b 100644 --- a/libs/nativewindow/include/android/hardware_buffer.h +++ b/libs/nativewindow/include/android/hardware_buffer.h @@ -556,6 +556,7 @@ int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* _Nonnull buffer, uint64_t us int32_t* _Nonnull outBytesPerPixel, int32_t* _Nonnull outBytesPerStride) __INTRODUCED_IN(29); + /** * Get the system wide unique id for an AHardwareBuffer. * diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index 7f0113500d..0bc2b5d312 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -1089,10 +1089,13 @@ static inline int ANativeWindow_getLastQueuedBuffer2(ANativeWindow* window, /** * Retrieves an identifier for the next frame to be queued by this window. * - * \return the next frame id. + * Frame ids start at 1 and are incremented on each new frame until the underlying surface changes, + * in which case the frame id is reset to 1. + * + * \return the next frame id (0 being uninitialized). */ -static inline int64_t ANativeWindow_getNextFrameId(ANativeWindow* window) { - int64_t value; +static inline uint64_t ANativeWindow_getNextFrameId(ANativeWindow* window) { + uint64_t value; window->perform(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID, &value); return value; } diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index 0c5a851c41..2174df5515 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -95,5 +95,16 @@ void RenderEngine::validateOutputBufferUsage(const sp<GraphicBuffer>& buffer) { "output buffer not gpu writeable"); } +std::future<RenderEngineResult> RenderEngine::drawLayers( + const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, + const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, + base::unique_fd&& bufferFence) { + const auto resultPromise = std::make_shared<std::promise<RenderEngineResult>>(); + std::future<RenderEngineResult> resultFuture = resultPromise->get_future(); + drawLayersInternal(std::move(resultPromise), display, layers, buffer, useFramebufferCache, + std::move(bufferFence)); + return resultFuture; +} + } // namespace renderengine } // namespace android diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index 467f848237..2375cb7bf1 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -1078,15 +1078,16 @@ EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer return image; } -status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, - const std::vector<const LayerSettings*>& layers, - const std::shared_ptr<ExternalTexture>& buffer, - const bool useFramebufferCache, base::unique_fd&& bufferFence, - base::unique_fd* drawFence) { +void GLESRenderEngine::drawLayersInternal( + const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise, + const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, + const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, + base::unique_fd&& bufferFence) { ATRACE_CALL(); if (layers.empty()) { ALOGV("Drawing empty layer stack"); - return NO_ERROR; + resultPromise->set_value({NO_ERROR, base::unique_fd()}); + return; } if (bufferFence.get() >= 0) { @@ -1100,7 +1101,8 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, if (buffer == nullptr) { ALOGE("No output buffer provided. Aborting GPU composition."); - return BAD_VALUE; + resultPromise->set_value({BAD_VALUE, base::unique_fd()}); + return; } validateOutputBufferUsage(buffer->getBuffer()); @@ -1128,7 +1130,8 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", buffer->getBuffer()->handle); checkErrors(); - return fbo->getStatus(); + resultPromise->set_value({fbo->getStatus(), base::unique_fd()}); + return; } setViewportAndProjection(display.physicalDisplay, display.clip); } else { @@ -1139,7 +1142,8 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).", buffer->getBuffer()->handle); checkErrors(); - return status; + resultPromise->set_value({status, base::unique_fd()}); + return; } } @@ -1156,10 +1160,6 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, const mat4 projectionMatrix = ui::Transform(display.orientation).asMatrix4() * mState.projectionMatrix; - if (!display.clearRegion.isEmpty()) { - glDisable(GL_BLEND); - fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0); - } Mesh mesh = Mesh::Builder() .setPrimitive(Mesh::TRIANGLE_FAN) @@ -1176,7 +1176,8 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).", buffer->getBuffer()->handle); checkErrors("Can't render first blur pass"); - return status; + resultPromise->set_value({status, base::unique_fd()}); + return; } if (blurLayers.size() == 0) { @@ -1198,7 +1199,8 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", buffer->getBuffer()->handle); checkErrors("Can't bind native framebuffer"); - return status; + resultPromise->set_value({status, base::unique_fd()}); + return; } status = mBlurFilter->render(blurLayersSize > 1); @@ -1206,7 +1208,8 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).", buffer->getBuffer()->handle); checkErrors("Can't render blur filter"); - return status; + resultPromise->set_value({status, base::unique_fd()}); + return; } } @@ -1293,30 +1296,31 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, } } - if (drawFence != nullptr) { - *drawFence = flush(); - } + base::unique_fd drawFence = flush(); + // If flush failed or we don't support native fences, we need to force the // gl command stream to be executed. - if (drawFence == nullptr || drawFence->get() < 0) { + if (drawFence.get() < 0) { bool success = finish(); if (!success) { ALOGE("Failed to flush RenderEngine commands"); checkErrors(); // Chances are, something illegal happened (either the caller passed // us bad parameters, or we messed up our shader generation). - return INVALID_OPERATION; + resultPromise->set_value({INVALID_OPERATION, std::move(drawFence)}); + return; } mLastDrawFence = nullptr; } else { // The caller takes ownership of drawFence, so we need to duplicate the // fd here. - mLastDrawFence = new Fence(dup(drawFence->get())); + mLastDrawFence = new Fence(dup(drawFence.get())); } mPriorResourcesCleaned = false; checkErrors(); - return NO_ERROR; + resultPromise->set_value({NO_ERROR, std::move(drawFence)}); + return; } void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) { diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index 14627ce0ce..c4adfdf752 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -63,11 +63,6 @@ public: bool isProtected() const override { return mInProtectedContext; } bool supportsProtectedContent() const override; void useProtectedContext(bool useProtectedContext) override; - status_t drawLayers(const DisplaySettings& display, - const std::vector<const LayerSettings*>& layers, - const std::shared_ptr<ExternalTexture>& buffer, - const bool useFramebufferCache, base::unique_fd&& bufferFence, - base::unique_fd* drawFence) override; void cleanupPostRender() override; int getContextPriority() override; bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; } @@ -107,6 +102,11 @@ protected: EXCLUDES(mRenderingMutex); void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex); bool canSkipPostRenderCleanup() const override; + void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise, + const DisplaySettings& display, + const std::vector<const LayerSettings*>& layers, + const std::shared_ptr<ExternalTexture>& buffer, + const bool useFramebufferCache, base::unique_fd&& bufferFence) override; private: friend class BindNativeBufferAsFramebuffer; diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h index 53fa622ad8..d395d06b95 100644 --- a/libs/renderengine/include/renderengine/DisplaySettings.h +++ b/libs/renderengine/include/renderengine/DisplaySettings.h @@ -51,10 +51,6 @@ struct DisplaySettings { // dataspace, in non-linear space. mat4 colorTransform = mat4(); - // Region that will be cleared to (0, 0, 0, 1) prior to rendering. - // This is specified in layer-stack space. - Region clearRegion = Region::INVALID_REGION; - // An additional orientation flag to be applied after clipping the output. // By way of example, this may be used for supporting fullscreen screenshot // capture of a device in landscape while the buffer is in portrait @@ -68,8 +64,7 @@ struct DisplaySettings { static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) { return lhs.physicalDisplay == rhs.physicalDisplay && lhs.clip == rhs.clip && lhs.maxLuminance == rhs.maxLuminance && lhs.outputDataspace == rhs.outputDataspace && - lhs.colorTransform == rhs.colorTransform && - lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.orientation == rhs.orientation; + lhs.colorTransform == rhs.colorTransform && lhs.orientation == rhs.orientation; } // Defining PrintTo helps with Google Tests. @@ -84,9 +79,6 @@ static inline void PrintTo(const DisplaySettings& settings, ::std::ostream* os) PrintTo(settings.outputDataspace, os); *os << "\n .colorTransform = " << settings.colorTransform; *os << "\n .clearRegion = "; - PrintTo(settings.clearRegion, os); - *os << "\n .orientation = " << settings.orientation; - *os << "\n}"; } } // namespace renderengine diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 967cf5deff..701c1f2071 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -68,6 +68,7 @@ class Image; class Mesh; class Texture; struct RenderEngineCreationArgs; +struct RenderEngineResult; namespace threaded { class RenderEngineThreaded; @@ -156,17 +157,12 @@ public: // parameter does nothing. // @param bufferFence Fence signalling that the buffer is ready to be drawn // to. - // @param drawFence A pointer to a fence, which will fire when the buffer - // has been drawn to and is ready to be examined. The fence will be - // initialized by this method. The caller will be responsible for owning the - // fence. - // @return An error code indicating whether drawing was successful. For - // now, this always returns NO_ERROR. - virtual status_t drawLayers(const DisplaySettings& display, - const std::vector<const LayerSettings*>& layers, - const std::shared_ptr<ExternalTexture>& buffer, - const bool useFramebufferCache, base::unique_fd&& bufferFence, - base::unique_fd* drawFence) = 0; + // @return A future object of RenderEngineResult struct indicating whether + // drawing was successful in async mode. + virtual std::future<RenderEngineResult> drawLayers( + const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, + const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, + base::unique_fd&& bufferFence); // Clean-up method that should be called on the main thread after the // drawFence returned by drawLayers fires. This method will free up @@ -232,6 +228,12 @@ protected: friend class threaded::RenderEngineThreaded; friend class RenderEngineTest_cleanupPostRender_cleansUpOnce_Test; const RenderEngineType mRenderEngineType; + + virtual void drawLayersInternal( + const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise, + const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, + const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, + base::unique_fd&& bufferFence) = 0; }; struct RenderEngineCreationArgs { @@ -318,6 +320,13 @@ private: RenderEngine::RenderEngineType::SKIA_GL_THREADED; }; +struct RenderEngineResult { + // status indicates if drawing is successful + status_t status; + // drawFence will fire when the buffer has been drawn to and is ready to be examined. + base::unique_fd drawFence; +}; + } // namespace renderengine } // namespace android diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index 0be3ba6b11..a7e6809223 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -47,10 +47,15 @@ public: MOCK_METHOD1(useProtectedContext, void(bool)); MOCK_METHOD0(cleanupPostRender, void()); MOCK_CONST_METHOD0(canSkipPostRenderCleanup, bool()); - MOCK_METHOD6(drawLayers, - status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&, - const std::shared_ptr<ExternalTexture>&, const bool, base::unique_fd&&, - base::unique_fd*)); + MOCK_METHOD5(drawLayers, + std::future<RenderEngineResult>(const DisplaySettings&, + const std::vector<const LayerSettings*>&, + const std::shared_ptr<ExternalTexture>&, + const bool, base::unique_fd&&)); + MOCK_METHOD6(drawLayersInternal, + void(const std::shared_ptr<std::promise<RenderEngineResult>>&&, + const DisplaySettings&, const std::vector<const LayerSettings*>&, + const std::shared_ptr<ExternalTexture>&, const bool, base::unique_fd&&)); MOCK_METHOD0(cleanFramebufferCache, void()); MOCK_METHOD0(getContextPriority, int()); MOCK_METHOD0(supportsBackgroundBlur, bool()); diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp index ae8f2384c4..c4fa1bb091 100644 --- a/libs/renderengine/skia/Cache.cpp +++ b/libs/renderengine/skia/Cache.cpp @@ -96,24 +96,26 @@ static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettin }; auto layers = std::vector<const LayerSettings*>{&layer, &caster}; - // When sourceDataspace matches dest, the general shadow fragment shader doesn't - // have color correction added. - // independently, when it is not srgb, the *vertex* shader has color correction added. - // This may be a bug, but the shader still needs to be cached as it is triggered - // during youtube pip. - for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) { - layer.sourceDataspace = dataspace; - // The 2nd matrix, which has different scales for x and y, will - // generate the slower (more general case) shadow shader - for (auto transform : {mat4(), kScaleAndTranslate, kFlip}) { - layer.geometry.positionTransform = transform; - caster.geometry.positionTransform = transform; - for (bool translucent : {false, true}){ - layer.shadow.casterIsTranslucent = translucent; - renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd(), nullptr); - } - } + // Four combinations of settings are used (two transforms here, and drawShadowLayers is + // called with two different destination data spaces) They're all rounded rect. + // Three of these are cache misses that generate new shaders. + // The first combination generates a short and simple shadow shader. + // The second combination, flip transform, generates two shaders. The first appears to involve + // gaussian_fp. The second is a long and general purpose shadow shader with a device space + // transformation stage. + // The third combination is a cache hit, nothing new. + // The fourth combination, flip transform with a non-SRGB destination dataspace, is new. + // It is unique in that nearly everything is done in the vertex shader, and that vertex shader + // requires color correction. This is triggered differently from every other instance of color + // correction. All other instances are triggered when src and dst dataspaces differ, while + // this one is triggered by the destination being non-srgb. Apparently since the third + // combination is a cache hit, this color correction is only added when the vertex shader is + // doing something non-trivial. + for (auto transform : {mat4(), kFlip}) { + layer.geometry.positionTransform = transform; + caster.geometry.positionTransform = transform; + renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, + base::unique_fd()); } } @@ -151,7 +153,7 @@ static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySetting for (auto alpha : {half(.2f), half(1.0f)}) { layer.alpha = alpha; renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd(), nullptr); + base::unique_fd()); } } } @@ -180,7 +182,7 @@ static void drawSolidLayers(SkiaRenderEngine* renderengine, const DisplaySetting for (float roundedCornersRadius : {0.0f, 50.f}) { layer.geometry.roundedCornersRadius = roundedCornersRadius; renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd(), nullptr); + base::unique_fd()); } } } @@ -204,7 +206,7 @@ static void drawBlurLayers(SkiaRenderEngine* renderengine, const DisplaySettings for (int radius : {9, 60}) { layer.backgroundBlurRadius = radius; renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd(), nullptr); + base::unique_fd()); } } @@ -251,7 +253,7 @@ static void drawClippedLayers(SkiaRenderEngine* renderengine, const DisplaySetti for (float alpha : {0.5f, 1.f}) { layer.alpha = alpha, renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd(), nullptr); + base::unique_fd()); } } } @@ -288,8 +290,7 @@ static void drawPIPImageLayer(SkiaRenderEngine* renderengine, const DisplaySetti }; auto layers = std::vector<const LayerSettings*>{&layer}; - renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd(), nullptr); + renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd()); } static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display, @@ -317,8 +318,7 @@ static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySett }; auto layers = std::vector<const LayerSettings*>{&layer}; - renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd(), nullptr); + renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd()); } // @@ -381,6 +381,7 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) { ExternalTexture::Usage::WRITEABLE); drawHolePunchLayer(renderengine, display, dstTexture); drawSolidLayers(renderengine, display, dstTexture); + drawShadowLayers(renderengine, display, srcTexture); drawShadowLayers(renderengine, p3Display, srcTexture); @@ -388,7 +389,10 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) { drawBlurLayers(renderengine, display, dstTexture); } - // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE; + // The majority of skia shaders needed by RenderEngine are related to sampling images. + // These need to be generated with various source textures. + // Make a list of applicable sources. + // GRALLOC_USAGE_HW_TEXTURE should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE. const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE; sp<GraphicBuffer> externalBuffer = new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, @@ -396,24 +400,24 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) { const auto externalTexture = std::make_shared<ExternalTexture>(externalBuffer, *renderengine, ExternalTexture::Usage::READABLE); + std::vector<const std::shared_ptr<ExternalTexture>> textures = + {srcTexture, externalTexture}; - // Another external texture with a different pixel format triggers useIsOpaqueWorkaround + // Another external texture with a different pixel format triggers useIsOpaqueWorkaround. + // It doesn't have to be f16, but it can't be the usual 8888. sp<GraphicBuffer> f16ExternalBuffer = new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_FP16, 1, usageExternal, "primeShaderCache_external_f16"); - const auto f16ExternalTexture = + // The F16 texture may not be usable on all devices, so check first that it was created. + status_t error = f16ExternalBuffer->initCheck(); + if (!error) { + const auto f16ExternalTexture = std::make_shared<ExternalTexture>(f16ExternalBuffer, *renderengine, ExternalTexture::Usage::READABLE); + textures.push_back(f16ExternalTexture); + } - // The majority of shaders are related to sampling images. - // These need to be generated with various source textures - // The F16 texture may not be usable on all devices, so check first that it was created with - // the requested usage bit. - auto textures = {srcTexture, externalTexture}; - auto texturesWithF16 = {srcTexture, externalTexture, f16ExternalTexture}; - bool canUsef16 = f16ExternalBuffer->getUsage() & GRALLOC_USAGE_HW_TEXTURE; - - for (auto texture : canUsef16 ? texturesWithF16 : textures) { + for (auto texture : textures) { drawImageLayers(renderengine, display, dstTexture, texture); // Draw layers for b/185569240. drawClippedLayers(renderengine, display, dstTexture, texture); @@ -421,6 +425,16 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) { drawPIPImageLayer(renderengine, display, dstTexture, externalTexture); + // draw one final layer synchronously to force GL submit + LayerSettings layer{ + .source = PixelSource{.solidColor = half3(0.f, 0.f, 0.f)}, + }; + auto layers = std::vector<const LayerSettings*>{&layer}; + // call get() to make it synchronous + renderengine + ->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd()) + .get(); + const nsecs_t timeAfter = systemTime(); const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6; const int shadersCompiled = renderengine->reportShadersCompiled(); diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index e42b5b9e79..cb686a643a 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -727,22 +727,24 @@ static SkRRect getBlurRRect(const BlurRegion& region) { return roundedRect; } -status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, - const std::vector<const LayerSettings*>& layers, - const std::shared_ptr<ExternalTexture>& buffer, - const bool /*useFramebufferCache*/, - base::unique_fd&& bufferFence, base::unique_fd* drawFence) { +void SkiaGLRenderEngine::drawLayersInternal( + const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise, + const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, + const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/, + base::unique_fd&& bufferFence) { ATRACE_NAME("SkiaGL::drawLayers"); std::lock_guard<std::mutex> lock(mRenderingMutex); if (layers.empty()) { ALOGV("Drawing empty layer stack"); - return NO_ERROR; + resultPromise->set_value({NO_ERROR, base::unique_fd()}); + return; } if (buffer == nullptr) { ALOGE("No output buffer provided. Aborting GPU composition."); - return BAD_VALUE; + resultPromise->set_value({BAD_VALUE, base::unique_fd()}); + return; } validateOutputBufferUsage(buffer->getBuffer()); @@ -774,7 +776,8 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get()); if (dstCanvas == nullptr) { ALOGE("Cannot acquire canvas from Skia."); - return BAD_VALUE; + resultPromise->set_value({BAD_VALUE, base::unique_fd()}); + return; } // setup color filter if necessary @@ -821,28 +824,6 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, canvas->clear(SK_ColorTRANSPARENT); initCanvas(canvas, display); - // TODO: clearRegion was required for SurfaceView when a buffer is not yet available but the - // view is still on-screen. The clear region could be re-specified as a black color layer, - // however. - if (!display.clearRegion.isEmpty()) { - ATRACE_NAME("ClearRegion"); - size_t numRects = 0; - Rect const* rects = display.clearRegion.getArray(&numRects); - SkIRect skRects[numRects]; - for (int i = 0; i < numRects; ++i) { - skRects[i] = - SkIRect::MakeLTRB(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom); - } - SkRegion clearRegion; - SkPaint paint; - sk_sp<SkShader> shader = - SkShaders::Color(SkColor4f{.fR = 0., .fG = 0., .fB = 0., .fA = 1.0}, - toSkColorSpace(dstDataspace)); - paint.setShader(shader); - clearRegion.setRects(skRects, numRects); - canvas->drawRegion(clearRegion, paint); - } - for (const auto& layer : layers) { ATRACE_FORMAT("DrawLayer: %s", layer->name.c_str()); @@ -1131,13 +1112,11 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, activeSurface->flush(); } - if (drawFence != nullptr) { - *drawFence = flush(); - } + base::unique_fd drawFence = flush(); // If flush failed or we don't support native fences, we need to force the // gl command stream to be executed. - bool requireSync = drawFence == nullptr || drawFence->get() < 0; + bool requireSync = drawFence.get() < 0; if (requireSync) { ATRACE_BEGIN("Submit(sync=true)"); } else { @@ -1149,11 +1128,13 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, ALOGE("Failed to flush RenderEngine commands"); // Chances are, something illegal happened (either the caller passed // us bad parameters, or we messed up our shader generation). - return INVALID_OPERATION; + resultPromise->set_value({INVALID_OPERATION, std::move(drawFence)}); + return; } // checkErrors(); - return NO_ERROR; + resultPromise->set_value({NO_ERROR, std::move(drawFence)}); + return; } inline SkRect SkiaGLRenderEngine::getSkRect(const FloatRect& rect) { @@ -1164,6 +1145,73 @@ inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) { return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); } +/** + * Verifies that common, simple bounds + clip combinations can be converted into + * a single RRect draw call returning true if possible. If true the radii parameter + * will be filled with the correct radii values that combined with bounds param will + * produce the insected roundRect. If false, the returned state of the radii param is undefined. + */ +static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop, + const SkRect& insetCrop, float cornerRadius, + SkVector radii[4]) { + const bool leftEqual = bounds.fLeft == crop.fLeft; + const bool topEqual = bounds.fTop == crop.fTop; + const bool rightEqual = bounds.fRight == crop.fRight; + const bool bottomEqual = bounds.fBottom == crop.fBottom; + + // In the event that the corners of the bounds only partially align with the crop we + // need to ensure that the resulting shape can still be represented as a round rect. + // In particular the round rect implementation will scale the value of all corner radii + // if the sum of the radius along any edge is greater than the length of that edge. + // See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap + const bool requiredWidth = bounds.width() > (cornerRadius * 2); + const bool requiredHeight = bounds.height() > (cornerRadius * 2); + if (!requiredWidth || !requiredHeight) { + return false; + } + + // Check each cropped corner to ensure that it exactly matches the crop or its corner is + // contained within the cropped shape and does not need rounded. + // compute the UpperLeft corner radius + if (leftEqual && topEqual) { + radii[0].set(cornerRadius, cornerRadius); + } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) || + (topEqual && bounds.fLeft >= insetCrop.fLeft)) { + radii[0].set(0, 0); + } else { + return false; + } + // compute the UpperRight corner radius + if (rightEqual && topEqual) { + radii[1].set(cornerRadius, cornerRadius); + } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) || + (topEqual && bounds.fRight <= insetCrop.fRight)) { + radii[1].set(0, 0); + } else { + return false; + } + // compute the BottomRight corner radius + if (rightEqual && bottomEqual) { + radii[2].set(cornerRadius, cornerRadius); + } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) || + (bottomEqual && bounds.fRight <= insetCrop.fRight)) { + radii[2].set(0, 0); + } else { + return false; + } + // compute the BottomLeft corner radius + if (leftEqual && bottomEqual) { + radii[3].set(cornerRadius, cornerRadius); + } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) || + (bottomEqual && bounds.fLeft >= insetCrop.fLeft)) { + radii[3].set(0, 0); + } else { + return false; + } + + return true; +} + inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect, const FloatRect& cropRect, const float cornerRadius) { @@ -1181,66 +1229,20 @@ inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const Fl // converting them to a single RRect draw. It is possible there are other cases // that can be converted. if (crop.contains(bounds)) { - bool intersectionIsRoundRect = true; - // check each cropped corner to ensure that it exactly matches the crop or is full - SkVector radii[4]; - const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius); - - const bool leftEqual = bounds.fLeft == crop.fLeft; - const bool topEqual = bounds.fTop == crop.fTop; - const bool rightEqual = bounds.fRight == crop.fRight; - const bool bottomEqual = bounds.fBottom == crop.fBottom; - - // compute the UpperLeft corner radius - if (leftEqual && topEqual) { - radii[0].set(cornerRadius, cornerRadius); - } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) || - (topEqual && bounds.fLeft >= insetCrop.fLeft) || - insetCrop.contains(bounds.fLeft, bounds.fTop)) { - radii[0].set(0, 0); - } else { - intersectionIsRoundRect = false; - } - // compute the UpperRight corner radius - if (rightEqual && topEqual) { - radii[1].set(cornerRadius, cornerRadius); - } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) || - (topEqual && bounds.fRight <= insetCrop.fRight) || - insetCrop.contains(bounds.fRight, bounds.fTop)) { - radii[1].set(0, 0); - } else { - intersectionIsRoundRect = false; - } - // compute the BottomRight corner radius - if (rightEqual && bottomEqual) { - radii[2].set(cornerRadius, cornerRadius); - } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) || - (bottomEqual && bounds.fRight <= insetCrop.fRight) || - insetCrop.contains(bounds.fRight, bounds.fBottom)) { - radii[2].set(0, 0); - } else { - intersectionIsRoundRect = false; - } - // compute the BottomLeft corner radius - if (leftEqual && bottomEqual) { - radii[3].set(cornerRadius, cornerRadius); - } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) || - (bottomEqual && bounds.fLeft >= insetCrop.fLeft) || - insetCrop.contains(bounds.fLeft, bounds.fBottom)) { - radii[3].set(0, 0); - } else { - intersectionIsRoundRect = false; + if (insetCrop.contains(bounds)) { + return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required } - if (intersectionIsRoundRect) { + SkVector radii[4]; + if (intersectionIsRoundRect(bounds, crop, insetCrop, cornerRadius, radii)) { SkRRect intersectionBounds; intersectionBounds.setRectRadii(bounds, radii); return {intersectionBounds, clip}; } } - // we didn't it any of our fast paths so set the clip to the cropRect + // we didn't hit any of our fast paths so set the clip to the cropRect clip.setRectXY(crop, cornerRadius, cornerRadius); } diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h index e1162f5574..e010c35c13 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.h +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -54,11 +54,6 @@ public: ~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex); std::future<void> primeCache() override; - status_t drawLayers(const DisplaySettings& display, - const std::vector<const LayerSettings*>& layers, - const std::shared_ptr<ExternalTexture>& buffer, - const bool useFramebufferCache, base::unique_fd&& bufferFence, - base::unique_fd* drawFence) override; void cleanupPostRender() override; void cleanFramebufferCache() override{}; int getContextPriority() override; @@ -77,6 +72,11 @@ protected: void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override; void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override; bool canSkipPostRenderCleanup() const override; + void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise, + const DisplaySettings& display, + const std::vector<const LayerSettings*>& layers, + const std::shared_ptr<ExternalTexture>& buffer, + const bool useFramebufferCache, base::unique_fd&& bufferFence) override; private: static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig); diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h index 7cd9eca976..f61653b940 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -44,14 +44,6 @@ public: virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{}; virtual bool isProtected() const override { return false; } // mInProtectedContext; } virtual bool supportsProtectedContent() const override { return false; }; - virtual status_t drawLayers(const DisplaySettings& /*display*/, - const std::vector<const LayerSettings*>& /*layers*/, - const std::shared_ptr<ExternalTexture>& /*buffer*/, - const bool /*useFramebufferCache*/, - base::unique_fd&& /*bufferFence*/, - base::unique_fd* /*drawFence*/) override { - return 0; - }; virtual int getContextPriority() override { return 0; } virtual void assertShadersCompiled(int numShaders) {} virtual int reportShadersCompiled() { return 0; } @@ -60,6 +52,14 @@ protected: virtual void mapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/, bool /*isRenderable*/) override = 0; virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/) override = 0; + + virtual void drawLayersInternal( + const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise, + const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, + const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, + base::unique_fd&& bufferFence) override { + resultPromise->set_value({NO_ERROR, base::unique_fd()}); + }; }; } // namespace skia diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp index 7c5bee9450..6af6195680 100644 --- a/libs/renderengine/skia/filters/BlurFilter.cpp +++ b/libs/renderengine/skia/filters/BlurFilter.cpp @@ -39,15 +39,15 @@ BlurFilter::BlurFilter() { uniform float2 in_maxSizeXY; half4 main(float2 xy) { - half4 c = sample(input, xy); - c += sample(input, float2( clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x), - clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y))); - c += sample(input, float2( clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x), - clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y))); - c += sample(input, float2( clamp( -in_blurOffset.x + xy.x, 0, in_maxSizeXY.x), - clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y))); - c += sample(input, float2( clamp( -in_blurOffset.x + xy.x, 0, in_maxSizeXY.x), - clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y))); + half4 c = input.eval(xy); + c += input.eval(float2(clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x), + clamp( in_blurOffset.y + xy.y, 0, in_maxSizeXY.y))); + c += input.eval(float2(clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x), + clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y))); + c += input.eval(float2(clamp(-in_blurOffset.x + xy.x, 0, in_maxSizeXY.x), + clamp( in_blurOffset.y + xy.y, 0, in_maxSizeXY.y))); + c += input.eval(float2(clamp(-in_blurOffset.x + xy.x, 0, in_maxSizeXY.x), + clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y))); return half4(c.rgb * 0.2, 1.0); } @@ -65,7 +65,7 @@ BlurFilter::BlurFilter() { uniform float mixFactor; half4 main(float2 xy) { - return half4(mix(sample(originalInput, xy), sample(blurredInput, xy), mixFactor)); + return half4(mix(originalInput.eval(xy), blurredInput.eval(xy), mixFactor)); } )"); diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp index fc45af945b..dc5fe175ab 100644 --- a/libs/renderengine/skia/filters/LinearEffect.cpp +++ b/libs/renderengine/skia/filters/LinearEffect.cpp @@ -391,7 +391,7 @@ static void generateEffectiveOOTF(bool undoPremultipliedAlpha, SkString& shader) shader.append(R"( uniform shader input; half4 main(float2 xy) { - float4 c = float4(sample(input, xy)); + float4 c = float4(input.eval(xy)); )"); if (undoPremultipliedAlpha) { shader.append(R"( diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.cpp b/libs/renderengine/skia/filters/StretchShaderFactory.cpp index 4ac5c4028b..c262e3557c 100644 --- a/libs/renderengine/skia/filters/StretchShaderFactory.cpp +++ b/libs/renderengine/skia/filters/StretchShaderFactory.cpp @@ -184,7 +184,7 @@ static const SkString stretchShader = SkString(R"( ); coord.x = (outU - uScrollX) * viewportWidth; coord.y = (outV - uScrollY) * viewportHeight; - return sample(uContentTexture, coord); + return uContentTexture.eval(coord); })"); const float INTERPOLATION_STRENGTH_VALUE = 0.7f; diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 33e3773d50..694bda65d4 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -419,17 +419,16 @@ public: void invokeDraw(renderengine::DisplaySettings settings, std::vector<const renderengine::LayerSettings*> layers) { - base::unique_fd fence; - status_t status = - mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence); - - int fd = fence.release(); - if (fd >= 0) { - sync_wait(fd, -1); - close(fd); - } + std::future<renderengine::RenderEngineResult> result = + mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd()); + ASSERT_TRUE(result.valid()); + auto [status, fence] = result.get(); ASSERT_EQ(NO_ERROR, status); + if (fence.ok()) { + sync_wait(fence.get(), -1); + } + if (layers.size() > 0 && mGLESRE != nullptr) { ASSERT_TRUE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId())); } @@ -524,10 +523,6 @@ public: void fillGreenColorBufferThenClearRegion(); - void clearLeftRegion(); - - void clearRegion(); - template <typename SourceVariant> void drawShadow(const renderengine::LayerSettings& castingLayer, const renderengine::ShadowSettings& shadow, const ubyte4& casterColor, @@ -1195,28 +1190,6 @@ void RenderEngineTest::fillBufferWithoutPremultiplyAlpha() { expectBufferColor(fullscreenRect(), 128, 0, 0, 128, 1); } -void RenderEngineTest::clearLeftRegion() { - renderengine::DisplaySettings settings; - settings.physicalDisplay = fullscreenRect(); - // Here logical space is 4x4 - settings.clip = Rect(4, 4); - settings.clearRegion = Region(Rect(2, 4)); - std::vector<const renderengine::LayerSettings*> layers; - // fake layer, without bounds should not render anything - renderengine::LayerSettings layer; - layers.push_back(&layer); - invokeDraw(settings, layers); -} - -void RenderEngineTest::clearRegion() { - // Reuse mBuffer - clearLeftRegion(); - expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 255); - expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH, - DEFAULT_DISPLAY_HEIGHT), - 0, 0, 0, 0); -} - template <typename SourceVariant> void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLayer, const renderengine::ShadowSettings& shadow, @@ -1338,30 +1311,13 @@ TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) { layer.geometry.boundaries = fullscreenRect().toFloatRect(); BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this); layers.push_back(&layer); - base::unique_fd fence; - status_t status = mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd(), &fence); + std::future<renderengine::RenderEngineResult> result = + mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd()); + ASSERT_TRUE(result.valid()); + auto [status, fence] = result.get(); ASSERT_EQ(BAD_VALUE, status); -} - -TEST_P(RenderEngineTest, drawLayers_nullOutputFence) { - initializeRenderEngine(); - - renderengine::DisplaySettings settings; - settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; - settings.physicalDisplay = fullscreenRect(); - settings.clip = fullscreenRect(); - - std::vector<const renderengine::LayerSettings*> layers; - renderengine::LayerSettings layer; - layer.geometry.boundaries = fullscreenRect().toFloatRect(); - BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this); - layer.alpha = 1.0; - layers.push_back(&layer); - - status_t status = mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr); - ASSERT_EQ(NO_ERROR, status); - expectBufferColor(fullscreenRect(), 255, 0, 0, 255); + ASSERT_FALSE(fence.ok()); } TEST_P(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) { @@ -1386,8 +1342,16 @@ TEST_P(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) { layer.alpha = 1.0; layers.push_back(&layer); - status_t status = mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr); + std::future<renderengine::RenderEngineResult> result = + mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd()); + ASSERT_TRUE(result.valid()); + auto [status, fence] = result.get(); + ASSERT_EQ(NO_ERROR, status); + if (fence.ok()) { + sync_wait(fence.get(), -1); + } + ASSERT_FALSE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId())); expectBufferColor(fullscreenRect(), 255, 0, 0, 255); } @@ -1647,11 +1611,6 @@ TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) { fillBufferWithoutPremultiplyAlpha(); } -TEST_P(RenderEngineTest, drawLayers_clearRegion) { - initializeRenderEngine(); - clearRegion(); -} - TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { initializeRenderEngine(); @@ -1791,15 +1750,21 @@ TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) { layer.alpha = 1.0; layers.push_back(&layer); - base::unique_fd fenceOne; - mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fenceOne); - base::unique_fd fenceTwo; - mRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne), &fenceTwo); - - const int fd = fenceTwo.get(); - if (fd >= 0) { - sync_wait(fd, -1); + std::future<renderengine::RenderEngineResult> resultOne = + mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd()); + ASSERT_TRUE(resultOne.valid()); + auto [statusOne, fenceOne] = resultOne.get(); + ASSERT_EQ(NO_ERROR, statusOne); + + std::future<renderengine::RenderEngineResult> resultTwo = + mRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne)); + ASSERT_TRUE(resultTwo.valid()); + auto [statusTwo, fenceTwo] = resultTwo.get(); + ASSERT_EQ(NO_ERROR, statusTwo); + if (fenceTwo.ok()) { + sync_wait(fenceTwo.get(), -1); } + // Only cleanup the first time. EXPECT_FALSE(mRE->canSkipPostRenderCleanup()); mRE->cleanupPostRender(); @@ -1900,6 +1865,40 @@ TEST_P(RenderEngineTest, testRoundedCornersParentCrop) { expectBufferColor(Point(0, (DEFAULT_DISPLAY_HEIGHT / 2) - 1), 0, 255, 0, 255); } +TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) { + initializeRenderEngine(); + + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; + + std::vector<const renderengine::LayerSettings*> layers; + + renderengine::LayerSettings redLayer; + redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; + redLayer.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 32); + redLayer.geometry.roundedCornersRadius = 64; + redLayer.geometry.roundedCornersCrop = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 128); + // Red background. + redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f); + redLayer.alpha = 1.0f; + + layers.push_back(&redLayer); + invokeDraw(settings, layers); + + // Due to roundedCornersRadius, the top corners are untouched. + expectBufferColor(Point(0, 0), 0, 0, 0, 0); + expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0); + + // ensure that the entire height of the red layer was clipped by the rounded corners crop. + expectBufferColor(Point(0, 31), 0, 0, 0, 0); + expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 31), 0, 0, 0, 0); + + // the bottom middle should be red + expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 31), 255, 0, 0, 255); +} + TEST_P(RenderEngineTest, testClear) { initializeRenderEngine(); diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp index 830f4630e5..99250c1412 100644 --- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp +++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp @@ -169,6 +169,7 @@ TEST_F(RenderEngineThreadedTest, supportsBackgroundBlur_returnsTrue) { status_t result = mThreadedRE->supportsBackgroundBlur(); ASSERT_EQ(true, result); } + TEST_F(RenderEngineThreadedTest, drawLayers) { renderengine::DisplaySettings settings; std::vector<const renderengine::LayerSettings*> layers; @@ -177,17 +178,22 @@ TEST_F(RenderEngineThreadedTest, drawLayers) { renderengine::ExternalTexture::Usage::READABLE | renderengine::ExternalTexture::Usage::WRITEABLE); base::unique_fd bufferFence; - base::unique_fd drawFence; - - EXPECT_CALL(*mRenderEngine, drawLayers) - .WillOnce([](const renderengine::DisplaySettings&, - const std::vector<const renderengine::LayerSettings*>&, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&, base::unique_fd*) -> status_t { return NO_ERROR; }); - status_t result = mThreadedRE->drawLayers(settings, layers, buffer, false, - std::move(bufferFence), &drawFence); - ASSERT_EQ(NO_ERROR, result); + EXPECT_CALL(*mRenderEngine, drawLayersInternal) + .WillOnce([&](const std::shared_ptr<std::promise<renderengine::RenderEngineResult>>&& + resultPromise, + const renderengine::DisplaySettings&, + const std::vector<const renderengine::LayerSettings*>&, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd &&) -> void { + resultPromise->set_value({NO_ERROR, base::unique_fd()}); + }); + + std::future<renderengine::RenderEngineResult> result = + mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence)); + ASSERT_TRUE(result.valid()); + auto [status, _] = result.get(); + ASSERT_EQ(NO_ERROR, status); } } // namespace android diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index 8e666d5733..a549672259 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.cpp +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -292,7 +292,7 @@ void RenderEngineThreaded::cleanupPostRender() { { std::lock_guard lock(mThreadMutex); mFunctionCalls.push([=](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::unmapExternalTextureBuffer"); + ATRACE_NAME("REThreaded::cleanupPostRender"); instance.cleanupPostRender(); }); } @@ -304,27 +304,33 @@ bool RenderEngineThreaded::canSkipPostRenderCleanup() const { return mRenderEngine->canSkipPostRenderCleanup(); } -status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display, - const std::vector<const LayerSettings*>& layers, - const std::shared_ptr<ExternalTexture>& buffer, - const bool useFramebufferCache, - base::unique_fd&& bufferFence, - base::unique_fd* drawFence) { +void RenderEngineThreaded::drawLayersInternal( + const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise, + const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, + const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, + base::unique_fd&& bufferFence) { + resultPromise->set_value({NO_ERROR, base::unique_fd()}); + return; +} + +std::future<RenderEngineResult> RenderEngineThreaded::drawLayers( + const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, + const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, + base::unique_fd&& bufferFence) { ATRACE_CALL(); - std::promise<status_t> resultPromise; - std::future<status_t> resultFuture = resultPromise.get_future(); + const auto resultPromise = std::make_shared<std::promise<RenderEngineResult>>(); + std::future<RenderEngineResult> resultFuture = resultPromise->get_future(); { std::lock_guard lock(mThreadMutex); - mFunctionCalls.push([&resultPromise, &display, &layers, &buffer, useFramebufferCache, - &bufferFence, &drawFence](renderengine::RenderEngine& instance) { + mFunctionCalls.push([resultPromise, &display, &layers, &buffer, useFramebufferCache, + &bufferFence](renderengine::RenderEngine& instance) { ATRACE_NAME("REThreaded::drawLayers"); - status_t status = instance.drawLayers(display, layers, buffer, useFramebufferCache, - std::move(bufferFence), drawFence); - resultPromise.set_value(status); + instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer, + useFramebufferCache, std::move(bufferFence)); }); } mCondition.notify_one(); - return resultFuture.get(); + return resultFuture; } void RenderEngineThreaded::cleanFramebufferCache() { diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h index b197df7e0f..2303caa7eb 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.h +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -56,11 +56,11 @@ public: void useProtectedContext(bool useProtectedContext) override; void cleanupPostRender() override; - status_t drawLayers(const DisplaySettings& display, - const std::vector<const LayerSettings*>& layers, - const std::shared_ptr<ExternalTexture>& buffer, - const bool useFramebufferCache, base::unique_fd&& bufferFence, - base::unique_fd* drawFence) override; + std::future<RenderEngineResult> drawLayers(const DisplaySettings& display, + const std::vector<const LayerSettings*>& layers, + const std::shared_ptr<ExternalTexture>& buffer, + const bool useFramebufferCache, + base::unique_fd&& bufferFence) override; void cleanFramebufferCache() override; int getContextPriority() override; @@ -71,6 +71,11 @@ protected: void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override; void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override; bool canSkipPostRenderCleanup() const override; + void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise, + const DisplaySettings& display, + const std::vector<const LayerSettings*>& layers, + const std::shared_ptr<ExternalTexture>& buffer, + const bool useFramebufferCache, base::unique_fd&& bufferFence) override; private: void threadMain(CreateInstanceFactory factory); diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index eed58c5715..ba5a64f317 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -137,7 +137,6 @@ cc_library_shared { "HdrCapabilities.cpp", "PixelFormat.cpp", "PublicFormat.cpp", - "Size.cpp", "StaticDisplayInfo.cpp", ], diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h index f196ab901a..9120972a42 100644 --- a/libs/ui/include/ui/DisplayId.h +++ b/libs/ui/include/ui/DisplayId.h @@ -38,12 +38,22 @@ struct DisplayId { uint64_t value; + // For deserialization. + static constexpr std::optional<DisplayId> fromValue(uint64_t); + + // As above, but also upcast to Id. + template <typename Id> + static constexpr std::optional<Id> fromValue(uint64_t value) { + if (const auto id = Id::tryCast(DisplayId(value))) { + return id; + } + return {}; + } + protected: explicit constexpr DisplayId(uint64_t id) : value(id) {} }; -static_assert(sizeof(DisplayId) == sizeof(uint64_t)); - inline bool operator==(DisplayId lhs, DisplayId rhs) { return lhs.value == rhs.value; } @@ -80,11 +90,8 @@ struct PhysicalDisplayId : DisplayId { // TODO(b/162612135) Remove default constructor PhysicalDisplayId() = default; - // TODO(b/162612135) Remove constructor - explicit constexpr PhysicalDisplayId(uint64_t id) : DisplayId(id) {} constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); } - constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); } private: @@ -96,10 +103,9 @@ private: explicit constexpr PhysicalDisplayId(DisplayId other) : DisplayId(other) {} }; -static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t)); - struct VirtualDisplayId : DisplayId { using BaseId = uint32_t; + // Flag indicating that this virtual display is backed by the GPU. static constexpr uint64_t FLAG_GPU = 1ULL << 61; @@ -163,10 +169,23 @@ private: explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {} }; +constexpr std::optional<DisplayId> DisplayId::fromValue(uint64_t value) { + if (const auto id = fromValue<PhysicalDisplayId>(value)) { + return id; + } + if (const auto id = fromValue<VirtualDisplayId>(value)) { + return id; + } + return {}; +} + +static_assert(sizeof(DisplayId) == sizeof(uint64_t)); +static_assert(sizeof(HalDisplayId) == sizeof(uint64_t)); static_assert(sizeof(VirtualDisplayId) == sizeof(uint64_t)); + +static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t)); static_assert(sizeof(HalVirtualDisplayId) == sizeof(uint64_t)); static_assert(sizeof(GpuVirtualDisplayId) == sizeof(uint64_t)); -static_assert(sizeof(HalDisplayId) == sizeof(uint64_t)); } // namespace android diff --git a/libs/ui/include/ui/DisplayState.h b/libs/ui/include/ui/DisplayState.h index 70a0d50611..98ee35652a 100644 --- a/libs/ui/include/ui/DisplayState.h +++ b/libs/ui/include/ui/DisplayState.h @@ -16,21 +16,18 @@ #pragma once +#include <ui/LayerStack.h> #include <ui/Rotation.h> #include <ui/Size.h> -#include <cstdint> #include <type_traits> namespace android::ui { -using LayerStack = uint32_t; -constexpr LayerStack NO_LAYER_STACK = static_cast<LayerStack>(-1); - // Transactional state of physical or virtual display. Note that libgui defines // android::DisplayState as a superset of android::ui::DisplayState. struct DisplayState { - LayerStack layerStack = NO_LAYER_STACK; + LayerStack layerStack; Rotation orientation = ROTATION_0; Size layerStackSpaceRect; }; diff --git a/libs/ui/include/ui/LayerStack.h b/libs/ui/include/ui/LayerStack.h new file mode 100644 index 0000000000..d6ffeb7fad --- /dev/null +++ b/libs/ui/include/ui/LayerStack.h @@ -0,0 +1,78 @@ +/* + * Copyright 2021 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 <cstdint> + +#include <ftl/cast.h> +#include <ftl/string.h> +#include <log/log.h> + +namespace android::ui { + +// A LayerStack identifies a Z-ordered group of layers. A layer can only be associated to a single +// LayerStack, but a LayerStack can be associated to multiple displays, mirroring the same content. +struct LayerStack { + uint32_t id = UINT32_MAX; + + template <typename T> + static constexpr LayerStack fromValue(T v) { + if (ftl::cast_safety<uint32_t>(v) == ftl::CastSafety::kSafe) { + return {static_cast<uint32_t>(v)}; + } + + ALOGW("Invalid layer stack %s", ftl::to_string(v).c_str()); + return {}; + } +}; + +constexpr LayerStack INVALID_LAYER_STACK; +constexpr LayerStack DEFAULT_LAYER_STACK{0u}; + +inline bool operator==(LayerStack lhs, LayerStack rhs) { + return lhs.id == rhs.id; +} + +inline bool operator!=(LayerStack lhs, LayerStack rhs) { + return !(lhs == rhs); +} + +inline bool operator>(LayerStack lhs, LayerStack rhs) { + return lhs.id > rhs.id; +} + +// A LayerFilter determines if a layer is included for output to a display. +struct LayerFilter { + LayerStack layerStack; + + // True if the layer is only output to internal displays, i.e. excluded from screenshots, screen + // recordings, and mirroring to virtual or external displays. Used for display cutout overlays. + bool toInternalDisplay = false; + + // Returns true if the input filter can be output to this filter. + bool includes(LayerFilter other) const { + // The layer stacks must match. + if (other.layerStack == INVALID_LAYER_STACK || other.layerStack != layerStack) { + return false; + } + + // The output must be to an internal display if the input filter has that constraint. + return !other.toInternalDisplay || toInternalDisplay; + } +}; + +} // namespace android::ui diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h index f1e825286e..ecc192dcae 100644 --- a/libs/ui/include/ui/Size.h +++ b/libs/ui/include/ui/Size.h @@ -23,103 +23,70 @@ #include <type_traits> #include <utility> -namespace android { -namespace ui { +namespace android::ui { -// Forward declare a few things. -struct Size; -bool operator==(const Size& lhs, const Size& rhs); - -/** - * A simple value type representing a two-dimensional size - */ +// A simple value type representing a two-dimensional size. struct Size { - int32_t width; - int32_t height; + int32_t width = -1; + int32_t height = -1; - // Special values - static const Size INVALID; - static const Size EMPTY; + constexpr Size() = default; - // ------------------------------------------------------------------------ - // Construction - // ------------------------------------------------------------------------ - - Size() : Size(INVALID) {} template <typename T> - Size(T&& w, T&& h) - : width(Size::clamp<int32_t, T>(std::forward<T>(w))), - height(Size::clamp<int32_t, T>(std::forward<T>(h))) {} - - // ------------------------------------------------------------------------ - // Accessors - // ------------------------------------------------------------------------ + constexpr Size(T w, T h) : width(clamp<int32_t>(w)), height(clamp<int32_t>(h)) {} int32_t getWidth() const { return width; } int32_t getHeight() const { return height; } + // Valid means non-negative width and height + bool isValid() const { return width >= 0 && height >= 0; } + + // Empty means zero width and height + bool isEmpty() const; + template <typename T> - void setWidth(T&& v) { - width = Size::clamp<int32_t, T>(std::forward<T>(v)); + void setWidth(T v) { + width = clamp<int32_t>(v); } + template <typename T> - void setHeight(T&& v) { - height = Size::clamp<int32_t, T>(std::forward<T>(v)); + void setHeight(T v) { + height = clamp<int32_t>(v); } - // ------------------------------------------------------------------------ - // Assignment - // ------------------------------------------------------------------------ + void set(Size size) { *this = size; } - void set(const Size& size) { *this = size; } template <typename T> - void set(T&& w, T&& h) { - set(Size(std::forward<T>(w), std::forward<T>(h))); + void set(T w, T h) { + set(Size(w, h)); } - // Sets the value to INVALID - void makeInvalid() { set(INVALID); } + // Sets the value to kInvalidSize + void makeInvalid(); - // Sets the value to EMPTY - void clear() { set(EMPTY); } + // Sets the value to kEmptySize + void clear(); - // ------------------------------------------------------------------------ - // Semantic checks - // ------------------------------------------------------------------------ - - // Valid means non-negative width and height - bool isValid() const { return width >= 0 && height >= 0; } - - // Empty means zero width and height - bool isEmpty() const { return *this == EMPTY; } - - // ------------------------------------------------------------------------ - // Clamp Helpers - // ------------------------------------------------------------------------ - - // Note: We use only features available in C++11 here for compatibility with - // external targets which include this file directly or indirectly and which - // themselves use C++11. - - // C++11 compatible replacement for std::remove_cv_reference_t [C++20] + // TODO: Replace with std::remove_cvref_t in C++20. template <typename T> - using remove_cv_reference_t = - typename std::remove_cv<typename std::remove_reference<T>::type>::type; + using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>; // Takes a value of type FromType, and ensures it can be represented as a value of type ToType, // clamping the input value to the output range if necessary. template <typename ToType, typename FromType> - static Size::remove_cv_reference_t<ToType> - clamp(typename std::enable_if< - std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_specialized && - std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_specialized, - FromType>::type v) { - using BareToType = remove_cv_reference_t<ToType>; - using BareFromType = remove_cv_reference_t<FromType>; - static constexpr auto toHighest = std::numeric_limits<BareToType>::max(); - static constexpr auto toLowest = std::numeric_limits<BareToType>::lowest(); - static constexpr auto fromHighest = std::numeric_limits<BareFromType>::max(); - static constexpr auto fromLowest = std::numeric_limits<BareFromType>::lowest(); + static constexpr remove_cvref_t<ToType> clamp(FromType v) { + using BareToType = remove_cvref_t<ToType>; + using ToLimits = std::numeric_limits<BareToType>; + + using BareFromType = remove_cvref_t<FromType>; + using FromLimits = std::numeric_limits<BareFromType>; + + static_assert(ToLimits::is_specialized && FromLimits::is_specialized); + + constexpr auto toHighest = ToLimits::max(); + constexpr auto toLowest = ToLimits::lowest(); + constexpr auto fromHighest = FromLimits::max(); + constexpr auto fromLowest = FromLimits::lowest(); // Get the closest representation of [toLowest, toHighest] in type // FromType to use to clamp the input value before conversion. @@ -127,37 +94,35 @@ struct Size { // std::common_type<...> is used to get a value-preserving type for the // top end of the range. using CommonHighestType = std::common_type_t<BareToType, BareFromType>; + using CommonLimits = std::numeric_limits<CommonHighestType>; // std::make_signed<std::common_type<...>> is used to get a // value-preserving type for the bottom end of the range, except this is // a bit trickier for non-integer types like float. - using CommonLowestType = - std::conditional_t<std::numeric_limits<CommonHighestType>::is_integer, - std::make_signed_t<std::conditional_t< - std::numeric_limits<CommonHighestType>::is_integer, - CommonHighestType, int /* not used */>>, - CommonHighestType>; + using CommonLowestType = std::conditional_t< + CommonLimits::is_integer, + std::make_signed_t<std::conditional_t<CommonLimits::is_integer, CommonHighestType, + int /* not used */>>, + CommonHighestType>; // We can then compute the clamp range in a way that can be later // trivially converted to either the 'from' or 'to' types, and be - // representabile in either. - static constexpr auto commonClampHighest = - std::min(static_cast<CommonHighestType>(fromHighest), - static_cast<CommonHighestType>(toHighest)); - static constexpr auto commonClampLowest = - std::max(static_cast<CommonLowestType>(fromLowest), - static_cast<CommonLowestType>(toLowest)); + // representable in either. + constexpr auto commonClampHighest = std::min(static_cast<CommonHighestType>(fromHighest), + static_cast<CommonHighestType>(toHighest)); + constexpr auto commonClampLowest = std::max(static_cast<CommonLowestType>(fromLowest), + static_cast<CommonLowestType>(toLowest)); - static constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest); - static constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest); + constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest); + constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest); // A clamp is needed only if the range we are clamping to is not the // same as the range of the input. - static constexpr bool isClampNeeded = + constexpr bool isClampNeeded = (fromLowest != fromClampLowest) || (fromHighest != fromClampHighest); // If a clamp is not needed, the conversion is just a trivial cast. - if (!isClampNeeded) { + if constexpr (!isClampNeeded) { return static_cast<BareToType>(v); } @@ -170,34 +135,46 @@ struct Size { // Otherwise clamping is done by using the already computed endpoints // for each type. - return (v <= fromClampLowest) - ? toClampLowest - : ((v >= fromClampHighest) ? toClampHighest : static_cast<BareToType>(v)); + if (v <= fromClampLowest) { + return toClampLowest; + } + + return v >= fromClampHighest ? toClampHighest : static_cast<BareToType>(v); } }; -// ------------------------------------------------------------------------ -// Comparisons -// ------------------------------------------------------------------------ +constexpr Size kInvalidSize; +constexpr Size kEmptySize{0, 0}; + +inline void Size::makeInvalid() { + set(kInvalidSize); +} -inline bool operator==(const Size& lhs, const Size& rhs) { +inline void Size::clear() { + set(kEmptySize); +} + +inline bool operator==(Size lhs, Size rhs) { return lhs.width == rhs.width && lhs.height == rhs.height; } -inline bool operator!=(const Size& lhs, const Size& rhs) { - return !operator==(lhs, rhs); +inline bool Size::isEmpty() const { + return *this == kEmptySize; +} + +inline bool operator!=(Size lhs, Size rhs) { + return !(lhs == rhs); } -inline bool operator<(const Size& lhs, const Size& rhs) { +inline bool operator<(Size lhs, Size rhs) { // Orders by increasing width, then height. if (lhs.width != rhs.width) return lhs.width < rhs.width; return lhs.height < rhs.height; } // Defining PrintTo helps with Google Tests. -static inline void PrintTo(const Size& size, ::std::ostream* os) { - *os << "Size(" << size.width << ", " << size.height << ")"; +inline void PrintTo(Size size, std::ostream* stream) { + *stream << "Size(" << size.width << ", " << size.height << ')'; } -} // namespace ui -} // namespace android +} // namespace android::ui diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp index 1d908b8ef1..8ddee7e740 100644 --- a/libs/ui/tests/DisplayId_test.cpp +++ b/libs/ui/tests/DisplayId_test.cpp @@ -32,6 +32,9 @@ TEST(DisplayIdTest, createPhysicalIdFromEdid) { EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); EXPECT_TRUE(PhysicalDisplayId::tryCast(id)); EXPECT_TRUE(HalDisplayId::tryCast(id)); + + EXPECT_EQ(id, DisplayId::fromValue(id.value)); + EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value)); } TEST(DisplayIdTest, createPhysicalIdFromPort) { @@ -43,6 +46,9 @@ TEST(DisplayIdTest, createPhysicalIdFromPort) { EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); EXPECT_TRUE(PhysicalDisplayId::tryCast(id)); EXPECT_TRUE(HalDisplayId::tryCast(id)); + + EXPECT_EQ(id, DisplayId::fromValue(id.value)); + EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value)); } TEST(DisplayIdTest, createGpuVirtualId) { @@ -52,6 +58,9 @@ TEST(DisplayIdTest, createGpuVirtualId) { EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); EXPECT_FALSE(HalDisplayId::tryCast(id)); + + EXPECT_EQ(id, DisplayId::fromValue(id.value)); + EXPECT_EQ(id, DisplayId::fromValue<GpuVirtualDisplayId>(id.value)); } TEST(DisplayIdTest, createHalVirtualId) { @@ -61,6 +70,9 @@ TEST(DisplayIdTest, createHalVirtualId) { EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); EXPECT_TRUE(HalDisplayId::tryCast(id)); + + EXPECT_EQ(id, DisplayId::fromValue(id.value)); + EXPECT_EQ(id, DisplayId::fromValue<HalVirtualDisplayId>(id.value)); } } // namespace android::ui diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp index 5f75aeabeb..acef47fb97 100644 --- a/libs/ui/tests/Size_test.cpp +++ b/libs/ui/tests/Size_test.cpp @@ -93,9 +93,8 @@ TEST(SizeTest, ValidAndEmpty) { } { - const auto& s = Size::INVALID; - EXPECT_FALSE(s.isValid()); - EXPECT_FALSE(s.isEmpty()); + EXPECT_FALSE(kInvalidSize.isValid()); + EXPECT_FALSE(kInvalidSize.isEmpty()); } { @@ -112,9 +111,8 @@ TEST(SizeTest, ValidAndEmpty) { } { - const auto& s = Size::EMPTY; - EXPECT_TRUE(s.isValid()); - EXPECT_TRUE(s.isEmpty()); + EXPECT_TRUE(kEmptySize.isValid()); + EXPECT_TRUE(kEmptySize.isEmpty()); } { diff --git a/services/automotive/display/AutomotiveDisplayProxyService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp index d6fc69562b..d205231033 100644 --- a/services/automotive/display/AutomotiveDisplayProxyService.cpp +++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp @@ -34,7 +34,10 @@ AutomotiveDisplayProxyService::getIGraphicBufferProducer(uint64_t id) { sp<IBinder> displayToken = nullptr; sp<SurfaceControl> surfaceControl = nullptr; if (it == mDisplays.end()) { - displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id)); + if (const auto displayId = DisplayId::fromValue<PhysicalDisplayId>(id)) { + displayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId); + } + if (displayToken == nullptr) { ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id); return nullptr; @@ -157,7 +160,11 @@ Return<void> AutomotiveDisplayProxyService::getDisplayInfo(uint64_t id, getDispl HwDisplayConfig activeConfig; HwDisplayState activeState; - auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id)); + sp<IBinder> displayToken; + if (const auto displayId = DisplayId::fromValue<PhysicalDisplayId>(id)) { + displayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId); + } + if (displayToken == nullptr) { ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id); } else { diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp index 05ef489133..a864cf8202 100644 --- a/services/inputflinger/InputReaderBase.cpp +++ b/services/inputflinger/InputReaderBase.cpp @@ -19,12 +19,12 @@ //#define LOG_NDEBUG 0 #include "InputReaderBase.h" -#include <ftl/NamedEnum.h> #include "input/DisplayViewport.h" #include "input/Input.h" -#include <android/log.h> #include <android-base/stringprintf.h> +#include <android/log.h> +#include <ftl/enum.h> #define INDENT " " #define INDENT2 " " @@ -117,7 +117,7 @@ std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByTyp } if (count > 1) { ALOGW("Found %zu viewports with type %s, but expected 1 at most", count, - NamedEnum::string(type).c_str()); + ftl::enum_string(type).c_str()); } return result; } diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 82b4fe476e..571c126658 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -176,10 +176,11 @@ std::string KeyEntry::getDescription() const { } return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64 ", source=0x%08x, displayId=%" PRId32 ", action=%s, " - "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, " + "flags=0x%08x, keyCode=%s(%d), scanCode=%d, metaState=0x%08x, " "repeatCount=%d), policyFlags=0x%08x", deviceId, eventTime, source, displayId, KeyEvent::actionToString(action), - flags, keyCode, scanCode, metaState, repeatCount, policyFlags); + flags, KeyEvent::getLabel(keyCode), keyCode, scanCode, metaState, + repeatCount, policyFlags); } void KeyEntry::recycle() { @@ -191,6 +192,18 @@ void KeyEntry::recycle() { interceptKeyWakeupTime = 0; } +// --- TouchModeEntry --- + +TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode) + : EventEntry(id, Type::TOUCH_MODE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER), + inTouchMode(inTouchMode) {} + +TouchModeEntry::~TouchModeEntry() {} + +std::string TouchModeEntry::getDescription() const { + return StringPrintf("TouchModeEvent(inTouchMode=%s)", inTouchMode ? "true" : "false"); +} + // --- MotionEntry --- MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, @@ -318,16 +331,4 @@ uint32_t DispatchEntry::nextSeq() { return seq; } -// --- CommandEntry --- - -CommandEntry::CommandEntry(Command command) - : command(command), - eventTime(0), - keyEntry(nullptr), - userActivityEventType(0), - seq(0), - handled(false) {} - -CommandEntry::~CommandEntry() {} - } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index ffe3bb691c..5365a78b0a 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -39,6 +39,9 @@ struct EventEntry { SENSOR, POINTER_CAPTURE_CHANGED, DRAG, + TOUCH_MODE_CHANGED, + + ftl_last = TOUCH_MODE_CHANGED }; int32_t id; @@ -185,7 +188,7 @@ struct MotionEntry : EventEntry { float xOffset, float yOffset); std::string getDescription() const override; - virtual ~MotionEntry(); + ~MotionEntry() override; }; struct SensorEntry : EventEntry { @@ -207,6 +210,15 @@ struct SensorEntry : EventEntry { ~SensorEntry() override; }; +struct TouchModeEntry : EventEntry { + bool inTouchMode; + + TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode); + std::string getDescription() const override; + + ~TouchModeEntry() override; +}; + // Tracks the progress of dispatching a particular event to a particular connection. struct DispatchEntry { const uint32_t seq; // unique sequence number, never 0 @@ -245,55 +257,6 @@ private: VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry); VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry); -class InputDispatcher; -// A command entry captures state and behavior for an action to be performed in the -// dispatch loop after the initial processing has taken place. It is essentially -// a kind of continuation used to postpone sensitive policy interactions to a point -// in the dispatch loop where it is safe to release the lock (generally after finishing -// the critical parts of the dispatch cycle). -// -// The special thing about commands is that they can voluntarily release and reacquire -// the dispatcher lock at will. Initially when the command starts running, the -// dispatcher lock is held. However, if the command needs to call into the policy to -// do some work, it can release the lock, do the work, then reacquire the lock again -// before returning. -// -// This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch -// never calls into the policy while holding its lock. -// -// Commands are implicitly 'LockedInterruptible'. -struct CommandEntry; -typedef std::function<void(InputDispatcher&, CommandEntry*)> Command; - -class Connection; -struct CommandEntry { - explicit CommandEntry(Command command); - ~CommandEntry(); - - Command command; - - // parameters for the command (usage varies by command) - sp<Connection> connection; - nsecs_t eventTime; - std::shared_ptr<KeyEntry> keyEntry; - std::shared_ptr<SensorEntry> sensorEntry; - std::shared_ptr<InputApplicationHandle> inputApplicationHandle; - std::string reason; - int32_t userActivityEventType; - uint32_t seq; - bool handled; - sp<IBinder> connectionToken; - sp<IBinder> oldToken; - sp<IBinder> newToken; - std::string obscuringPackage; - PointerCaptureRequest pointerCaptureRequest; - int32_t pid; - nsecs_t consumeTime; // time when the event was consumed by InputConsumer - int32_t displayId; - float x; - float y; -}; - } // namespace android::inputdispatcher #endif // _UI_INPUT_INPUTDISPATCHER_ENTRY_H diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp index 4a75773201..600f02ba80 100644 --- a/services/inputflinger/dispatcher/FocusResolver.cpp +++ b/services/inputflinger/dispatcher/FocusResolver.cpp @@ -27,7 +27,7 @@ static constexpr bool DEBUG_FOCUS = false; #include <android-base/stringprintf.h> #include <binder/Binder.h> -#include <ftl/NamedEnum.h> +#include <ftl/enum.h> #include <gui/WindowInfo.h> #include <log/log.h> @@ -65,7 +65,7 @@ std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows( if (result == Focusability::OK) { return std::nullopt; } - removeFocusReason = NamedEnum::string(result); + removeFocusReason = ftl::enum_string(result); } // We don't have a focused window or the currently focused window is no longer focusable. Check @@ -79,7 +79,7 @@ std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows( if (result == Focusability::OK) { return updateFocusedWindow(displayId, "Window became focusable. Previous reason: " + - NamedEnum::string(previousResult), + ftl::enum_string(previousResult), requestedFocus, request->windowName); } } @@ -116,7 +116,7 @@ std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow( request.token, request.windowName); } ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason: %s", - request.windowName.c_str(), displayId, NamedEnum::string(result).c_str()); + request.windowName.c_str(), displayId, ftl::enum_string(result).c_str()); return std::nullopt; } @@ -134,7 +134,7 @@ std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow( // The requested window is not currently focusable. Wait for the window to become focusable // but remove focus from the current window so that input events can go into a pending queue // and be sent to the window when it becomes focused. - return updateFocusedWindow(displayId, "Waiting for window because " + NamedEnum::string(result), + return updateFocusedWindow(displayId, "Waiting for window because " + ftl::enum_string(result), nullptr); } @@ -212,7 +212,7 @@ std::string FocusResolver::dump() const { for (const auto& [displayId, request] : mFocusRequestByDisplay) { auto it = mLastFocusResultByDisplay.find(displayId); std::string result = - it != mLastFocusResultByDisplay.end() ? NamedEnum::string(it->second) : ""; + it != mLastFocusResultByDisplay.end() ? ftl::enum_string(it->second) : ""; dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s' result='%s'\n", displayId, request.windowName.c_str(), result.c_str()); } diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h index 1d6cd9a5fa..6d11a77aad 100644 --- a/services/inputflinger/dispatcher/FocusResolver.h +++ b/services/inputflinger/dispatcher/FocusResolver.h @@ -77,6 +77,8 @@ private: NO_WINDOW, NOT_FOCUSABLE, NOT_VISIBLE, + + ftl_last = NOT_VISIBLE }; // Checks if the window token can be focused on a display. The token can be focused if there is @@ -113,4 +115,4 @@ private: std::optional<android::gui::FocusRequest> getFocusRequest(int32_t displayId); }; -} // namespace android::inputdispatcher
\ No newline at end of file +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 4a7860a987..da3e237e3e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -19,34 +19,6 @@ #define LOG_NDEBUG 1 -// Log detailed debug messages about each inbound event notification to the dispatcher. -#define DEBUG_INBOUND_EVENT_DETAILS 0 - -// Log detailed debug messages about each outbound event processed by the dispatcher. -#define DEBUG_OUTBOUND_EVENT_DETAILS 0 - -// Log debug messages about the dispatch cycle. -#define DEBUG_DISPATCH_CYCLE 0 - -// Log debug messages about channel creation -#define DEBUG_CHANNEL_CREATION 0 - -// Log debug messages about input event injection. -#define DEBUG_INJECTION 0 - -// Log debug messages about input focus tracking. -static constexpr bool DEBUG_FOCUS = false; - -// Log debug messages about touch occlusion -// STOPSHIP(b/169067926): Set to false -static constexpr bool DEBUG_TOUCH_OCCLUSION = true; - -// Log debug messages about the app switch latency optimization. -#define DEBUG_APP_SWITCH 0 - -// Log debug messages about hover events. -#define DEBUG_HOVER 0 - #include <InputFlingerProperties.sysprop.h> #include <android-base/chrono_utils.h> #include <android-base/properties.h> @@ -55,6 +27,7 @@ static constexpr bool DEBUG_TOUCH_OCCLUSION = true; #include <binder/Binder.h> #include <binder/IServiceManager.h> #include <com/android/internal/compat/IPlatformCompatNative.h> +#include <ftl/enum.h> #include <gui/SurfaceComposerClient.h> #include <input/InputDevice.h> #include <log/log.h> @@ -82,6 +55,7 @@ static constexpr bool DEBUG_TOUCH_OCCLUSION = true; using android::base::HwTimeoutMultiplier; using android::base::Result; using android::base::StringPrintf; +using android::gui::DisplayInfo; using android::gui::FocusRequest; using android::gui::TouchOcclusionMode; using android::gui::WindowInfo; @@ -94,9 +68,50 @@ using com::android::internal::compat::IPlatformCompatNative; namespace android::inputdispatcher { +namespace { + +// Log detailed debug messages about each inbound event notification to the dispatcher. +constexpr bool DEBUG_INBOUND_EVENT_DETAILS = false; + +// Log detailed debug messages about each outbound event processed by the dispatcher. +constexpr bool DEBUG_OUTBOUND_EVENT_DETAILS = false; + +// Log debug messages about the dispatch cycle. +constexpr bool DEBUG_DISPATCH_CYCLE = false; + +// Log debug messages about channel creation +constexpr bool DEBUG_CHANNEL_CREATION = false; + +// Log debug messages about input event injection. +constexpr bool DEBUG_INJECTION = false; + +// Log debug messages about input focus tracking. +constexpr bool DEBUG_FOCUS = false; + +// Log debug messages about touch occlusion +// STOPSHIP(b/169067926): Set to false +constexpr bool DEBUG_TOUCH_OCCLUSION = true; + +// Log debug messages about the app switch latency optimization. +constexpr bool DEBUG_APP_SWITCH = false; + +// Log debug messages about hover events. +constexpr bool DEBUG_HOVER = false; + +// Temporarily releases a held mutex for the lifetime of the instance. +// Named to match std::scoped_lock +class scoped_unlock { +public: + explicit scoped_unlock(std::mutex& mutex) : mMutex(mutex) { mMutex.unlock(); } + ~scoped_unlock() { mMutex.lock(); } + +private: + std::mutex& mMutex; +}; + // When per-window-input-rotation is enabled, InputFlinger works in the un-rotated display // coordinates and SurfaceFlinger includes the display rotation in the input window transforms. -static bool isPerWindowInputRotationEnabled() { +bool isPerWindowInputRotationEnabled() { static const bool PER_WINDOW_INPUT_ROTATION = sysprop::InputFlingerProperties::per_window_input_rotation().value_or(false); @@ -136,27 +151,27 @@ constexpr size_t RECENT_QUEUE_MAX_SIZE = 10; constexpr int LOGTAG_INPUT_INTERACTION = 62000; constexpr int LOGTAG_INPUT_FOCUS = 62001; -static inline nsecs_t now() { +inline nsecs_t now() { return systemTime(SYSTEM_TIME_MONOTONIC); } -static inline const char* toString(bool value) { +inline const char* toString(bool value) { return value ? "true" : "false"; } -static inline const std::string toString(sp<IBinder> binder) { +inline const std::string toString(const sp<IBinder>& binder) { if (binder == nullptr) { return "<null>"; } return StringPrintf("%p", binder.get()); } -static inline int32_t getMotionEventActionPointerIndex(int32_t action) { +inline int32_t getMotionEventActionPointerIndex(int32_t action) { return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; } -static bool isValidKeyAction(int32_t action) { +bool isValidKeyAction(int32_t action) { switch (action) { case AKEY_EVENT_ACTION_DOWN: case AKEY_EVENT_ACTION_UP: @@ -166,7 +181,7 @@ static bool isValidKeyAction(int32_t action) { } } -static bool validateKeyEvent(int32_t action) { +bool validateKeyEvent(int32_t action) { if (!isValidKeyAction(action)) { ALOGE("Key event has invalid action code 0x%x", action); return false; @@ -174,7 +189,7 @@ static bool validateKeyEvent(int32_t action) { return true; } -static bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) { +bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) { switch (action & AMOTION_EVENT_ACTION_MASK) { case AMOTION_EVENT_ACTION_DOWN: case AMOTION_EVENT_ACTION_UP: @@ -199,12 +214,12 @@ static bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t po } } -static int64_t millis(std::chrono::nanoseconds t) { +int64_t millis(std::chrono::nanoseconds t) { return std::chrono::duration_cast<std::chrono::milliseconds>(t).count(); } -static bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount, - const PointerProperties* pointerProperties) { +bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount, + const PointerProperties* pointerProperties) { if (!isValidMotionAction(action, actionButton, pointerCount)) { ALOGE("Motion event has invalid action code 0x%x", action); return false; @@ -231,7 +246,7 @@ static bool validateMotionEvent(int32_t action, int32_t actionButton, size_t poi return true; } -static std::string dumpRegion(const Region& region) { +std::string dumpRegion(const Region& region) { if (region.isEmpty()) { return "<empty>"; } @@ -252,7 +267,7 @@ static std::string dumpRegion(const Region& region) { return dump; } -static std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) { +std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) { constexpr size_t maxEntries = 50; // max events to print constexpr size_t skipBegin = maxEntries / 2; const size_t skipEnd = queue.size() - maxEntries / 2; @@ -291,12 +306,12 @@ static std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t cu * Also useful when the entries are sp<>. If an entry is not found, nullptr is returned. */ template <typename K, typename V> -static V getValueByKey(const std::unordered_map<K, V>& map, K key) { +V getValueByKey(const std::unordered_map<K, V>& map, K key) { auto it = map.find(key); return it != map.end() ? it->second : V{}; } -static bool haveSameToken(const sp<WindowInfoHandle>& first, const sp<WindowInfoHandle>& second) { +bool haveSameToken(const sp<WindowInfoHandle>& first, const sp<WindowInfoHandle>& second) { if (first == second) { return true; } @@ -308,7 +323,7 @@ static bool haveSameToken(const sp<WindowInfoHandle>& first, const sp<WindowInfo return first->getToken() == second->getToken(); } -static bool haveSameApplicationToken(const WindowInfo* first, const WindowInfo* second) { +bool haveSameApplicationToken(const WindowInfo* first, const WindowInfo* second) { if (first == nullptr || second == nullptr) { return false; } @@ -316,13 +331,13 @@ static bool haveSameApplicationToken(const WindowInfo* first, const WindowInfo* first->applicationInfo.token == second->applicationInfo.token; } -static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { +bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT; } -static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget, - std::shared_ptr<EventEntry> eventEntry, - int32_t inputTargetFlags) { +std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget, + std::shared_ptr<EventEntry> eventEntry, + int32_t inputTargetFlags) { if (eventEntry->type == EventEntry::Type::MOTION) { const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry); if ((motionEntry.source & AINPUT_SOURCE_CLASS_JOYSTICK) || @@ -399,9 +414,9 @@ static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inp return dispatchEntry; } -static void addGestureMonitors(const std::vector<Monitor>& monitors, - std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0, - float yOffset = 0) { +void addGestureMonitors(const std::vector<Monitor>& monitors, + std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0, + float yOffset = 0) { if (monitors.empty()) { return; } @@ -411,9 +426,8 @@ static void addGestureMonitors(const std::vector<Monitor>& monitors, } } -static status_t openInputChannelPair(const std::string& name, - std::shared_ptr<InputChannel>& serverChannel, - std::unique_ptr<InputChannel>& clientChannel) { +status_t openInputChannelPair(const std::string& name, std::shared_ptr<InputChannel>& serverChannel, + std::unique_ptr<InputChannel>& clientChannel) { std::unique_ptr<InputChannel> uniqueServerChannel; status_t result = InputChannel::openInputChannelPair(name, uniqueServerChannel, clientChannel); @@ -422,7 +436,7 @@ static status_t openInputChannelPair(const std::string& name, } template <typename T> -static bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) { +bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) { if (lhs == nullptr && rhs == nullptr) { return true; } @@ -432,7 +446,7 @@ static bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared return *lhs == *rhs; } -static sp<IPlatformCompatNative> getCompatService() { +sp<IPlatformCompatNative> getCompatService() { sp<IBinder> service(defaultServiceManager()->getService(String16("platform_compat_native"))); if (service == nullptr) { ALOGE("Failed to link to compat service"); @@ -441,7 +455,7 @@ static sp<IPlatformCompatNative> getCompatService() { return interface_cast<IPlatformCompatNative>(service); } -static KeyEvent createKeyEvent(const KeyEntry& entry) { +KeyEvent createKeyEvent(const KeyEntry& entry) { KeyEvent event; event.initialize(entry.id, entry.deviceId, entry.source, entry.displayId, INVALID_HMAC, entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState, @@ -449,7 +463,7 @@ static KeyEvent createKeyEvent(const KeyEntry& entry) { return event; } -static std::optional<int32_t> findMonitorPidByToken( +std::optional<int32_t> findMonitorPidByToken( const std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay, const sp<IBinder>& token) { for (const auto& it : monitorsByDisplay) { @@ -463,7 +477,7 @@ static std::optional<int32_t> findMonitorPidByToken( return std::nullopt; } -static bool shouldReportMetricsForConnection(const Connection& connection) { +bool shouldReportMetricsForConnection(const Connection& connection) { // Do not keep track of gesture monitors. They receive every event and would disproportionately // affect the statistics. if (connection.monitor) { @@ -476,8 +490,7 @@ static bool shouldReportMetricsForConnection(const Connection& connection) { return true; } -static bool shouldReportFinishedEvent(const DispatchEntry& dispatchEntry, - const Connection& connection) { +bool shouldReportFinishedEvent(const DispatchEntry& dispatchEntry, const Connection& connection) { const EventEntry& eventEntry = *dispatchEntry.eventEntry; const int32_t& inputEventId = eventEntry.id; if (inputEventId != dispatchEntry.resolvedEventId) { @@ -513,6 +526,22 @@ static bool shouldReportFinishedEvent(const DispatchEntry& dispatchEntry, return true; } +/** + * Connection is responsive if it has no events in the waitQueue that are older than the + * current time. + */ +bool isConnectionResponsive(const Connection& connection) { + const nsecs_t currentTime = now(); + for (const DispatchEntry* entry : connection.waitQueue) { + if (entry->timeoutTime < currentTime) { + return false; + } + } + return true; +} + +} // namespace + // --- InputDispatcher --- InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) @@ -545,17 +574,17 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic } InputDispatcher::~InputDispatcher() { - { // acquire lock - std::scoped_lock _l(mLock); + std::scoped_lock _l(mLock); - resetKeyRepeatLocked(); - releasePendingEventLocked(); - drainInboundQueueLocked(); - } + resetKeyRepeatLocked(); + releasePendingEventLocked(); + drainInboundQueueLocked(); + mCommandQueue.clear(); while (!mConnectionsByToken.empty()) { sp<Connection> connection = mConnectionsByToken.begin()->second; - removeInputChannel(connection->inputChannel->getConnectionToken()); + removeInputChannelLocked(connection->inputChannel->getConnectionToken(), + false /* notify */); } } @@ -595,7 +624,7 @@ void InputDispatcher::dispatchOnce() { // Run all pending commands if there are any. // If any commands were run then force the next poll to wake up immediately. - if (runCommandsLockedInterruptible()) { + if (runCommandsLockedInterruptable()) { nextWakeupTime = LONG_LONG_MIN; } @@ -798,6 +827,14 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { break; } + case EventEntry::Type::TOUCH_MODE_CHANGED: { + const auto typedEntry = std::static_pointer_cast<TouchModeEntry>(mPendingEvent); + dispatchTouchModeChangeLocked(currentTime, typedEntry); + done = true; + dropReason = DropReason::NOT_DROPPED; // touch mode events are never dropped + break; + } + case EventEntry::Type::POINTER_CAPTURE_CHANGED: { const auto typedEntry = std::static_pointer_cast<PointerCaptureChangedEntry>(mPendingEvent); @@ -910,8 +947,7 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt } // Alternatively, maybe there's a gesture monitor that could handle this event - std::vector<TouchedMonitor> gestureMonitors = - findTouchedGestureMonitorsLocked(displayId, {}); + std::vector<TouchedMonitor> gestureMonitors = findTouchedGestureMonitorsLocked(displayId); for (TouchedMonitor& gestureMonitor : gestureMonitors) { sp<Connection> connection = getConnectionLocked(gestureMonitor.monitor.inputChannel->getConnectionToken()); @@ -956,9 +992,9 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE mAppSwitchSawKeyDown = true; } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) { if (mAppSwitchSawKeyDown) { -#if DEBUG_APP_SWITCH - ALOGD("App switch is pending!"); -#endif + if (DEBUG_APP_SWITCH) { + ALOGD("App switch is pending!"); + } mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT; mAppSwitchSawKeyDown = false; needWake = true; @@ -979,6 +1015,7 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE LOG_ALWAYS_FATAL("Focus events should be inserted using enqueueFocusEventLocked"); break; } + case EventEntry::Type::TOUCH_MODE_CHANGED: case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::SENSOR: @@ -1005,11 +1042,9 @@ void InputDispatcher::addRecentEventLocked(std::shared_ptr<EventEntry> entry) { sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool addOutsideTargets, - bool addPortalWindows, bool ignoreDragWindow) { - if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) { - LOG_ALWAYS_FATAL( - "Must provide a valid touch state if adding portal windows or outside targets"); + if (addOutsideTargets && touchState == nullptr) { + LOG_ALWAYS_FATAL("Must provide a valid touch state if adding outside targets"); } // Traverse windows from front to back to find touched window. const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId); @@ -1026,16 +1061,6 @@ sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayI bool isTouchModal = !flags.test(WindowInfo::Flag::NOT_FOCUSABLE) && !flags.test(WindowInfo::Flag::NOT_TOUCH_MODAL); if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) { - int32_t portalToDisplayId = windowInfo->portalToDisplayId; - if (portalToDisplayId != ADISPLAY_ID_NONE && - portalToDisplayId != displayId) { - if (addPortalWindows) { - // For the monitoring channels of the display. - touchState->addPortalWindow(windowHandle); - } - return findTouchedWindowAtLocked(portalToDisplayId, x, y, touchState, - addOutsideTargets, addPortalWindows); - } // Found window. return windowHandle; } @@ -1053,17 +1078,11 @@ sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayI } std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked( - int32_t displayId, const std::vector<sp<WindowInfoHandle>>& portalWindows) const { + int32_t displayId) const { std::vector<TouchedMonitor> touchedMonitors; std::vector<Monitor> monitors = getValueByKey(mGestureMonitorsByDisplay, displayId); addGestureMonitors(monitors, touchedMonitors); - for (const sp<WindowInfoHandle>& portalWindow : portalWindows) { - const WindowInfo* windowInfo = portalWindow->getInfo(); - monitors = getValueByKey(mGestureMonitorsByDisplay, windowInfo->portalToDisplayId); - addGestureMonitors(monitors, touchedMonitors, -windowInfo->frameLeft, - -windowInfo->frameTop); - } return touchedMonitors; } @@ -1071,9 +1090,9 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason const char* reason; switch (dropReason) { case DropReason::POLICY: -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("Dropped event because policy consumed it."); -#endif + if (DEBUG_INBOUND_EVENT_DETAILS) { + ALOGD("Dropped event because policy consumed it."); + } reason = "inbound event was dropped because the policy consumed it"; break; case DropReason::DISABLED: @@ -1131,9 +1150,10 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason break; } case EventEntry::Type::FOCUS: + case EventEntry::Type::TOUCH_MODE_CHANGED: case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { - LOG_ALWAYS_FATAL("Should not drop %s events", NamedEnum::string(entry.type).c_str()); + LOG_ALWAYS_FATAL("Should not drop %s events", ftl::enum_string(entry.type).c_str()); break; } } @@ -1157,37 +1177,35 @@ bool InputDispatcher::isAppSwitchPendingLocked() { void InputDispatcher::resetPendingAppSwitchLocked(bool handled) { mAppSwitchDueTime = LONG_LONG_MAX; -#if DEBUG_APP_SWITCH - if (handled) { - ALOGD("App switch has arrived."); - } else { - ALOGD("App switch was abandoned."); + if (DEBUG_APP_SWITCH) { + if (handled) { + ALOGD("App switch has arrived."); + } else { + ALOGD("App switch was abandoned."); + } } -#endif } bool InputDispatcher::haveCommandsLocked() const { return !mCommandQueue.empty(); } -bool InputDispatcher::runCommandsLockedInterruptible() { +bool InputDispatcher::runCommandsLockedInterruptable() { if (mCommandQueue.empty()) { return false; } do { - std::unique_ptr<CommandEntry> commandEntry = std::move(mCommandQueue.front()); + auto command = std::move(mCommandQueue.front()); mCommandQueue.pop_front(); - Command command = commandEntry->command; - command(*this, commandEntry.get()); // commands are implicitly 'LockedInterruptible' - - commandEntry->connection.clear(); + // Commands are run with the lock held, but may release and re-acquire the lock from within. + command(); } while (!mCommandQueue.empty()); return true; } -void InputDispatcher::postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) { - mCommandQueue.push_back(std::move(commandEntry)); +void InputDispatcher::postCommandLocked(Command&& command) { + mCommandQueue.push_back(command); } void InputDispatcher::drainInboundQueueLocked() { @@ -1209,9 +1227,9 @@ void InputDispatcher::releasePendingEventLocked() { void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) { InjectionState* injectionState = entry->injectionState; if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) { -#if DEBUG_DISPATCH_CYCLE - ALOGD("Injected inbound event was dropped."); -#endif + if (DEBUG_DISPATCH_CYCLE) { + ALOGD("Injected inbound event was dropped."); + } setInjectionResult(*entry, InputEventInjectionResult::FAILED); } if (entry == mNextUnblockedEvent) { @@ -1246,27 +1264,28 @@ std::shared_ptr<KeyEntry> InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t cur bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime, const ConfigurationChangedEntry& entry) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime); -#endif + if (DEBUG_OUTBOUND_EVENT_DETAILS) { + ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime); + } // Reset key repeating in case a keyboard device was added or removed or something. resetKeyRepeatLocked(); // Enqueue a command to run outside the lock to tell the policy that the configuration changed. - std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( - &InputDispatcher::doNotifyConfigurationChangedLockedInterruptible); - commandEntry->eventTime = entry.eventTime; - postCommandLocked(std::move(commandEntry)); + auto command = [this, eventTime = entry.eventTime]() REQUIRES(mLock) { + scoped_unlock unlock(mLock); + mPolicy->notifyConfigurationChanged(eventTime); + }; + postCommandLocked(std::move(command)); return true; } bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, const DeviceResetEntry& entry) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry.eventTime, - entry.deviceId); -#endif + if (DEBUG_OUTBOUND_EVENT_DETAILS) { + ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry.eventTime, + entry.deviceId); + } // Reset key repeating in case a keyboard device was disabled or enabled. if (mKeyRepeatState.lastKeyEntry && mKeyRepeatState.lastKeyEntry->deviceId == entry.deviceId) { @@ -1390,6 +1409,43 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( dropReason = DropReason::NOT_DROPPED; } +void InputDispatcher::dispatchTouchModeChangeLocked(nsecs_t currentTime, + const std::shared_ptr<TouchModeEntry>& entry) { + const std::vector<sp<WindowInfoHandle>>& windowHandles = + getWindowHandlesLocked(mFocusedDisplayId); + if (windowHandles.empty()) { + return; + } + const std::vector<InputTarget> inputTargets = + getInputTargetsFromWindowHandlesLocked(windowHandles); + if (inputTargets.empty()) { + return; + } + entry->dispatchInProgress = true; + dispatchEventLocked(currentTime, entry, inputTargets); +} + +std::vector<InputTarget> InputDispatcher::getInputTargetsFromWindowHandlesLocked( + const std::vector<sp<WindowInfoHandle>>& windowHandles) const { + std::vector<InputTarget> inputTargets; + for (const sp<WindowInfoHandle>& handle : windowHandles) { + // TODO(b/193718270): Due to performance concerns, consider notifying visible windows only. + const sp<IBinder>& token = handle->getToken(); + if (token == nullptr) { + continue; + } + std::shared_ptr<InputChannel> channel = getInputChannelLocked(token); + if (channel == nullptr) { + continue; // Window has gone away + } + InputTarget target; + target.inputChannel = channel; + target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + inputTargets.push_back(target); + } + return inputTargets; +} + bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { // Preprocessing. @@ -1421,9 +1477,9 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<Key } else if (entry->action == AKEY_EVENT_ACTION_UP && mKeyRepeatState.lastKeyEntry && mKeyRepeatState.lastKeyEntry->deviceId != entry->deviceId) { // The key on device 'deviceId' is still down, do not stop key repeat -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId); -#endif + if (DEBUG_INBOUND_EVENT_DETAILS) { + ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId); + } } else if (!entry->syntheticRepeat) { resetKeyRepeatLocked(); } @@ -1454,13 +1510,13 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<Key // Give the policy a chance to intercept the key. if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { - std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( - &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); sp<IBinder> focusedWindowToken = mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry)); - commandEntry->connectionToken = focusedWindowToken; - commandEntry->keyEntry = entry; - postCommandLocked(std::move(commandEntry)); + + auto command = [this, focusedWindowToken, entry]() REQUIRES(mLock) { + doInterceptKeyBeforeDispatchingCommand(focusedWindowToken, *entry); + }; + postCommandLocked(std::move(command)); return false; // wait for the command to run } else { entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; @@ -1502,47 +1558,42 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<Key } void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", " - "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, " - "metaState=0x%x, repeatCount=%d, downTime=%" PRId64, - prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, entry.policyFlags, - entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState, - entry.repeatCount, entry.downTime); -#endif -} - -void InputDispatcher::doNotifySensorLockedInterruptible(CommandEntry* commandEntry) { - mLock.unlock(); - - const std::shared_ptr<SensorEntry>& entry = commandEntry->sensorEntry; - if (entry->accuracyChanged) { - mPolicy->notifySensorAccuracy(entry->deviceId, entry->sensorType, entry->accuracy); + if (DEBUG_OUTBOUND_EVENT_DETAILS) { + ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", " + "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, " + "metaState=0x%x, repeatCount=%d, downTime=%" PRId64, + prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, + entry.policyFlags, entry.action, entry.flags, entry.keyCode, entry.scanCode, + entry.metaState, entry.repeatCount, entry.downTime); } - mPolicy->notifySensorEvent(entry->deviceId, entry->sensorType, entry->accuracy, - entry->hwTimestamp, entry->values); - mLock.lock(); } -void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry, +void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime, + const std::shared_ptr<SensorEntry>& entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, " - "source=0x%x, sensorType=%s", - entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source, - NamedEnum::string(entry->sensorType).c_str()); -#endif - std::unique_ptr<CommandEntry> commandEntry = - std::make_unique<CommandEntry>(&InputDispatcher::doNotifySensorLockedInterruptible); - commandEntry->sensorEntry = entry; - postCommandLocked(std::move(commandEntry)); + if (DEBUG_OUTBOUND_EVENT_DETAILS) { + ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, " + "source=0x%x, sensorType=%s", + entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source, + ftl::enum_string(entry->sensorType).c_str()); + } + auto command = [this, entry]() REQUIRES(mLock) { + scoped_unlock unlock(mLock); + + if (entry->accuracyChanged) { + mPolicy->notifySensorAccuracy(entry->deviceId, entry->sensorType, entry->accuracy); + } + mPolicy->notifySensorEvent(entry->deviceId, entry->sensorType, entry->accuracy, + entry->hwTimestamp, entry->values); + }; + postCommandLocked(std::move(command)); } bool InputDispatcher::flushSensor(int deviceId, InputDeviceSensorType sensorType) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("flushSensor deviceId=%d, sensorType=%s", deviceId, - NamedEnum::string(sensorType).c_str()); -#endif + if (DEBUG_OUTBOUND_EVENT_DETAILS) { + ALOGD("flushSensor deviceId=%d, sensorType=%s", deviceId, + ftl::enum_string(sensorType).c_str()); + } { // acquire lock std::scoped_lock _l(mLock); @@ -1613,23 +1664,6 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< // Add monitor channels from event's or focused display. addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry)); - if (isPointerEvent) { - std::unordered_map<int32_t, TouchState>::iterator it = - mTouchStatesByDisplay.find(entry->displayId); - if (it != mTouchStatesByDisplay.end()) { - const TouchState& state = it->second; - if (!state.portalWindows.empty()) { - // The event has gone through these portal windows, so we add monitoring targets of - // the corresponding displays as well. - for (size_t i = 0; i < state.portalWindows.size(); i++) { - const WindowInfo* windowInfo = state.portalWindows[i]->getInfo(); - addGlobalMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId, - -windowInfo->frameLeft, -windowInfo->frameTop); - } - } - } - } - // Dispatch the motion. if (conflictingPointerActions) { CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, @@ -1670,42 +1704,43 @@ void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<Dr } void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 - ", policyFlags=0x%x, " - "action=0x%x, actionButton=0x%x, flags=0x%x, " - "metaState=0x%x, buttonState=0x%x," - "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64, - prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, entry.policyFlags, - entry.action, entry.actionButton, entry.flags, entry.metaState, entry.buttonState, - entry.edgeFlags, entry.xPrecision, entry.yPrecision, entry.downTime); - - for (uint32_t i = 0; i < entry.pointerCount; i++) { - ALOGD(" Pointer %d: id=%d, toolType=%d, " - "x=%f, y=%f, pressure=%f, size=%f, " - "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " - "orientation=%f", - i, entry.pointerProperties[i].id, entry.pointerProperties[i].toolType, - entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), - entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), - entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), - entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), - entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), - entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), - entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), - entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); - } -#endif + if (DEBUG_OUTBOUND_EVENT_DETAILS) { + ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 + ", policyFlags=0x%x, " + "action=%s, actionButton=0x%x, flags=0x%x, " + "metaState=0x%x, buttonState=0x%x," + "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64, + prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, + entry.policyFlags, MotionEvent::actionToString(entry.action).c_str(), + entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.edgeFlags, + entry.xPrecision, entry.yPrecision, entry.downTime); + + for (uint32_t i = 0; i < entry.pointerCount; i++) { + ALOGD(" Pointer %d: id=%d, toolType=%d, " + "x=%f, y=%f, pressure=%f, size=%f, " + "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " + "orientation=%f", + i, entry.pointerProperties[i].id, entry.pointerProperties[i].toolType, + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); + } + } } void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<EventEntry> eventEntry, const std::vector<InputTarget>& inputTargets) { ATRACE_CALL(); -#if DEBUG_DISPATCH_CYCLE - ALOGD("dispatchEventToCurrentInputTargets"); -#endif + if (DEBUG_DISPATCH_CYCLE) { + ALOGD("dispatchEventToCurrentInputTargets"); + } updateInteractionTokensLocked(*eventEntry, inputTargets); @@ -1773,11 +1808,12 @@ int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) { } case EventEntry::Type::POINTER_CAPTURE_CHANGED: case EventEntry::Type::FOCUS: + case EventEntry::Type::TOUCH_MODE_CHANGED: case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::SENSOR: case EventEntry::Type::DRAG: { - ALOGE("%s events do not have a target display", NamedEnum::string(entry.type).c_str()); + ALOGE("%s events do not have a target display", ftl::enum_string(entry.type).c_str()); return ADISPLAY_ID_NONE; } } @@ -1829,7 +1865,12 @@ InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked( if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) { ALOGI("Dropping %s event because there is no focused window or focused application in " "display %" PRId32 ".", - NamedEnum::string(entry.type).c_str(), displayId); + ftl::enum_string(entry.type).c_str(), displayId); + return InputEventInjectionResult::FAILED; + } + + // Drop key events if requested by input feature + if (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) { return InputEventInjectionResult::FAILED; } @@ -1854,7 +1895,7 @@ InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked( } else if (currentTime > *mNoFocusedWindowTimeoutTime) { // Already raised ANR. Drop the event ALOGE("Dropping %s event because there is no focused window", - NamedEnum::string(entry.type).c_str()); + ftl::enum_string(entry.type).c_str()); return InputEventInjectionResult::FAILED; } else { // Still waiting for the focused window @@ -2017,12 +2058,11 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)); } bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN; - newTouchedWindowHandle = - findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, - isDown /*addOutsideTargets*/, true /*addPortalWindows*/); + newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, + isDown /*addOutsideTargets*/); std::vector<TouchedMonitor> newGestureMonitors = isDown - ? findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows) + ? findTouchedGestureMonitorsLocked(displayId) : std::vector<TouchedMonitor>{}; // Figure out whether splitting will be allowed for this window. @@ -2070,7 +2110,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( ALOGD("%s", log.c_str()); } } - onUntrustedTouchLocked(occlusionInfo.obscuringPackage); + sendUntrustedTouchCommandLocked(occlusionInfo.obscuringPackage); if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) { ALOGW("Dropping untrusted touch event due to %s/%d", occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid); @@ -2079,6 +2119,11 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( } } + // Drop touch events if requested by input feature + if (newTouchedWindowHandle != nullptr && shouldDropInput(entry, newTouchedWindowHandle)) { + newTouchedWindowHandle = nullptr; + } + // Also don't send the new touch event to unresponsive gesture monitors newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors); @@ -2144,6 +2189,13 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( sp<WindowInfoHandle> oldTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState); + + // Drop touch events if requested by input feature + if (newTouchedWindowHandle != nullptr && + shouldDropInput(entry, newTouchedWindowHandle)) { + newTouchedWindowHandle = nullptr; + } + if (oldTouchedWindowHandle != newTouchedWindowHandle && oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) { if (DEBUG_FOCUS) { @@ -2187,10 +2239,10 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( if (mLastHoverWindowHandle != nullptr && (maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT || mLastHoverWindowHandle != newTouchedWindowHandle)) { -#if DEBUG_HOVER - ALOGD("Sending hover exit event to window %s.", - mLastHoverWindowHandle->getName().c_str()); -#endif + if (DEBUG_HOVER) { + ALOGD("Sending hover exit event to window %s.", + mLastHoverWindowHandle->getName().c_str()); + } tempTouchState.addOrUpdateWindow(mLastHoverWindowHandle, InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0)); } @@ -2200,10 +2252,10 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( if (newHoverWindowHandle != nullptr && (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER || newHoverWindowHandle != newTouchedWindowHandle)) { -#if DEBUG_HOVER - ALOGD("Sending hover enter event to window %s.", - newHoverWindowHandle->getName().c_str()); -#endif + if (DEBUG_HOVER) { + ALOGD("Sending hover enter event to window %s.", + newHoverWindowHandle->getName().c_str()); + } tempTouchState.addOrUpdateWindow(newHoverWindowHandle, InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, BitSet32(0)); @@ -2393,13 +2445,12 @@ Failed: void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) { const sp<WindowInfoHandle> dropWindow = findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/, - false /*addOutsideTargets*/, false /*addPortalWindows*/, - true /*ignoreDragWindow*/); + false /*addOutsideTargets*/, true /*ignoreDragWindow*/); if (dropWindow) { vec2 local = dropWindow->getInfo()->transform.transform(x, y); - notifyDropWindowLocked(dropWindow->getToken(), local.x, local.y); + sendDropWindowCommandLocked(dropWindow->getToken(), local.x, local.y); } else { - notifyDropWindowLocked(nullptr, 0, 0); + sendDropWindowCommandLocked(nullptr, 0, 0); } mDragState.reset(); } @@ -2428,8 +2479,7 @@ void InputDispatcher::addDragEventLocked(const MotionEntry& entry) { const sp<WindowInfoHandle> hoverWindowHandle = findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/, - false /*addOutsideTargets*/, false /*addPortalWindows*/, - true /*ignoreDragWindow*/); + false /*addOutsideTargets*/, true /*ignoreDragWindow*/); // enqueue drag exit if needed. if (hoverWindowHandle != mDragState->dragHoverWindowHandle && !haveSameToken(hoverWindowHandle, mDragState->dragHoverWindowHandle)) { @@ -2446,7 +2496,7 @@ void InputDispatcher::addDragEventLocked(const MotionEntry& entry) { } else if (maskedAction == AMOTION_EVENT_ACTION_UP) { finishDragAndDrop(entry.displayId, x, y); } else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) { - notifyDropWindowLocked(nullptr, 0, 0); + sendDropWindowCommandLocked(nullptr, 0, 0); mDragState.reset(); } } @@ -2474,9 +2524,15 @@ void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHa inputTarget.inputChannel = inputChannel; inputTarget.flags = targetFlags; inputTarget.globalScaleFactor = windowInfo->globalScaleFactor; - inputTarget.displayOrientation = windowInfo->displayOrientation; - inputTarget.displaySize = - int2(windowHandle->getInfo()->displayWidth, windowHandle->getInfo()->displayHeight); + const auto& displayInfoIt = mDisplayInfos.find(windowInfo->displayId); + if (displayInfoIt != mDisplayInfos.end()) { + const auto& displayInfo = displayInfoIt->second; + inputTarget.displayOrientation = displayInfo.transform.getOrientation(); + inputTarget.displaySize = int2(displayInfo.logicalWidth, displayInfo.logicalHeight); + } else { + ALOGI_IF(isPerWindowInputRotationEnabled(), + "DisplayInfo not found for window on display: %d", windowInfo->displayId); + } inputTargets.push_back(inputTarget); it = inputTargets.end() - 1; } @@ -2645,8 +2701,7 @@ std::string InputDispatcher::dumpWindowForTouchOcclusion(const WindowInfo* info, "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32 "], touchableRegion=%s, window={%s}, flags={%s}, inputFeatures={%s}, " "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n", - (isTouchedWindow) ? "[TOUCHED] " : "", - NamedEnum::string(info->type, "%" PRId32).c_str(), + isTouchedWindow ? "[TOUCHED] " : "", ftl::enum_string(info->type).c_str(), info->packageName.c_str(), info->ownerUid, info->id, toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft, info->frameTop, info->frameRight, info->frameBottom, @@ -2734,9 +2789,9 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { if (focusedWindowHandle != nullptr) { const WindowInfo* info = focusedWindowHandle->getInfo(); if (info->inputFeatures.test(WindowInfo::Feature::DISABLE_USER_ACTIVITY)) { -#if DEBUG_DISPATCH_CYCLE - ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str()); -#endif + if (DEBUG_DISPATCH_CYCLE) { + ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str()); + } return; } } @@ -2762,6 +2817,10 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { eventType = USER_ACTIVITY_EVENT_BUTTON; break; } + case EventEntry::Type::TOUCH_MODE_CHANGED: { + break; + } + case EventEntry::Type::FOCUS: case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: @@ -2769,17 +2828,17 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { case EventEntry::Type::POINTER_CAPTURE_CHANGED: case EventEntry::Type::DRAG: { LOG_ALWAYS_FATAL("%s events are not user activity", - NamedEnum::string(eventEntry.type).c_str()); + ftl::enum_string(eventEntry.type).c_str()); break; } } - std::unique_ptr<CommandEntry> commandEntry = - std::make_unique<CommandEntry>(&InputDispatcher::doPokeUserActivityLockedInterruptible); - commandEntry->eventTime = eventEntry.eventTime; - commandEntry->userActivityEventType = eventType; - commandEntry->displayId = displayId; - postCommandLocked(std::move(commandEntry)); + auto command = [this, eventTime = eventEntry.eventTime, eventType, displayId]() + REQUIRES(mLock) { + scoped_unlock unlock(mLock); + mPolicy->pokeUserActivity(eventTime, eventType, displayId); + }; + postCommandLocked(std::move(command)); } void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, @@ -2792,21 +2851,21 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, connection->getInputChannelName().c_str(), eventEntry->id); ATRACE_NAME(message.c_str()); } -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " - "globalScaleFactor=%f, pointerIds=0x%x %s", - connection->getInputChannelName().c_str(), inputTarget.flags, - inputTarget.globalScaleFactor, inputTarget.pointerIds.value, - inputTarget.getPointerInfoString().c_str()); -#endif + if (DEBUG_DISPATCH_CYCLE) { + ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " + "globalScaleFactor=%f, pointerIds=0x%x %s", + connection->getInputChannelName().c_str(), inputTarget.flags, + inputTarget.globalScaleFactor, inputTarget.pointerIds.value, + inputTarget.getPointerInfoString().c_str()); + } // Skip this event if the connection status is not normal. // We don't want to enqueue additional outbound events if the connection is broken. if (connection->status != Connection::STATUS_NORMAL) { -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ Dropping event because the channel status is %s", - connection->getInputChannelName().c_str(), connection->getStatusLabel()); -#endif + if (DEBUG_DISPATCH_CYCLE) { + ALOGD("channel '%s' ~ Dropping event because the channel status is %s", + connection->getInputChannelName().c_str(), connection->getStatusLabel()); + } return; } @@ -2814,7 +2873,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, if (inputTarget.flags & InputTarget::FLAG_SPLIT) { LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION, "Entry type %s should not have FLAG_SPLIT", - NamedEnum::string(eventEntry->type).c_str()); + ftl::enum_string(eventEntry->type).c_str()); const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry); if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) { @@ -2905,10 +2964,11 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) { -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event", - connection->getInputChannelName().c_str()); -#endif + if (DEBUG_DISPATCH_CYCLE) { + ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key " + "event", + connection->getInputChannelName().c_str()); + } return; // skip the inconsistent event } break; @@ -2938,11 +2998,11 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE && !connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source, motionEntry.displayId)) { -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter " - "event", - connection->getInputChannelName().c_str()); -#endif + if (DEBUG_DISPATCH_CYCLE) { + ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover " + "enter event", + connection->getInputChannelName().c_str()); + } // We keep the 'resolvedEventId' here equal to the original 'motionEntry.id' because // this is a one-to-one event conversion. dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER; @@ -2958,11 +3018,11 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio if (!connection->inputState.trackMotion(motionEntry, dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) { -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion " - "event", - connection->getInputChannelName().c_str()); -#endif + if (DEBUG_DISPATCH_CYCLE) { + ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion " + "event", + connection->getInputChannelName().c_str()); + } return; // skip the inconsistent event } @@ -2989,6 +3049,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio break; } case EventEntry::Type::FOCUS: + case EventEntry::Type::TOUCH_MODE_CHANGED: case EventEntry::Type::POINTER_CAPTURE_CHANGED: case EventEntry::Type::DRAG: { break; @@ -3000,7 +3061,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("%s events should not go to apps", - NamedEnum::string(newEntry.type).c_str()); + ftl::enum_string(newEntry.type).c_str()); break; } } @@ -3094,10 +3155,11 @@ void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t a return; } - std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( - &InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible); - commandEntry->newToken = token; - postCommandLocked(std::move(commandEntry)); + auto command = [this, token]() REQUIRES(mLock) { + scoped_unlock unlock(mLock); + mPolicy->onPointerDownOutsideFocus(token); + }; + postCommandLocked(std::move(command)); } void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, @@ -3107,9 +3169,9 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, connection->getInputChannelName().c_str()); ATRACE_NAME(message.c_str()); } -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str()); -#endif + if (DEBUG_DISPATCH_CYCLE) { + ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str()); + } while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) { DispatchEntry* dispatchEntry = connection->outboundQueue.front(); @@ -3206,6 +3268,16 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, break; } + case EventEntry::Type::TOUCH_MODE_CHANGED: { + const TouchModeEntry& touchModeEntry = + static_cast<const TouchModeEntry&>(eventEntry); + status = connection->inputPublisher + .publishTouchModeEvent(dispatchEntry->seq, touchModeEntry.id, + touchModeEntry.inTouchMode); + + break; + } + case EventEntry::Type::POINTER_CAPTURE_CHANGED: { const auto& captureEntry = static_cast<const PointerCaptureChangedEntry&>(eventEntry); @@ -3228,7 +3300,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::SENSOR: { LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events", - NamedEnum::string(eventEntry.type).c_str()); + ftl::enum_string(eventEntry.type).c_str()); return; } } @@ -3247,11 +3319,11 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } else { // Pipe is full and we are waiting for the app to finish process some events // before sending more events to it. -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ Could not publish event because the pipe is full, " - "waiting for the application to catch up", - connection->getInputChannelName().c_str()); -#endif + if (DEBUG_DISPATCH_CYCLE) { + ALOGD("channel '%s' ~ Could not publish event because the pipe is full, " + "waiting for the application to catch up", + connection->getInputChannelName().c_str()); + } } } else { ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, " @@ -3318,10 +3390,10 @@ const std::array<uint8_t, 32> InputDispatcher::getSignature( void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled, nsecs_t consumeTime) { -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s", - connection->getInputChannelName().c_str(), seq, toString(handled)); -#endif + if (DEBUG_DISPATCH_CYCLE) { + ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s", + connection->getInputChannelName().c_str(), seq, toString(handled)); + } if (connection->status == Connection::STATUS_BROKEN || connection->status == Connection::STATUS_ZOMBIE) { @@ -3329,16 +3401,19 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, } // Notify other system components and prepare to start the next dispatch cycle. - onDispatchCycleFinishedLocked(currentTime, connection, seq, handled, consumeTime); + auto command = [this, currentTime, connection, seq, handled, consumeTime]() REQUIRES(mLock) { + doDispatchCycleFinishedCommand(currentTime, connection, seq, handled, consumeTime); + }; + postCommandLocked(std::move(command)); } void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, bool notify) { -#if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s", - connection->getInputChannelName().c_str(), toString(notify)); -#endif + if (DEBUG_DISPATCH_CYCLE) { + ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s", + connection->getInputChannelName().c_str(), toString(notify)); + } // Clear the dispatch queues. drainDispatchQueue(connection->outboundQueue); @@ -3353,7 +3428,15 @@ void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, if (notify) { // Notify other system components. - onDispatchCycleBrokenLocked(currentTime, connection); + ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!", + connection->getInputChannelName().c_str()); + + auto command = [this, connection]() REQUIRES(mLock) { + if (connection->status == Connection::STATUS_ZOMBIE) return; + scoped_unlock unlock(mLock); + mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken()); + }; + postCommandLocked(std::move(command)); } } } @@ -3420,7 +3503,7 @@ int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionTok gotOne = true; } if (gotOne) { - runCommandsLockedInterruptible(); + runCommandsLockedInterruptable(); if (status == WOULD_BLOCK) { return 1; } @@ -3497,12 +3580,12 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( if (cancelationEvents.empty()) { return; } -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync " - "with reality: %s, mode=%d.", - connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason, - options.mode); -#endif + if (DEBUG_OUTBOUND_EVENT_DETAILS) { + ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync " + "with reality: %s, mode=%d.", + connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason, + options.mode); + } InputTarget target; sp<WindowInfoHandle> windowHandle = @@ -3529,17 +3612,18 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( break; } case EventEntry::Type::FOCUS: + case EventEntry::Type::TOUCH_MODE_CHANGED: case EventEntry::Type::POINTER_CAPTURE_CHANGED: case EventEntry::Type::DRAG: { LOG_ALWAYS_FATAL("Canceling %s events is not supported", - NamedEnum::string(cancelationEventEntry->type).c_str()); + ftl::enum_string(cancelationEventEntry->type).c_str()); break; } case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::SENSOR: { LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue", - NamedEnum::string(cancelationEventEntry->type).c_str()); + ftl::enum_string(cancelationEventEntry->type).c_str()); break; } } @@ -3566,10 +3650,10 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( return; } -#if DEBUG_OUTBOUND_EVENT_DETAILS + if (DEBUG_OUTBOUND_EVENT_DETAILS) { ALOGD("channel '%s' ~ Synthesized %zu down events to ensure consistent event stream.", connection->getInputChannelName().c_str(), downEvents.size()); -#endif + } InputTarget target; sp<WindowInfoHandle> windowHandle = @@ -3592,13 +3676,14 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( case EventEntry::Type::KEY: case EventEntry::Type::FOCUS: + case EventEntry::Type::TOUCH_MODE_CHANGED: case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::POINTER_CAPTURE_CHANGED: case EventEntry::Type::SENSOR: case EventEntry::Type::DRAG: { LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue", - NamedEnum::string(downEventEntry->type).c_str()); + ftl::enum_string(downEventEntry->type).c_str()); break; } } @@ -3712,9 +3797,9 @@ std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( } void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime); -#endif + if (DEBUG_INBOUND_EVENT_DETAILS) { + ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime); + } bool needWake; { // acquire lock @@ -3768,14 +3853,14 @@ void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int3 } void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifyKey - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 - "policyFlags=0x%x, action=0x%x, " - "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64, - args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, - args->action, args->flags, args->keyCode, args->scanCode, args->metaState, - args->downTime); -#endif + if (DEBUG_INBOUND_EVENT_DETAILS) { + ALOGD("notifyKey - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 + "policyFlags=0x%x, action=0x%x, " + "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64, + args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, + args->action, args->flags, args->keyCode, args->scanCode, args->metaState, + args->downTime); + } if (!validateKeyEvent(args->action)) { return; } @@ -3846,33 +3931,33 @@ bool InputDispatcher::shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args } void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, " - "displayId=%" PRId32 ", policyFlags=0x%x, " - "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, " - "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, " - "yCursorPosition=%f, downTime=%" PRId64, - args->id, args->eventTime, args->deviceId, args->source, args->displayId, - args->policyFlags, args->action, args->actionButton, args->flags, args->metaState, - args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision, - args->xCursorPosition, args->yCursorPosition, args->downTime); - for (uint32_t i = 0; i < args->pointerCount; i++) { - ALOGD(" Pointer %d: id=%d, toolType=%d, " - "x=%f, y=%f, pressure=%f, size=%f, " - "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " - "orientation=%f", - i, args->pointerProperties[i].id, args->pointerProperties[i].toolType, - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); - } -#endif + if (DEBUG_INBOUND_EVENT_DETAILS) { + ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, " + "displayId=%" PRId32 ", policyFlags=0x%x, " + "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, " + "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, " + "yCursorPosition=%f, downTime=%" PRId64, + args->id, args->eventTime, args->deviceId, args->source, args->displayId, + args->policyFlags, args->action, args->actionButton, args->flags, args->metaState, + args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision, + args->xCursorPosition, args->yCursorPosition, args->downTime); + for (uint32_t i = 0; i < args->pointerCount; i++) { + ALOGD(" Pointer %d: id=%d, toolType=%d, " + "x=%f, y=%f, pressure=%f, size=%f, " + "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " + "orientation=%f", + i, args->pointerProperties[i].id, args->pointerProperties[i].toolType, + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); + } + } if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount, args->pointerProperties)) { return; @@ -3925,6 +4010,13 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { args->downTime, args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0); + if (args->id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID && + IdGenerator::getSource(args->id) == IdGenerator::Source::INPUT_READER && + !mInputFilterEnabled) { + const bool isDown = args->action == AMOTION_EVENT_ACTION_DOWN; + mLatencyTracker.trackListener(args->id, isDown, args->eventTime, args->readTime); + } + needWake = enqueueInboundEventLocked(std::move(newEntry)); mLock.unlock(); } // release lock @@ -3935,12 +4027,12 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { } void InputDispatcher::notifySensor(const NotifySensorArgs* args) { -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, " - " sensorType=%s", - args->id, args->eventTime, args->deviceId, args->source, - NamedEnum::string(args->sensorType).c_str()); -#endif + if (DEBUG_INBOUND_EVENT_DETAILS) { + ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, " + " sensorType=%s", + args->id, args->eventTime, args->deviceId, args->source, + ftl::enum_string(args->sensorType).c_str()); + } bool needWake; { // acquire lock @@ -3963,10 +4055,10 @@ void InputDispatcher::notifySensor(const NotifySensorArgs* args) { } void InputDispatcher::notifyVibratorState(const NotifyVibratorStateArgs* args) { -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d, isOn=%d", args->eventTime, - args->deviceId, args->isOn); -#endif + if (DEBUG_INBOUND_EVENT_DETAILS) { + ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d, isOn=%d", args->eventTime, + args->deviceId, args->isOn); + } mPolicy->notifyVibratorState(args->deviceId, args->isOn); } @@ -3975,11 +4067,11 @@ bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs } void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) { -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, " - "switchMask=0x%08x", - args->eventTime, args->policyFlags, args->switchValues, args->switchMask); -#endif + if (DEBUG_INBOUND_EVENT_DETAILS) { + ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, " + "switchMask=0x%08x", + args->eventTime, args->policyFlags, args->switchValues, args->switchMask); + } uint32_t policyFlags = args->policyFlags; policyFlags |= POLICY_FLAG_TRUSTED; @@ -3987,10 +4079,10 @@ void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) { } void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime, - args->deviceId); -#endif + if (DEBUG_INBOUND_EVENT_DETAILS) { + ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime, + args->deviceId); + } bool needWake; { // acquire lock @@ -4007,10 +4099,10 @@ void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { } void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) { -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime, - args->request.enable ? "true" : "false"); -#endif + if (DEBUG_INBOUND_EVENT_DETAILS) { + ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime, + args->request.enable ? "true" : "false"); + } bool needWake; { // acquire lock @@ -4028,11 +4120,11 @@ void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChan InputEventInjectionResult InputDispatcher::injectInputEvent( const InputEvent* event, int32_t injectorPid, int32_t injectorUid, InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) { -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, " - "syncMode=%d, timeout=%lld, policyFlags=0x%08x", - event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags); -#endif + if (DEBUG_INBOUND_EVENT_DETAILS) { + ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, " + "syncMode=%d, timeout=%lld, policyFlags=0x%08x", + event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags); + } nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count(); policyFlags |= POLICY_FLAG_INJECTED; @@ -4210,10 +4302,10 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( nsecs_t remainingTimeout = endTime - now(); if (remainingTimeout <= 0) { -#if DEBUG_INJECTION - ALOGD("injectInputEvent - Timed out waiting for injection result " - "to become available."); -#endif + if (DEBUG_INJECTION) { + ALOGD("injectInputEvent - Timed out waiting for injection result " + "to become available."); + } injectionResult = InputEventInjectionResult::TIMED_OUT; break; } @@ -4224,16 +4316,16 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( if (injectionResult == InputEventInjectionResult::SUCCEEDED && syncMode == InputEventInjectionSync::WAIT_FOR_FINISHED) { while (injectionState->pendingForegroundDispatches != 0) { -#if DEBUG_INJECTION - ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.", - injectionState->pendingForegroundDispatches); -#endif + if (DEBUG_INJECTION) { + ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.", + injectionState->pendingForegroundDispatches); + } nsecs_t remainingTimeout = endTime - now(); if (remainingTimeout <= 0) { -#if DEBUG_INJECTION - ALOGD("injectInputEvent - Timed out waiting for pending foreground " - "dispatches to finish."); -#endif + if (DEBUG_INJECTION) { + ALOGD("injectInputEvent - Timed out waiting for pending foreground " + "dispatches to finish."); + } injectionResult = InputEventInjectionResult::TIMED_OUT; break; } @@ -4246,10 +4338,10 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( injectionState->release(); } // release lock -#if DEBUG_INJECTION - ALOGD("injectInputEvent - Finished with result %d. injectorPid=%d, injectorUid=%d", - injectionResult, injectorPid, injectorUid); -#endif + if (DEBUG_INJECTION) { + ALOGD("injectInputEvent - Finished with result %d. injectorPid=%d, injectorUid=%d", + injectionResult, injectorPid, injectorUid); + } return injectionResult; } @@ -4296,11 +4388,11 @@ void InputDispatcher::setInjectionResult(EventEntry& entry, InputEventInjectionResult injectionResult) { InjectionState* injectionState = entry.injectionState; if (injectionState) { -#if DEBUG_INJECTION - ALOGD("Setting input event injection result to %d. " - "injectorPid=%d, injectorUid=%d", - injectionResult, injectionState->injectorPid, injectionState->injectorUid); -#endif + if (DEBUG_INJECTION) { + ALOGD("Setting input event injection result to %d. " + "injectorPid=%d, injectorUid=%d", + injectionResult, injectionState->injectorPid, injectionState->injectorUid); + } if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) { // Log the outcome since the injector did not wait for the injection result. @@ -4460,8 +4552,7 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( std::vector<sp<WindowInfoHandle>> newHandles; for (const sp<WindowInfoHandle>& handle : windowInfoHandles) { const WindowInfo* info = handle->getInfo(); - if ((getInputChannelLocked(handle->getToken()) == nullptr && - info->portalToDisplayId == ADISPLAY_ID_NONE)) { + if (getInputChannelLocked(handle->getToken()) == nullptr) { const bool noInputChannel = info->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL); const bool canReceiveInput = !info->flags.test(WindowInfo::Flag::NOT_TOUCHABLE) || @@ -4495,6 +4586,7 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( void InputDispatcher::setInputWindows( const std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>>& handlesPerDisplay) { + // TODO(b/198444055): Remove setInputWindows from InputDispatcher. { // acquire lock std::scoped_lock _l(mLock); for (const auto& [displayId, handles] : handlesPerDisplay) { @@ -4575,6 +4667,18 @@ void InputDispatcher::setInputWindowsLocked( CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, "touched window was removed"); synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options); + // Since we are about to drop the touch, cancel the events for the wallpaper as + // well. + if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND && + touchedWindow.windowHandle->getInfo()->hasWallpaper) { + sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow(); + if (wallpaper != nullptr) { + sp<Connection> wallpaperConnection = + getConnectionLocked(wallpaper->getToken()); + synthesizeCancelationEventsForConnectionLocked(wallpaperConnection, + options); + } + } } state.windows.erase(state.windows.begin() + i); } else { @@ -4706,7 +4810,7 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { // Find new focused window and validate sp<IBinder> newFocusedWindowToken = mFocusResolver.getFocusedWindowToken(displayId); - notifyFocusChangedLocked(oldFocusedWindowToken, newFocusedWindowToken); + sendFocusChangedCommandLocked(oldFocusedWindowToken, newFocusedWindowToken); if (newFocusedWindowToken == nullptr) { ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId); @@ -4785,6 +4889,7 @@ void InputDispatcher::setInputFilterEnabled(bool enabled) { void InputDispatcher::setInTouchMode(bool inTouchMode) { std::scoped_lock lock(mLock); mInTouchMode = inTouchMode; + // TODO(b/193718270): Fire TouchModeEvent here. } void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) { @@ -5003,14 +5108,6 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { } else { dump += INDENT3 "Windows: <none>\n"; } - if (!state.portalWindows.empty()) { - dump += INDENT3 "Portal windows:\n"; - for (size_t i = 0; i < state.portalWindows.size(); i++) { - const sp<WindowInfoHandle> portalWindowHandle = state.portalWindows[i]; - dump += StringPrintf(INDENT4 "%zu: name='%s'\n", i, - portalWindowHandle->getName().c_str()); - } - } } } else { dump += INDENT "TouchStates: <no displays touched>\n"; @@ -5022,9 +5119,17 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { } if (!mWindowHandlesByDisplay.empty()) { - for (auto& it : mWindowHandlesByDisplay) { - const std::vector<sp<WindowInfoHandle>> windowHandles = it.second; - dump += StringPrintf(INDENT "Display: %" PRId32 "\n", it.first); + for (const auto& [displayId, windowHandles] : mWindowHandlesByDisplay) { + dump += StringPrintf(INDENT "Display: %" PRId32 "\n", displayId); + if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) { + const auto& displayInfo = it->second; + dump += StringPrintf(INDENT2 "logicalSize=%dx%d\n", displayInfo.logicalWidth, + displayInfo.logicalHeight); + displayInfo.transform.dump(dump, "transform", INDENT4); + } else { + dump += INDENT2 "No DisplayInfo found!\n"; + } + if (!windowHandles.empty()) { dump += INDENT2 "Windows:\n"; for (size_t i = 0; i < windowHandles.size(); i++) { @@ -5032,7 +5137,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { const WindowInfo* windowInfo = windowHandle->getInfo(); dump += StringPrintf(INDENT3 "%zu: name='%s', id=%" PRId32 ", displayId=%d, " - "portalToDisplayId=%d, paused=%s, focusable=%s, " + "paused=%s, focusable=%s, " "hasWallpaper=%s, visible=%s, alpha=%.2f, " "flags=%s, type=%s, " "frame=[%d,%d][%d,%d], globalScale=%f, " @@ -5040,13 +5145,12 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { "applicationInfo.token=%s, " "touchableRegion=", i, windowInfo->name.c_str(), windowInfo->id, - windowInfo->displayId, windowInfo->portalToDisplayId, - toString(windowInfo->paused), + windowInfo->displayId, toString(windowInfo->paused), toString(windowInfo->focusable), toString(windowInfo->hasWallpaper), toString(windowInfo->visible), windowInfo->alpha, windowInfo->flags.string().c_str(), - NamedEnum::string(windowInfo->type).c_str(), + ftl::enum_string(windowInfo->type).c_str(), windowInfo->frameLeft, windowInfo->frameTop, windowInfo->frameRight, windowInfo->frameBottom, windowInfo->globalScaleFactor, @@ -5057,13 +5161,12 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { windowInfo->inputFeatures.string().c_str()); dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64 "ms, trustedOverlay=%s, hasToken=%s, " - "touchOcclusionMode=%s, displayOrientation=%d\n", + "touchOcclusionMode=%s\n", windowInfo->ownerPid, windowInfo->ownerUid, millis(windowInfo->dispatchingTimeout), toString(windowInfo->trustedOverlay), toString(windowInfo->token != nullptr), - toString(windowInfo->touchOcclusionMode).c_str(), - windowInfo->displayOrientation); + toString(windowInfo->touchOcclusionMode).c_str()); windowInfo->transform.dump(dump, "transform", INDENT4); } } else { @@ -5138,6 +5241,12 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT "ReplacedKeys: <empty>\n"; } + if (!mCommandQueue.empty()) { + dump += StringPrintf(INDENT "CommandQueue: size=%zu\n", mCommandQueue.size()); + } else { + dump += INDENT "CommandQueue: <empty>\n"; + } + if (!mConnectionsByToken.empty()) { dump += INDENT "Connections:\n"; for (const auto& [token, connection] : mConnectionsByToken) { @@ -5204,9 +5313,9 @@ private: }; Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) { -#if DEBUG_CHANNEL_CREATION - ALOGD("channel '%s' ~ createInputChannel", name.c_str()); -#endif + if (DEBUG_CHANNEL_CREATION) { + ALOGD("channel '%s' ~ createInputChannel", name.c_str()); + } std::unique_ptr<InputChannel> serverChannel; std::unique_ptr<InputChannel> clientChannel; @@ -5486,46 +5595,92 @@ void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) { mConnectionsByToken.erase(connection->inputChannel->getConnectionToken()); } -void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime, - const sp<Connection>& connection, uint32_t seq, - bool handled, nsecs_t consumeTime) { - std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( - &InputDispatcher::doDispatchCycleFinishedLockedInterruptible); - commandEntry->connection = connection; - commandEntry->eventTime = currentTime; - commandEntry->seq = seq; - commandEntry->handled = handled; - commandEntry->consumeTime = consumeTime; - postCommandLocked(std::move(commandEntry)); -} +void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, + const sp<Connection>& connection, uint32_t seq, + bool handled, nsecs_t consumeTime) { + // Handle post-event policy actions. + std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq); + if (dispatchEntryIt == connection->waitQueue.end()) { + return; + } + DispatchEntry* dispatchEntry = *dispatchEntryIt; + const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; + if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { + ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(), + ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str()); + } + if (shouldReportFinishedEvent(*dispatchEntry, *connection)) { + mLatencyTracker.trackFinishedEvent(dispatchEntry->eventEntry->id, + connection->inputChannel->getConnectionToken(), + dispatchEntry->deliveryTime, consumeTime, finishTime); + } -void InputDispatcher::onDispatchCycleBrokenLocked(nsecs_t currentTime, - const sp<Connection>& connection) { - ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!", - connection->getInputChannelName().c_str()); + bool restartEvent; + if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) { + KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry)); + restartEvent = + afterKeyEventLockedInterruptable(connection, dispatchEntry, keyEntry, handled); + } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) { + MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry)); + restartEvent = afterMotionEventLockedInterruptable(connection, dispatchEntry, motionEntry, + handled); + } else { + restartEvent = false; + } + + // Dequeue the event and start the next cycle. + // Because the lock might have been released, it is possible that the + // contents of the wait queue to have been drained, so we need to double-check + // a few things. + dispatchEntryIt = connection->findWaitQueueEntry(seq); + if (dispatchEntryIt != connection->waitQueue.end()) { + dispatchEntry = *dispatchEntryIt; + connection->waitQueue.erase(dispatchEntryIt); + const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken(); + mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken); + if (!connection->responsive) { + connection->responsive = isConnectionResponsive(*connection); + if (connection->responsive) { + // The connection was unresponsive, and now it's responsive. + processConnectionResponsiveLocked(*connection); + } + } + traceWaitQueueLength(*connection); + if (restartEvent && connection->status == Connection::STATUS_NORMAL) { + connection->outboundQueue.push_front(dispatchEntry); + traceOutboundQueueLength(*connection); + } else { + releaseDispatchEntry(dispatchEntry); + } + } - std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( - &InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); - commandEntry->connection = connection; - postCommandLocked(std::move(commandEntry)); + // Start the next dispatch cycle for this connection. + startDispatchCycleLocked(now(), connection); } -void InputDispatcher::notifyFocusChangedLocked(const sp<IBinder>& oldToken, - const sp<IBinder>& newToken) { - std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( - &InputDispatcher::doNotifyFocusChangedLockedInterruptible); - commandEntry->oldToken = oldToken; - commandEntry->newToken = newToken; - postCommandLocked(std::move(commandEntry)); +void InputDispatcher::sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, + const sp<IBinder>& newToken) { + auto command = [this, oldToken, newToken]() REQUIRES(mLock) { + scoped_unlock unlock(mLock); + mPolicy->notifyFocusChanged(oldToken, newToken); + }; + postCommandLocked(std::move(command)); } -void InputDispatcher::notifyDropWindowLocked(const sp<IBinder>& token, float x, float y) { - std::unique_ptr<CommandEntry> commandEntry = - std::make_unique<CommandEntry>(&InputDispatcher::doNotifyDropWindowLockedInterruptible); - commandEntry->newToken = token; - commandEntry->x = x; - commandEntry->y = y; - postCommandLocked(std::move(commandEntry)); +void InputDispatcher::sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) { + auto command = [this, token, x, y]() REQUIRES(mLock) { + scoped_unlock unlock(mLock); + mPolicy->notifyDropWindow(token, x, y); + }; + postCommandLocked(std::move(command)); +} + +void InputDispatcher::sendUntrustedTouchCommandLocked(const std::string& obscuringPackage) { + auto command = [this, obscuringPackage]() REQUIRES(mLock) { + scoped_unlock unlock(mLock); + mPolicy->notifyUntrustedTouch(obscuringPackage); + }; + postCommandLocked(std::move(command)); } void InputDispatcher::onAnrLocked(const sp<Connection>& connection) { @@ -5568,17 +5723,11 @@ void InputDispatcher::onAnrLocked(std::shared_ptr<InputApplicationHandle> applic StringPrintf("%s does not have a focused window", application->getName().c_str()); updateLastAnrStateLocked(*application, reason); - std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( - &InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible); - commandEntry->inputApplicationHandle = std::move(application); - postCommandLocked(std::move(commandEntry)); -} - -void InputDispatcher::onUntrustedTouchLocked(const std::string& obscuringPackage) { - std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( - &InputDispatcher::doNotifyUntrustedTouchLockedInterruptible); - commandEntry->obscuringPackage = obscuringPackage; - postCommandLocked(std::move(commandEntry)); + auto command = [this, application = std::move(application)]() REQUIRES(mLock) { + scoped_unlock unlock(mLock); + mPolicy->notifyNoFocusedWindowAnr(application); + }; + postCommandLocked(std::move(command)); } void InputDispatcher::updateLastAnrStateLocked(const sp<WindowInfoHandle>& window, @@ -5609,109 +5758,24 @@ void InputDispatcher::updateLastAnrStateLocked(const std::string& windowLabel, dumpDispatchStateLocked(mLastAnrState); } -void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) { - mLock.unlock(); - - mPolicy->notifyConfigurationChanged(commandEntry->eventTime); - - mLock.lock(); -} - -void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) { - sp<Connection> connection = commandEntry->connection; - - if (connection->status != Connection::STATUS_ZOMBIE) { - mLock.unlock(); - - mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken()); - - mLock.lock(); - } -} - -void InputDispatcher::doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) { - sp<IBinder> oldToken = commandEntry->oldToken; - sp<IBinder> newToken = commandEntry->newToken; - mLock.unlock(); - mPolicy->notifyFocusChanged(oldToken, newToken); - mLock.lock(); -} - -void InputDispatcher::doNotifyDropWindowLockedInterruptible(CommandEntry* commandEntry) { - sp<IBinder> newToken = commandEntry->newToken; - mLock.unlock(); - mPolicy->notifyDropWindow(newToken, commandEntry->x, commandEntry->y); - mLock.lock(); -} - -void InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) { - mLock.unlock(); - - mPolicy->notifyNoFocusedWindowAnr(commandEntry->inputApplicationHandle); - - mLock.lock(); -} - -void InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) { - mLock.unlock(); - - mPolicy->notifyWindowUnresponsive(commandEntry->connectionToken, commandEntry->reason); - - mLock.lock(); -} - -void InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) { - mLock.unlock(); - - mPolicy->notifyMonitorUnresponsive(commandEntry->pid, commandEntry->reason); - - mLock.lock(); -} - -void InputDispatcher::doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) { - mLock.unlock(); - - mPolicy->notifyWindowResponsive(commandEntry->connectionToken); - - mLock.lock(); -} - -void InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) { - mLock.unlock(); - - mPolicy->notifyMonitorResponsive(commandEntry->pid); - - mLock.lock(); -} - -void InputDispatcher::doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) { - mLock.unlock(); - - mPolicy->notifyUntrustedTouch(commandEntry->obscuringPackage); - - mLock.lock(); -} - -void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( - CommandEntry* commandEntry) { - KeyEntry& entry = *(commandEntry->keyEntry); - KeyEvent event = createKeyEvent(entry); - - mLock.unlock(); - - android::base::Timer t; - const sp<IBinder>& token = commandEntry->connectionToken; - nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry.policyFlags); - if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { - ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms", - std::to_string(t.duration().count()).c_str()); - } - - mLock.lock(); +void InputDispatcher::doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken, + KeyEntry& entry) { + const KeyEvent event = createKeyEvent(entry); + nsecs_t delay = 0; + { // release lock + scoped_unlock unlock(mLock); + android::base::Timer t; + delay = mPolicy->interceptKeyBeforeDispatching(focusedWindowToken, &event, + entry.policyFlags); + if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { + ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms", + std::to_string(t.duration().count()).c_str()); + } + } // acquire lock if (delay < 0) { entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP; - } else if (!delay) { + } else if (delay == 0) { entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; } else { entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER; @@ -5719,122 +5783,37 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( } } -void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) { - mLock.unlock(); - mPolicy->onPointerDownOutsideFocus(commandEntry->newToken); - mLock.lock(); -} - -/** - * Connection is responsive if it has no events in the waitQueue that are older than the - * current time. - */ -static bool isConnectionResponsive(const Connection& connection) { - const nsecs_t currentTime = now(); - for (const DispatchEntry* entry : connection.waitQueue) { - if (entry->timeoutTime < currentTime) { - return false; - } - } - return true; -} - -void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) { - sp<Connection> connection = commandEntry->connection; - const nsecs_t finishTime = commandEntry->eventTime; - uint32_t seq = commandEntry->seq; - const bool handled = commandEntry->handled; - - // Handle post-event policy actions. - std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq); - if (dispatchEntryIt == connection->waitQueue.end()) { - return; - } - DispatchEntry* dispatchEntry = *dispatchEntryIt; - const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; - if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { - ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(), - ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str()); - } - if (shouldReportFinishedEvent(*dispatchEntry, *connection)) { - mLatencyTracker.trackFinishedEvent(dispatchEntry->eventEntry->id, - connection->inputChannel->getConnectionToken(), - dispatchEntry->deliveryTime, commandEntry->consumeTime, - finishTime); - } - - bool restartEvent; - if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) { - KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry)); - restartEvent = - afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled); - } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) { - MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry)); - restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry, - handled); - } else { - restartEvent = false; - } - - // Dequeue the event and start the next cycle. - // Because the lock might have been released, it is possible that the - // contents of the wait queue to have been drained, so we need to double-check - // a few things. - dispatchEntryIt = connection->findWaitQueueEntry(seq); - if (dispatchEntryIt != connection->waitQueue.end()) { - dispatchEntry = *dispatchEntryIt; - connection->waitQueue.erase(dispatchEntryIt); - const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken(); - mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken); - if (!connection->responsive) { - connection->responsive = isConnectionResponsive(*connection); - if (connection->responsive) { - // The connection was unresponsive, and now it's responsive. - processConnectionResponsiveLocked(*connection); - } - } - traceWaitQueueLength(*connection); - if (restartEvent && connection->status == Connection::STATUS_NORMAL) { - connection->outboundQueue.push_front(dispatchEntry); - traceOutboundQueueLength(*connection); - } else { - releaseDispatchEntry(dispatchEntry); - } - } - - // Start the next dispatch cycle for this connection. - startDispatchCycleLocked(now(), connection); -} - void InputDispatcher::sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) { - std::unique_ptr<CommandEntry> monitorUnresponsiveCommand = std::make_unique<CommandEntry>( - &InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible); - monitorUnresponsiveCommand->pid = pid; - monitorUnresponsiveCommand->reason = std::move(reason); - postCommandLocked(std::move(monitorUnresponsiveCommand)); + auto command = [this, pid, reason = std::move(reason)]() REQUIRES(mLock) { + scoped_unlock unlock(mLock); + mPolicy->notifyMonitorUnresponsive(pid, reason); + }; + postCommandLocked(std::move(command)); } -void InputDispatcher::sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken, +void InputDispatcher::sendWindowUnresponsiveCommandLocked(const sp<IBinder>& token, std::string reason) { - std::unique_ptr<CommandEntry> windowUnresponsiveCommand = std::make_unique<CommandEntry>( - &InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible); - windowUnresponsiveCommand->connectionToken = std::move(connectionToken); - windowUnresponsiveCommand->reason = std::move(reason); - postCommandLocked(std::move(windowUnresponsiveCommand)); + auto command = [this, token, reason = std::move(reason)]() REQUIRES(mLock) { + scoped_unlock unlock(mLock); + mPolicy->notifyWindowUnresponsive(token, reason); + }; + postCommandLocked(std::move(command)); } void InputDispatcher::sendMonitorResponsiveCommandLocked(int32_t pid) { - std::unique_ptr<CommandEntry> monitorResponsiveCommand = std::make_unique<CommandEntry>( - &InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible); - monitorResponsiveCommand->pid = pid; - postCommandLocked(std::move(monitorResponsiveCommand)); + auto command = [this, pid]() REQUIRES(mLock) { + scoped_unlock unlock(mLock); + mPolicy->notifyMonitorResponsive(pid); + }; + postCommandLocked(std::move(command)); } -void InputDispatcher::sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) { - std::unique_ptr<CommandEntry> windowResponsiveCommand = std::make_unique<CommandEntry>( - &InputDispatcher::doNotifyWindowResponsiveLockedInterruptible); - windowResponsiveCommand->connectionToken = std::move(connectionToken); - postCommandLocked(std::move(windowResponsiveCommand)); +void InputDispatcher::sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken) { + auto command = [this, connectionToken]() REQUIRES(mLock) { + scoped_unlock unlock(mLock); + mPolicy->notifyWindowResponsive(connectionToken); + }; + postCommandLocked(std::move(command)); } /** @@ -5882,7 +5861,7 @@ void InputDispatcher::processConnectionResponsiveLocked(const Connection& connec sendWindowResponsiveCommandLocked(connectionToken); } -bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection, +bool InputDispatcher::afterKeyEventLockedInterruptable(const sp<Connection>& connection, DispatchEntry* dispatchEntry, KeyEntry& keyEntry, bool handled) { if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) { @@ -5907,11 +5886,12 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con // then cancel the associated fallback key, if any. if (fallbackKeyCode != -1) { // Dispatch the unhandled key to the policy with the cancel flag. -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Unhandled key event: Asking policy to cancel fallback action. " - "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", - keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags); -#endif + if (DEBUG_OUTBOUND_EVENT_DETAILS) { + ALOGD("Unhandled key event: Asking policy to cancel fallback action. " + "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", + keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, + keyEntry.policyFlags); + } KeyEvent event = createKeyEvent(keyEntry); event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED); @@ -5939,21 +5919,21 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con // Then ask the policy what to do with it. bool initialDown = keyEntry.action == AKEY_EVENT_ACTION_DOWN && keyEntry.repeatCount == 0; if (fallbackKeyCode == -1 && !initialDown) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Unhandled key event: Skipping unhandled key event processing " - "since this is not an initial down. " - "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", - originalKeyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags); -#endif + if (DEBUG_OUTBOUND_EVENT_DETAILS) { + ALOGD("Unhandled key event: Skipping unhandled key event processing " + "since this is not an initial down. " + "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", + originalKeyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags); + } return false; } // Dispatch the unhandled key to the policy. -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Unhandled key event: Asking policy to perform fallback action. " - "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", - keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags); -#endif + if (DEBUG_OUTBOUND_EVENT_DETAILS) { + ALOGD("Unhandled key event: Asking policy to perform fallback action. " + "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", + keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags); + } KeyEvent event = createKeyEvent(keyEntry); mLock.unlock(); @@ -5987,19 +5967,19 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con // longer dispatch a fallback key to the application. if (fallbackKeyCode != AKEYCODE_UNKNOWN && (!fallback || fallbackKeyCode != event.getKeyCode())) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - if (fallback) { - ALOGD("Unhandled key event: Policy requested to send key %d" - "as a fallback for %d, but on the DOWN it had requested " - "to send %d instead. Fallback canceled.", - event.getKeyCode(), originalKeyCode, fallbackKeyCode); - } else { - ALOGD("Unhandled key event: Policy did not request fallback for %d, " - "but on the DOWN it had requested to send %d. " - "Fallback canceled.", - originalKeyCode, fallbackKeyCode); + if (DEBUG_OUTBOUND_EVENT_DETAILS) { + if (fallback) { + ALOGD("Unhandled key event: Policy requested to send key %d" + "as a fallback for %d, but on the DOWN it had requested " + "to send %d instead. Fallback canceled.", + event.getKeyCode(), originalKeyCode, fallbackKeyCode); + } else { + ALOGD("Unhandled key event: Policy did not request fallback for %d, " + "but on the DOWN it had requested to send %d. " + "Fallback canceled.", + originalKeyCode, fallbackKeyCode); + } } -#endif CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS, "canceling fallback, policy no longer desires it"); @@ -6013,18 +5993,18 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con } } -#if DEBUG_OUTBOUND_EVENT_DETAILS - { - std::string msg; - const KeyedVector<int32_t, int32_t>& fallbackKeys = - connection->inputState.getFallbackKeys(); - for (size_t i = 0; i < fallbackKeys.size(); i++) { - msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i), fallbackKeys.valueAt(i)); + if (DEBUG_OUTBOUND_EVENT_DETAILS) { + { + std::string msg; + const KeyedVector<int32_t, int32_t>& fallbackKeys = + connection->inputState.getFallbackKeys(); + for (size_t i = 0; i < fallbackKeys.size(); i++) { + msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i), fallbackKeys.valueAt(i)); + } + ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.", + fallbackKeys.size(), msg.c_str()); } - ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.", - fallbackKeys.size(), msg.c_str()); } -#endif if (fallback) { // Restart the dispatch cycle using the fallback key. @@ -6040,16 +6020,16 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con keyEntry.downTime = event.getDownTime(); keyEntry.syntheticRepeat = false; -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Unhandled key event: Dispatching fallback key. " - "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x", - originalKeyCode, fallbackKeyCode, keyEntry.metaState); -#endif + if (DEBUG_OUTBOUND_EVENT_DETAILS) { + ALOGD("Unhandled key event: Dispatching fallback key. " + "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x", + originalKeyCode, fallbackKeyCode, keyEntry.metaState); + } return true; // restart the event } else { -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Unhandled key event: No fallback key."); -#endif + if (DEBUG_OUTBOUND_EVENT_DETAILS) { + ALOGD("Unhandled key event: No fallback key."); + } // Report the key as unhandled, since there is no fallback key. mReporter->reportUnhandledKey(keyEntry.id); @@ -6058,21 +6038,12 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con return false; } -bool InputDispatcher::afterMotionEventLockedInterruptible(const sp<Connection>& connection, +bool InputDispatcher::afterMotionEventLockedInterruptable(const sp<Connection>& connection, DispatchEntry* dispatchEntry, MotionEntry& motionEntry, bool handled) { return false; } -void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) { - mLock.unlock(); - - mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType, - commandEntry->displayId); - - mLock.lock(); -} - void InputDispatcher::traceInboundQueueLengthLocked() { if (ATRACE_ENABLED()) { ATRACE_INT("iq", mInboundQueue.size()); @@ -6184,7 +6155,7 @@ void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& ch disablePointerCaptureForcedLocked(); if (mFocusedDisplayId == changes.displayId) { - notifyFocusChangedLocked(changes.oldFocus, changes.newFocus); + sendFocusChangedCommandLocked(changes.oldFocus, changes.newFocus); } } @@ -6216,22 +6187,14 @@ void InputDispatcher::disablePointerCaptureForcedLocked() { mInboundQueue.push_front(std::move(entry)); } -void InputDispatcher::setPointerCaptureLocked(bool enabled) { - mCurrentPointerCaptureRequest.enable = enabled; +void InputDispatcher::setPointerCaptureLocked(bool enable) { + mCurrentPointerCaptureRequest.enable = enable; mCurrentPointerCaptureRequest.seq++; - std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( - &InputDispatcher::doSetPointerCaptureLockedInterruptible); - commandEntry->pointerCaptureRequest = mCurrentPointerCaptureRequest; - postCommandLocked(std::move(commandEntry)); -} - -void InputDispatcher::doSetPointerCaptureLockedInterruptible( - android::inputdispatcher::CommandEntry* commandEntry) { - mLock.unlock(); - - mPolicy->setPointerCapture(commandEntry->pointerCaptureRequest); - - mLock.lock(); + auto command = [this, request = mCurrentPointerCaptureRequest]() REQUIRES(mLock) { + scoped_unlock unlock(mLock); + mPolicy->setPointerCapture(request); + }; + postCommandLocked(std::move(command)); } void InputDispatcher::displayRemoved(int32_t displayId) { @@ -6249,16 +6212,44 @@ void InputDispatcher::displayRemoved(int32_t displayId) { mLooper->wake(); } -void InputDispatcher::onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos) { +void InputDispatcher::onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos, + const std::vector<DisplayInfo>& displayInfos) { // The listener sends the windows as a flattened array. Separate the windows by display for // more convenient parsing. std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay; - for (const auto& info : windowInfos) { handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>()); handlesPerDisplay[info.displayId].push_back(new WindowInfoHandle(info)); } - setInputWindows(handlesPerDisplay); + + { // acquire lock + std::scoped_lock _l(mLock); + mDisplayInfos.clear(); + for (const auto& displayInfo : displayInfos) { + mDisplayInfos.emplace(displayInfo.displayId, displayInfo); + } + + for (const auto& [displayId, handles] : handlesPerDisplay) { + setInputWindowsLocked(handles, displayId); + } + } + // Wake up poll loop since it may need to make new input dispatching choices. + mLooper->wake(); +} + +bool InputDispatcher::shouldDropInput( + const EventEntry& entry, const sp<android::gui::WindowInfoHandle>& windowHandle) const { + if (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT) || + (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED) && + isWindowObscuredLocked(windowHandle))) { + ALOGW("Dropping %s event targeting %s as requested by input feature %s on display " + "%" PRId32 ".", + ftl::enum_string(entry.type).c_str(), windowHandle->getName().c_str(), + windowHandle->getInfo()->inputFeatures.string().c_str(), + windowHandle->getInfo()->displayId); + return true; + } + return false; } } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index db559dfed6..4f6d0d2759 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -143,7 +143,8 @@ public: void displayRemoved(int32_t displayId) override; - void onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos) override; + void onWindowInfosChanged(const std::vector<gui::WindowInfo>&, + const std::vector<gui::DisplayInfo>&) override; private: enum class DropReason { @@ -171,7 +172,26 @@ private: std::shared_ptr<EventEntry> mPendingEvent GUARDED_BY(mLock); std::deque<std::shared_ptr<EventEntry>> mInboundQueue GUARDED_BY(mLock); std::deque<std::shared_ptr<EventEntry>> mRecentQueue GUARDED_BY(mLock); - std::deque<std::unique_ptr<CommandEntry>> mCommandQueue GUARDED_BY(mLock); + + // A command entry captures state and behavior for an action to be performed in the + // dispatch loop after the initial processing has taken place. It is essentially + // a kind of continuation used to postpone sensitive policy interactions to a point + // in the dispatch loop where it is safe to release the lock (generally after finishing + // the critical parts of the dispatch cycle). + // + // The special thing about commands is that they can voluntarily release and reacquire + // the dispatcher lock at will. Initially when the command starts running, the + // dispatcher lock is held. However, if the command needs to call into the policy to + // do some work, it can release the lock, do the work, then reacquire the lock again + // before returning. + // + // This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch + // never calls into the policy while holding its lock. + // + // Commands are called with the lock held, but they can release and re-acquire the lock from + // within. + using Command = std::function<void()>; + std::deque<Command> mCommandQueue GUARDED_BY(mLock); DropReason mLastDropReason GUARDED_BY(mLock); @@ -215,7 +235,6 @@ private: sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool addOutsideTargets = false, - bool addPortalWindows = false, bool ignoreDragWindow = false) REQUIRES(mLock); @@ -296,8 +315,8 @@ private: // Deferred command processing. bool haveCommandsLocked() const REQUIRES(mLock); - bool runCommandsLockedInterruptible() REQUIRES(mLock); - void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock); + bool runCommandsLockedInterruptable() REQUIRES(mLock); + void postCommandLocked(Command&& command) REQUIRES(mLock); nsecs_t processAnrsLocked() REQUIRES(mLock); std::chrono::nanoseconds getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock); @@ -319,8 +338,10 @@ private: float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock); android::os::BlockUntrustedTouchesMode mBlockUntrustedTouchesMode GUARDED_BY(mLock); - std::unordered_map<int32_t, std::vector<sp<android::gui::WindowInfoHandle>>> + std::unordered_map<int32_t /*displayId*/, std::vector<sp<android::gui::WindowInfoHandle>>> mWindowHandlesByDisplay GUARDED_BY(mLock); + std::unordered_map<int32_t /*displayId*/, android::gui::DisplayInfo> mDisplayInfos + GUARDED_BY(mLock); void setInputWindowsLocked( const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles, int32_t displayId) REQUIRES(mLock); @@ -343,6 +364,12 @@ private: bool hasResponsiveConnectionLocked(android::gui::WindowInfoHandle& windowHandle) const REQUIRES(mLock); + // Gets all the input targets (with their respective input channels) from the window handles + // passed as argument. + std::vector<InputTarget> getInputTargetsFromWindowHandlesLocked( + const std::vector<sp<android::gui::WindowInfoHandle>>& windowHandles) const + REQUIRES(mLock); + /* * Validate and update InputWindowHandles for a given display. */ @@ -405,9 +432,12 @@ private: void dispatchPointerCaptureChangedLocked( nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry, DropReason& dropReason) REQUIRES(mLock); + void dispatchTouchModeChangeLocked(nsecs_t currentTime, + const std::shared_ptr<TouchModeEntry>& entry) + REQUIRES(mLock); void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<EventEntry> entry, const std::vector<InputTarget>& inputTargets) REQUIRES(mLock); - void dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry, + void dispatchSensorLocked(nsecs_t currentTime, const std::shared_ptr<SensorEntry>& entry, DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) REQUIRES(mLock); void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry); @@ -454,24 +484,11 @@ private: */ void processConnectionResponsiveLocked(const Connection& connection) REQUIRES(mLock); - /** - * Post `doNotifyMonitorUnresponsiveLockedInterruptible` command. - */ void sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) REQUIRES(mLock); - /** - * Post `doNotifyWindowUnresponsiveLockedInterruptible` command. - */ - void sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken, std::string reason) + void sendWindowUnresponsiveCommandLocked(const sp<IBinder>& connectionToken, std::string reason) REQUIRES(mLock); - /** - * Post `doNotifyMonitorResponsiveLockedInterruptible` command. - */ void sendMonitorResponsiveCommandLocked(int32_t pid) REQUIRES(mLock); - /** - * Post `doNotifyWindowResponsiveLockedInterruptible` command. - */ - void sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) REQUIRES(mLock); - + void sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock); // Optimization: AnrTracker is used to quickly find which connection is due for a timeout next. // AnrTracker must be kept in-sync with all responsive connection.waitQueues. @@ -499,9 +516,7 @@ private: android::os::InputEventInjectionResult findTouchedWindowTargetsLocked( nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) REQUIRES(mLock); - std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked( - int32_t displayId, - const std::vector<sp<android::gui::WindowInfoHandle>>& portalWindows) const + std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(int32_t displayId) const REQUIRES(mLock); std::vector<TouchedMonitor> selectResponsiveMonitorsLocked( const std::vector<TouchedMonitor>& gestureMonitors) const REQUIRES(mLock); @@ -542,6 +557,10 @@ private: std::string getApplicationWindowLabel(const InputApplicationHandle* applicationHandle, const sp<android::gui::WindowInfoHandle>& windowHandle); + bool shouldDropInput(const EventEntry& entry, + const sp<android::gui::WindowInfoHandle>& windowHandle) const + REQUIRES(mLock); + // Manage the dispatch cycle for a single connection. // These methods are deliberately not Interruptible because doing all of the work // with the mutex held makes it easier to ensure that connection invariants are maintained. @@ -607,53 +626,30 @@ private: REQUIRES(mLock); // Interesting events that we might like to log or tell the framework about. - void onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp<Connection>& connection, - uint32_t seq, bool handled, nsecs_t consumeTime) - REQUIRES(mLock); - void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection) + void doDispatchCycleFinishedCommand(nsecs_t finishTime, const sp<Connection>& connection, + uint32_t seq, bool handled, nsecs_t consumeTime) REQUIRES(mLock); + void doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken, + KeyEntry& entry) REQUIRES(mLock); void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock); - void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus) + void sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) REQUIRES(mLock); - void notifyDropWindowLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock); + void sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock); + void sendUntrustedTouchCommandLocked(const std::string& obscuringPackage) REQUIRES(mLock); void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock); void onAnrLocked(std::shared_ptr<InputApplicationHandle> application) REQUIRES(mLock); - void onUntrustedTouchLocked(const std::string& obscuringPackage) REQUIRES(mLock); void updateLastAnrStateLocked(const sp<android::gui::WindowInfoHandle>& window, const std::string& reason) REQUIRES(mLock); void updateLastAnrStateLocked(const InputApplicationHandle& application, const std::string& reason) REQUIRES(mLock); void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason) REQUIRES(mLock); - - // Outbound policy interactions. - void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) - REQUIRES(mLock); - void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doNotifyDropWindowLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - - // ANR-related callbacks - start - void doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - // ANR-related callbacks - end - void doNotifySensorLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry) - REQUIRES(mLock); - void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doSetPointerCaptureLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - bool afterKeyEventLockedInterruptible(const sp<Connection>& connection, + bool afterKeyEventLockedInterruptable(const sp<Connection>& connection, DispatchEntry* dispatchEntry, KeyEntry& keyEntry, bool handled) REQUIRES(mLock); - bool afterMotionEventLockedInterruptible(const sp<Connection>& connection, + bool afterMotionEventLockedInterruptable(const sp<Connection>& connection, DispatchEntry* dispatchEntry, MotionEntry& motionEntry, bool handled) REQUIRES(mLock); - void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); // Find touched state and touched window by token. std::pair<TouchState*, TouchedWindow*> findTouchStateAndWindowLocked(const sp<IBinder>& token) diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp index d634dcda1d..52f189c5c5 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.cpp +++ b/services/inputflinger/dispatcher/LatencyTracker.cpp @@ -50,13 +50,12 @@ static bool isMatureEvent(nsecs_t eventTime, nsecs_t now) { * key-value pair. Equivalent to the imaginary std api std::multimap::erase(key, value). */ template <typename K, typename V> -static void eraseByKeyAndValue(std::multimap<K, V>& map, K key, V value) { - auto iterpair = map.equal_range(key); - - for (auto it = iterpair.first; it != iterpair.second; ++it) { +static void eraseByValue(std::multimap<K, V>& map, const V& value) { + for (auto it = map.begin(); it != map.end();) { if (it->second == value) { - map.erase(it); - break; + it = map.erase(it); + } else { + it++; } } } @@ -76,9 +75,7 @@ void LatencyTracker::trackListener(int32_t inputEventId, bool isDown, nsecs_t ev // confuse us by reporting the rest of the timeline for one of them. This should happen // rarely, so we won't lose much data mTimelines.erase(it); - // In case we have another input event with a different id and at the same eventTime, - // only erase this specific inputEventId. - eraseByKeyAndValue(mEventTimes, eventTime, inputEventId); + eraseByValue(mEventTimes, inputEventId); return; } mTimelines.emplace(inputEventId, InputEventTimeline(isDown, eventTime, readTime)); @@ -90,7 +87,8 @@ void LatencyTracker::trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& nsecs_t finishTime) { const auto it = mTimelines.find(inputEventId); if (it == mTimelines.end()) { - // It's possible that an app sends a bad (or late)'Finish' signal, since it's free to do + // This could happen if we erased this event when duplicate events were detected. It's + // also possible that an app sent a bad (or late) 'Finish' signal, since it's free to do // anything in its process. Just drop the report and move on. return; } @@ -120,7 +118,8 @@ void LatencyTracker::trackGraphicsLatency( std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) { const auto it = mTimelines.find(inputEventId); if (it == mTimelines.end()) { - // It's possible that an app sends a bad (or late) 'Timeline' signal, since it's free to do + // This could happen if we erased this event when duplicate events were detected. It's + // also possible that an app sent a bad (or late) 'Timeline' signal, since it's free to do // anything in its process. Just drop the report and move on. return; } @@ -166,14 +165,6 @@ void LatencyTracker::reportAndPruneMatureRecords(nsecs_t newEventTime) { } } -void LatencyTracker::reportNow() { - for (const auto& [inputEventId, timeline] : mTimelines) { - mTimelineProcessor->processTimeline(timeline); - } - mTimelines.clear(); - mEventTimes.clear(); -} - std::string LatencyTracker::dump(const char* prefix) { return StringPrintf("%sLatencyTracker:\n", prefix) + StringPrintf("%s mTimelines.size() = %zu\n", prefix, mTimelines.size()) + diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h index 289b8ed6c4..4b0c618590 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.h +++ b/services/inputflinger/dispatcher/LatencyTracker.h @@ -43,6 +43,12 @@ public: LatencyTracker(InputEventTimelineProcessor* processor); /** * Start keeping track of an event identified by inputEventId. This must be called first. + * If duplicate events are encountered (events that have the same eventId), none of them will be + * tracked. This is because there is not enough information to correctly track them. The api's + * 'trackFinishedEvent' and 'trackGraphicsLatency' only contain the inputEventId, and not the + * eventTime. Even if eventTime was provided, there would still be a possibility of having + * duplicate events that happen to have the same eventTime and inputEventId. Therefore, we + * must drop all duplicate data. */ void trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, nsecs_t readTime); void trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken, @@ -50,14 +56,6 @@ public: void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline); - /** - * Report all collected events immediately, even if some of them are currently incomplete - * and may receive 'trackFinishedEvent' or 'trackGraphicsLatency' calls in the future. - * This is useful for tests. Otherwise, tests would have to inject additional "future" events, - * which is not convenient. - */ - void reportNow(); - std::string dump(const char* prefix); private: diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index 20b6eadf5b..4a50a683e9 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -37,7 +37,6 @@ void TouchState::reset() { source = 0; displayId = ADISPLAY_ID_NONE; windows.clear(); - portalWindows.clear(); gestureMonitors.clear(); } @@ -48,7 +47,6 @@ void TouchState::copyFrom(const TouchState& other) { source = other.source; displayId = other.displayId; windows = other.windows; - portalWindows = other.portalWindows; gestureMonitors = other.gestureMonitors; } @@ -77,16 +75,6 @@ void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, int windows.push_back(touchedWindow); } -void TouchState::addPortalWindow(const sp<android::gui::WindowInfoHandle>& windowHandle) { - size_t numWindows = portalWindows.size(); - for (size_t i = 0; i < numWindows; i++) { - if (portalWindows[i] == windowHandle) { - return; - } - } - portalWindows.push_back(windowHandle); -} - void TouchState::addGestureMonitors(const std::vector<TouchedMonitor>& newMonitors) { const size_t newSize = gestureMonitors.size() + newMonitors.size(); gestureMonitors.reserve(newSize); @@ -119,7 +107,6 @@ void TouchState::filterNonAsIsTouchWindows() { void TouchState::filterNonMonitors() { windows.clear(); - portalWindows.clear(); } sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const { @@ -147,4 +134,14 @@ bool TouchState::isSlippery() const { return haveSlipperyForegroundWindow; } +sp<WindowInfoHandle> TouchState::getWallpaperWindow() const { + for (size_t i = 0; i < windows.size(); i++) { + const TouchedWindow& window = windows[i]; + if (window.windowHandle->getInfo()->type == WindowInfo::Type::WALLPAPER) { + return window.windowHandle; + } + } + return nullptr; +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index a4e52b0d83..7dcf55d813 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -36,11 +36,6 @@ struct TouchState { int32_t displayId; // id to the display that currently has a touch, others are rejected std::vector<TouchedWindow> windows; - // This collects the portal windows that the touch has gone through. Each portal window - // targets a display (embedded display for most cases). With this info, we can add the - // monitoring channels of the displays touched. - std::vector<sp<android::gui::WindowInfoHandle>> portalWindows; - std::vector<TouchedMonitor> gestureMonitors; TouchState(); @@ -56,6 +51,7 @@ struct TouchState { void filterNonMonitors(); sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const; bool isSlippery() const; + sp<android::gui::WindowInfoHandle> getWallpaperWindow() const; }; } // namespace inputdispatcher diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index a7dccf0d19..b74f30466d 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -202,6 +202,7 @@ public: * InputDispatcher is the source of truth of Pointer Capture. */ virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) = 0; + /* Flush input device motion sensor. * * Returns true on success. diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index 68d5e7c300..d10f8b6605 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -40,6 +40,7 @@ #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <cutils/properties.h> +#include <ftl/enum.h> #include <input/KeyCharacterMap.h> #include <input/KeyLayoutMap.h> #include <input/VirtualKeyMap.h> @@ -263,7 +264,7 @@ static std::vector<std::filesystem::path> allFilesInPath(const std::filesystem:: */ static std::vector<std::filesystem::path> findSysfsNodes(const std::filesystem::path& sysfsRoot, SysfsClass clazz) { - std::string nodeStr = NamedEnum::string(clazz); + std::string nodeStr = ftl::enum_string(clazz); std::for_each(nodeStr.begin(), nodeStr.end(), [](char& c) { c = std::tolower(static_cast<unsigned char>(c)); }); std::vector<std::filesystem::path> nodes; @@ -1209,6 +1210,15 @@ bool EventHub::hasScanCode(int32_t deviceId, int32_t scanCode) const { return false; } +bool EventHub::hasKeyCode(int32_t deviceId, int32_t keyCode) const { + std::scoped_lock _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device != nullptr) { + return device->hasKeycodeLocked(keyCode); + } + return false; +} + bool EventHub::hasLed(int32_t deviceId, int32_t led) const { std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 1e9ec54d5d..36344b5c3a 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -559,7 +559,13 @@ int32_t InputDevice::getMetaState() { } void InputDevice::updateMetaState(int32_t keyCode) { - for_each_mapper([keyCode](InputMapper& mapper) { mapper.updateMetaState(keyCode); }); + first_in_mappers<bool>([keyCode](InputMapper& mapper) { + if (sourcesMatchMask(mapper.getSources(), AINPUT_SOURCE_KEYBOARD) && + mapper.updateMetaState(keyCode)) { + return std::make_optional(true); + } + return std::optional<bool>(); + }); } void InputDevice::bumpGeneration() { diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 5120860503..86d4c26469 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -561,6 +561,7 @@ void InputReader::toggleCapsLockState(int32_t deviceId) { } if (device->isIgnored()) { + ALOGW("Ignoring toggleCapsLock for ignored deviceId %" PRId32 ".", deviceId); return; } diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp index 9c8a29a059..a6934960c9 100644 --- a/services/inputflinger/reader/controller/PeripheralController.cpp +++ b/services/inputflinger/reader/controller/PeripheralController.cpp @@ -17,9 +17,9 @@ #include <locale> #include <regex> -#include "../Macros.h" +#include <ftl/enum.h> -#include <ftl/NamedEnum.h> +#include "../Macros.h" #include "PeripheralController.h" // Log detailed debug messages about input device lights. @@ -286,7 +286,7 @@ void PeripheralController::dump(std::string& dump) { for (const auto& [lightId, light] : mLights) { dump += StringPrintf(INDENT4 "Id: %d", lightId); dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str()); - dump += StringPrintf(INDENT4 "Type: %s", NamedEnum::string(light->type).c_str()); + dump += StringPrintf(INDENT4 "Type: %s", ftl::enum_string(light->type).c_str()); light->dump(dump); } } @@ -487,7 +487,7 @@ bool PeripheralController::setLightColor(int32_t lightId, int32_t color) { auto& light = it->second; if (DEBUG_LIGHT_DETAILS) { ALOGD("setLightColor lightId %d type %s color 0x%x", lightId, - NamedEnum::string(light->type).c_str(), color); + ftl::enum_string(light->type).c_str(), color); } return light->setLightColor(color); } @@ -501,7 +501,7 @@ std::optional<int32_t> PeripheralController::getLightColor(int32_t lightId) { std::optional<int32_t> color = light->getLightColor(); if (DEBUG_LIGHT_DETAILS) { ALOGD("getLightColor lightId %d type %s color 0x%x", lightId, - NamedEnum::string(light->type).c_str(), color.value_or(0)); + ftl::enum_string(light->type).c_str(), color.value_or(0)); } return color; } diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 3c3f88e36e..1f96294e96 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -146,6 +146,8 @@ enum class InputDeviceClass : uint32_t { enum class SysfsClass : uint32_t { POWER_SUPPLY = 0, LEDS = 1, + + ftl_last = LEDS }; enum class LightColor : uint32_t { @@ -312,6 +314,7 @@ public: uint8_t* outFlags) const = 0; virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const = 0; + virtual bool hasKeyCode(int32_t deviceId, int32_t keyCode) const = 0; /* LED related functions expect Android LED constants, not scan codes or HID usages */ virtual bool hasLed(int32_t deviceId, int32_t led) const = 0; @@ -489,6 +492,7 @@ public: std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override final; bool hasScanCode(int32_t deviceId, int32_t scanCode) const override final; + bool hasKeyCode(int32_t deviceId, int32_t keyCode) const override final; bool hasLed(int32_t deviceId, int32_t led) const override final; void setLedState(int32_t deviceId, int32_t led, bool on) override final; diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index f32472d37b..518aaa0490 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -323,6 +323,7 @@ public: inline bool hasScanCode(int32_t scanCode) const { return mEventHub->hasScanCode(mId, scanCode); } + inline bool hasKeyCode(int32_t keyCode) const { return mEventHub->hasKeyCode(mId, keyCode); } inline bool hasLed(int32_t led) const { return mEventHub->hasLed(mId, led); } inline void setLedState(int32_t led, bool on) { return mEventHub->setLedState(mId, led, on); } inline void getVirtualKeyDefinitions(std::vector<VirtualKeyDefinition>& outVirtualKeys) const { diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp index df1acd439f..b9aef54ead 100644 --- a/services/inputflinger/reader/mapper/InputMapper.cpp +++ b/services/inputflinger/reader/mapper/InputMapper.cpp @@ -84,7 +84,9 @@ int32_t InputMapper::getMetaState() { return 0; } -void InputMapper::updateMetaState(int32_t keyCode) {} +bool InputMapper::updateMetaState(int32_t keyCode) { + return false; +} void InputMapper::updateExternalStylusState(const StylusState& state) {} diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h index 15cff1cfca..d9adc0fbc6 100644 --- a/services/inputflinger/reader/mapper/InputMapper.h +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -83,7 +83,11 @@ public: virtual std::optional<int32_t> getLightPlayerId(int32_t lightId) { return std::nullopt; } virtual int32_t getMetaState(); - virtual void updateMetaState(int32_t keyCode); + /** + * Process the meta key and update the global meta state when changed. + * Return true if the meta key could be handled by the InputMapper. + */ + virtual bool updateMetaState(int32_t keyCode); virtual void updateExternalStylusState(const StylusState& state); diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 104d087387..52af38dcaf 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -384,8 +384,13 @@ int32_t KeyboardInputMapper::getMetaState() { return mMetaState; } -void KeyboardInputMapper::updateMetaState(int32_t keyCode) { +bool KeyboardInputMapper::updateMetaState(int32_t keyCode) { + if (!android::isMetaKey(keyCode) || !getDeviceContext().hasKeyCode(keyCode)) { + return false; + } + updateMetaStateIfNeeded(keyCode, false); + return true; } bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) { diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h index ca41712f48..fc92320773 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -40,7 +40,7 @@ public: const int32_t* keyCodes, uint8_t* outFlags) override; virtual int32_t getMetaState() override; - virtual void updateMetaState(int32_t keyCode) override; + virtual bool updateMetaState(int32_t keyCode) override; virtual std::optional<int32_t> getAssociatedDisplayId() override; virtual void updateLedState(bool reset); diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp index a507632d0e..a1bd548403 100644 --- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp @@ -16,8 +16,9 @@ #include <locale> -#include "../Macros.h" +#include <ftl/enum.h> +#include "../Macros.h" #include "SensorInputMapper.h" // Log detailed debug messages about each sensor event notification to the dispatcher. @@ -93,7 +94,7 @@ void SensorInputMapper::dump(std::string& dump) { dump += StringPrintf(INDENT3 " mHasHardwareTimestamp %d\n", mHasHardwareTimestamp); dump += INDENT3 "Sensors:\n"; for (const auto& [sensorType, sensor] : mSensors) { - dump += StringPrintf(INDENT4 "%s\n", NamedEnum::string(sensorType).c_str()); + dump += StringPrintf(INDENT4 "%s\n", ftl::enum_string(sensorType).c_str()); dump += StringPrintf(INDENT5 "enabled: %d\n", sensor.enabled); dump += StringPrintf(INDENT5 "samplingPeriod: %lld\n", sensor.samplingPeriod.count()); dump += StringPrintf(INDENT5 "maxBatchReportLatency: %lld\n", @@ -208,10 +209,10 @@ SensorInputMapper::Sensor SensorInputMapper::createSensor(InputDeviceSensorType axis.max /* maxRange */, axis.scale /* resolution */, 0.0f /* power */, 0 /* minDelay */, 0 /* fifoReservedEventCount */, 0 /* fifoMaxEventCount */, - NamedEnum::string(sensorType), 0 /* maxDelay */, 0 /* flags */, + ftl::enum_string(sensorType), 0 /* maxDelay */, 0 /* flags */, getDeviceId()); - std::string prefix = "sensor." + NamedEnum::string(sensorType); + std::string prefix = "sensor." + ftl::enum_string(sensorType); transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower); int32_t reportingMode = 0; @@ -335,7 +336,7 @@ bool SensorInputMapper::enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds maxBatchReportLatency) { if (DEBUG_SENSOR_EVENT_DETAILS) { ALOGD("Enable Sensor %s samplingPeriod %lld maxBatchReportLatency %lld", - NamedEnum::string(sensorType).c_str(), samplingPeriod.count(), + ftl::enum_string(sensorType).c_str(), samplingPeriod.count(), maxBatchReportLatency.count()); } @@ -359,7 +360,7 @@ bool SensorInputMapper::enableSensor(InputDeviceSensorType sensorType, void SensorInputMapper::disableSensor(InputDeviceSensorType sensorType) { if (DEBUG_SENSOR_EVENT_DETAILS) { - ALOGD("Disable Sensor %s", NamedEnum::string(sensorType).c_str()); + ALOGD("Disable Sensor %s", ftl::enum_string(sensorType).c_str()); } if (!setSensorEnabled(sensorType, false /* enabled */)) { @@ -393,13 +394,12 @@ void SensorInputMapper::sync(nsecs_t when, bool force) { nsecs_t timestamp = mHasHardwareTimestamp ? mHardwareTimestamp : when; if (DEBUG_SENSOR_EVENT_DETAILS) { ALOGD("Sensor %s timestamp %" PRIu64 " values [%f %f %f]", - NamedEnum::string(sensorType).c_str(), timestamp, values[0], values[1], - values[2]); + ftl::enum_string(sensorType).c_str(), timestamp, values[0], values[1], values[2]); } if (sensor.lastSampleTimeNs.has_value() && timestamp - sensor.lastSampleTimeNs.value() < sensor.samplingPeriod.count()) { if (DEBUG_SENSOR_EVENT_DETAILS) { - ALOGD("Sensor %s Skip a sample.", NamedEnum::string(sensorType).c_str()); + ALOGD("Sensor %s Skip a sample.", ftl::enum_string(sensorType).c_str()); } } else { // Convert to Android unit diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index ac5f6b652b..419b0d0eed 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -18,9 +18,10 @@ #include "../Macros.h" // clang-format on -#include <ftl/NamedEnum.h> #include "TouchInputMapper.h" +#include <ftl/enum.h> + #include "CursorButtonAccumulator.h" #include "CursorScrollAccumulator.h" #include "TouchButtonAccumulator.h" @@ -259,7 +260,7 @@ void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { void TouchInputMapper::dump(std::string& dump) { dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", - NamedEnum::string(mDeviceMode).c_str()); + ftl::enum_string(mDeviceMode).c_str()); dumpParameters(dump); dumpVirtualKeys(dump); dumpRawPointerAxes(dump); @@ -515,9 +516,9 @@ void TouchInputMapper::configureParameters() { void TouchInputMapper::dumpParameters(std::string& dump) { dump += INDENT3 "Parameters:\n"; - dump += INDENT4 "GestureMode: " + NamedEnum::string(mParameters.gestureMode) + "\n"; + dump += INDENT4 "GestureMode: " + ftl::enum_string(mParameters.gestureMode) + "\n"; - dump += INDENT4 "DeviceType: " + NamedEnum::string(mParameters.deviceType) + "\n"; + dump += INDENT4 "DeviceType: " + ftl::enum_string(mParameters.deviceType) + "\n"; dump += StringPrintf(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, " "displayId='%s'\n", @@ -525,7 +526,7 @@ void TouchInputMapper::dumpParameters(std::string& dump) { toString(mParameters.associatedDisplayIsExternal), mParameters.uniqueDisplayId.c_str()); dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); - dump += INDENT4 "Orientation: " + NamedEnum::string(mParameters.orientation) + "\n"; + dump += INDENT4 "Orientation: " + ftl::enum_string(mParameters.orientation) + "\n"; } void TouchInputMapper::configureRawPointerAxes() { diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index e104220e47..a56468f445 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -185,6 +185,8 @@ protected: UNSCALED, // unscaled mapping (touchpad) NAVIGATION, // unscaled mapping with assist gesture (touch navigation) POINTER, // pointer mapping (pointer) + + ftl_last = POINTER }; DeviceMode mDeviceMode; @@ -198,6 +200,8 @@ protected: TOUCH_PAD, TOUCH_NAVIGATION, POINTER, + + ftl_last = POINTER }; DeviceType deviceType; @@ -210,6 +214,8 @@ protected: ORIENTATION_90 = DISPLAY_ORIENTATION_90, ORIENTATION_180 = DISPLAY_ORIENTATION_180, ORIENTATION_270 = DISPLAY_ORIENTATION_270, + + ftl_last = ORIENTATION_270 }; Orientation orientation; @@ -219,6 +225,8 @@ protected: enum class GestureMode { SINGLE_TOUCH, MULTI_TOUCH, + + ftl_last = MULTI_TOUCH }; GestureMode gestureMode; @@ -818,4 +826,4 @@ private: } // namespace android -#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
\ No newline at end of file +#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 912e840553..903f337eb9 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -16,6 +16,7 @@ #include "../dispatcher/InputDispatcher.h" +#include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android-base/thread_annotations.h> #include <binder/Binder.h> @@ -73,6 +74,12 @@ static KeyEvent getTestKeyEvent() { return event; } +static void assertMotionAction(int32_t expectedAction, int32_t receivedAction) { + ASSERT_EQ(expectedAction, receivedAction) + << "expected " << MotionEvent::actionToString(expectedAction) << ", got " + << MotionEvent::actionToString(receivedAction); +} + // --- FakeInputDispatcherPolicy --- class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface { @@ -683,7 +690,11 @@ TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) { // --- InputDispatcherTest SetInputWindowTest --- static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 500ms; -static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s; +// Default input dispatching timeout if there is no focused application or paused window +// from which to determine an appropriate dispatching timeout. +static const std::chrono::duration DISPATCHING_TIMEOUT = std::chrono::milliseconds( + android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS * + android::base::HwTimeoutMultiplier()); class FakeApplicationHandle : public InputApplicationHandle { public: @@ -796,7 +807,8 @@ public: } case AINPUT_EVENT_TYPE_MOTION: { const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event); - EXPECT_EQ(expectedAction, motionEvent.getAction()); + assertMotionAction(expectedAction, motionEvent.getAction()); + if (expectedFlags.has_value()) { EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags()); } @@ -977,6 +989,10 @@ public: mInfo.addTouchableRegion(frame); } + void setType(WindowInfo::Type type) { mInfo.type = type; } + + void setHasWallpaper(bool hasWallpaper) { mInfo.hasWallpaper = hasWallpaper; } + void addFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags |= flags; } void setFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags = flags; } @@ -1480,6 +1496,197 @@ TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) { windowSecond->assertNoEvents(); } +/** + * Two windows: A top window, and a wallpaper behind the window. + * Touch goes to the top window, and then top window disappears. Ensure that wallpaper window + * gets ACTION_CANCEL. + * 1. foregroundWindow <-- has wallpaper (hasWallpaper=true) + * 2. wallpaperWindow <-- is wallpaper (type=InputWindowInfo::Type::WALLPAPER) + */ +TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_WallpaperTouchIsCanceled) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> foregroundWindow = + new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + foregroundWindow->setHasWallpaper(true); + sp<FakeWindowHandle> wallpaperWindow = + new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); + wallpaperWindow->setType(WindowInfo::Type::WALLPAPER); + constexpr int expectedWallpaperFlags = + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}}); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Both foreground window and its wallpaper should receive the touch down + foregroundWindow->consumeMotionDown(); + wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + foregroundWindow->consumeMotionMove(); + wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + // Now the foreground window goes away, but the wallpaper stays + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wallpaperWindow}}}); + foregroundWindow->consumeMotionCancel(); + // Since the "parent" window of the wallpaper is gone, wallpaper should receive cancel, too. + wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); +} + +/** + * A single window that receives touch (on top), and a wallpaper window underneath it. + * The top window gets a multitouch gesture. + * Ensure that wallpaper gets the same gesture. + */ +TEST_F(InputDispatcherTest, WallpaperWindow_ReceivesMultiTouch) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); + window->setHasWallpaper(true); + + sp<FakeWindowHandle> wallpaperWindow = + new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); + wallpaperWindow->setType(WindowInfo::Type::WALLPAPER); + constexpr int expectedWallpaperFlags = + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, wallpaperWindow}}}); + + // Touch down on top window + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 100})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Both top window and its wallpaper should receive the touch down + window->consumeMotionDown(); + wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + // Second finger down on the top window + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(100)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(150) + .y(150)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + window->consumeMotionPointerDown(1 /* pointerIndex */); + wallpaperWindow->consumeMotionPointerDown(1 /* pointerIndex */, ADISPLAY_ID_DEFAULT, + expectedWallpaperFlags); + window->assertNoEvents(); + wallpaperWindow->assertNoEvents(); +} + +/** + * Two windows: a window on the left and window on the right. + * A third window, wallpaper, is behind both windows, and spans both top windows. + * The first touch down goes to the left window. A second pointer touches down on the right window. + * The touch is split, so both left and right windows should receive ACTION_DOWN. + * The wallpaper will get the full event, so it should receive ACTION_DOWN followed by + * ACTION_POINTER_DOWN(1). + */ +TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> leftWindow = + new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); + leftWindow->setFrame(Rect(0, 0, 200, 200)); + leftWindow->setFlags(WindowInfo::Flag::SPLIT_TOUCH | WindowInfo::Flag::NOT_TOUCH_MODAL); + leftWindow->setHasWallpaper(true); + + sp<FakeWindowHandle> rightWindow = + new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); + rightWindow->setFrame(Rect(200, 0, 400, 200)); + rightWindow->setFlags(WindowInfo::Flag::SPLIT_TOUCH | WindowInfo::Flag::NOT_TOUCH_MODAL); + rightWindow->setHasWallpaper(true); + + sp<FakeWindowHandle> wallpaperWindow = + new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); + wallpaperWindow->setFrame(Rect(0, 0, 400, 200)); + wallpaperWindow->setType(WindowInfo::Type::WALLPAPER); + constexpr int expectedWallpaperFlags = + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + + mDispatcher->setInputWindows( + {{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}}); + + // Touch down on left window + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 100})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Both foreground window and its wallpaper should receive the touch down + leftWindow->consumeMotionDown(); + wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + // Second finger down on the right window + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(100)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(300) + .y(100)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + leftWindow->consumeMotionMove(); + // Since the touch is split, right window gets ACTION_DOWN + rightWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); + wallpaperWindow->consumeMotionPointerDown(1 /* pointerIndex */, ADISPLAY_ID_DEFAULT, + expectedWallpaperFlags); + + // Now, leftWindow, which received the first finger, disappears. + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {rightWindow, wallpaperWindow}}}); + leftWindow->consumeMotionCancel(); + // Since a "parent" window of the wallpaper is gone, wallpaper should receive cancel, too. + wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + // The pointer that's still down on the right window moves, and goes to the right window only. + // As far as the dispatcher's concerned though, both pointers are still present. + const MotionEvent secondFingerMoveEvent = + MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(100)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(310) + .y(110)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)); + rightWindow->consumeMotionMove(); + + leftWindow->assertNoEvents(); + rightWindow->assertNoEvents(); + wallpaperWindow->assertNoEvents(); +} + TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> windowLeft = @@ -2296,11 +2503,21 @@ public: expectedDisplayId, expectedFlags); } + void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE, + expectedDisplayId, expectedFlags); + } + void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) { mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId, expectedFlags); } + void consumeMotionCancel(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, + expectedDisplayId, expectedFlags); + } + MotionEvent* consumeMotion() { InputEvent* event = mInputReceiver->consume(); if (!event) { @@ -2320,6 +2537,57 @@ private: std::unique_ptr<FakeInputReceiver> mInputReceiver; }; +/** + * Two entities that receive touch: A window, and a global monitor. + * The touch goes to the window, and then the window disappears. + * The monitor does not get cancel right away. But if more events come in, the touch gets canceled + * for the monitor, as well. + * 1. foregroundWindow + * 2. monitor <-- global monitor (doesn't observe z order, receives all events) + */ +TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_GlobalMonitorTouchIsCanceled) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + + FakeMonitorReceiver monitor = + FakeMonitorReceiver(mDispatcher, "GlobalMonitor", ADISPLAY_ID_DEFAULT, + false /*isGestureMonitor*/); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Both the foreground window and the global monitor should receive the touch down + window->consumeMotionDown(); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + window->consumeMotionMove(); + monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT); + + // Now the foreground window goes away + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}}); + window->consumeMotionCancel(); + monitor.assertNoEvents(); // Global monitor does not get a cancel yet + + // If more events come in, there will be no more foreground window to send them to. This will + // cause a cancel for the monitor, as well. + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {120, 200})) + << "Injection should fail because the window was removed"; + window->assertNoEvents(); + // Global monitor now gets the cancel + monitor.consumeMotionCancel(ADISPLAY_ID_DEFAULT); +} + // Tests for gesture monitors TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); @@ -3562,7 +3830,7 @@ protected: << " event, got " << inputEventTypeToString(event->getType()) << " event"; const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event); - EXPECT_EQ(expectedAction, motionEvent.getAction()); + assertMotionAction(expectedAction, motionEvent.getAction()); for (size_t i = 0; i < points.size(); i++) { float expectedX = points[i].x; @@ -5466,4 +5734,134 @@ TEST_F(InputDispatcherDragTests, DragAndDrop_InvalidWindow) { mSecondWindow->assertNoEvents(); } +class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {}; + +TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); + window->setInputFeatures(WindowInfo::Feature::DROP_INPUT); + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + window->setFocusable(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + setFocusedWindow(window); + window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); + + // With the flag set, window should not get any input + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + window->assertNoEvents(); + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&motionArgs); + window->assertNoEvents(); + + // With the flag cleared, the window should get input + window->setInputFeatures({}); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + + keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + window->consumeKeyUp(ADISPLAY_ID_DEFAULT); + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&motionArgs); + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + window->assertNoEvents(); +} + +TEST_F(InputDispatcherDropInputFeatureTest, ObscuredWindowDropsInput) { + std::shared_ptr<FakeApplicationHandle> obscuringApplication = + std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> obscuringWindow = + new FakeWindowHandle(obscuringApplication, mDispatcher, "obscuringWindow", + ADISPLAY_ID_DEFAULT); + obscuringWindow->setFrame(Rect(0, 0, 50, 50)); + obscuringWindow->setOwnerInfo(111, 111); + obscuringWindow->setFlags(WindowInfo::Flag::NOT_TOUCHABLE); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); + window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED); + window->setOwnerInfo(222, 222); + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + window->setFocusable(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}}); + setFocusedWindow(window); + window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); + + // With the flag set, window should not get any input + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + window->assertNoEvents(); + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&motionArgs); + window->assertNoEvents(); + + // With the flag cleared, the window should get input + window->setInputFeatures({}); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}}); + + keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + window->consumeKeyUp(ADISPLAY_ID_DEFAULT); + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&motionArgs); + window->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED); + window->assertNoEvents(); +} + +TEST_F(InputDispatcherDropInputFeatureTest, UnobscuredWindowGetsInput) { + std::shared_ptr<FakeApplicationHandle> obscuringApplication = + std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> obscuringWindow = + new FakeWindowHandle(obscuringApplication, mDispatcher, "obscuringWindow", + ADISPLAY_ID_DEFAULT); + obscuringWindow->setFrame(Rect(0, 0, 50, 50)); + obscuringWindow->setOwnerInfo(111, 111); + obscuringWindow->setFlags(WindowInfo::Flag::NOT_TOUCHABLE); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); + window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED); + window->setOwnerInfo(222, 222); + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + window->setFocusable(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}}); + setFocusedWindow(window); + window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); + + // With the flag set, window should not get any input + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + window->assertNoEvents(); + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&motionArgs); + window->assertNoEvents(); + + // When the window is no longer obscured because it went on top, it should get input + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, obscuringWindow}}}); + + keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyArgs); + window->consumeKeyUp(ADISPLAY_ID_DEFAULT); + + motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&motionArgs); + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + window->assertNoEvents(); +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index b419d9ab3d..5c430f5621 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -205,7 +205,7 @@ class FakeInputReaderPolicy : public InputReaderPolicyInterface { std::condition_variable mDevicesChangedCondition; InputReaderConfiguration mConfig; - std::unordered_map<int32_t, std::shared_ptr<FakePointerController>> mPointerControllers; + std::shared_ptr<FakePointerController> mPointerController; std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock); bool mInputDevicesChanged GUARDED_BY(mLock){false}; std::vector<DisplayViewport> mViewports; @@ -291,8 +291,8 @@ public: void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); } - void setPointerController(int32_t deviceId, std::shared_ptr<FakePointerController> controller) { - mPointerControllers.insert_or_assign(deviceId, std::move(controller)); + void setPointerController(std::shared_ptr<FakePointerController> controller) { + mPointerController = std::move(controller); } const InputReaderConfiguration* getReaderConfiguration() const { @@ -360,8 +360,9 @@ private: *outConfig = mConfig; } - std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override { - return mPointerControllers[deviceId]; + std::shared_ptr<PointerControllerInterface> obtainPointerController( + int32_t /*deviceId*/) override { + return mPointerController; } void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override { @@ -873,6 +874,24 @@ private: return false; } + bool hasKeyCode(int32_t deviceId, int32_t keyCode) const override { + Device* device = getDevice(deviceId); + if (!device) { + return false; + } + for (size_t i = 0; i < device->keysByScanCode.size(); i++) { + if (keyCode == device->keysByScanCode.valueAt(i).keyCode) { + return true; + } + } + for (size_t j = 0; j < device->keysByUsageCode.size(); j++) { + if (keyCode == device->keysByUsageCode.valueAt(j).keyCode) { + return true; + } + } + return false; + } + bool hasLed(int32_t deviceId, int32_t led) const override { Device* device = getDevice(deviceId); return device && device->leds.indexOfKey(led) >= 0; @@ -2135,8 +2154,12 @@ protected: sp<FakeInputReaderPolicy> mFakePolicy; sp<InputReaderInterface> mReader; + std::shared_ptr<FakePointerController> mFakePointerController; + void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); + mFakePointerController = std::make_shared<FakePointerController>(); + mFakePolicy->setPointerController(mFakePointerController); mTestListener = new TestInputListener(2000ms /*eventHappenedTimeout*/, 30ms /*eventDidNotHappenTimeout*/); @@ -3732,6 +3755,25 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) { mapper2.getMetaState()); } +TEST_F(KeyboardInputMapperTest, Process_toggleCapsLockState) { + mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); + + // Suppose we have two mappers. (DPAD + KEYBOARD) + addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_DPAD, + AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC); + KeyboardInputMapper& mapper = + addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); + // Initialize metastate to AMETA_NUM_LOCK_ON. + ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState()); + mapper.updateMetaState(AKEYCODE_NUM_LOCK); + + mReader->toggleCapsLockState(DEVICE_ID); + ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState()); +} + // --- KeyboardInputMapperTest_ExternalDevice --- class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest { @@ -3828,7 +3870,7 @@ protected: InputMapperTest::SetUp(); mFakePointerController = std::make_shared<FakePointerController>(); - mFakePolicy->setPointerController(mDevice->getId(), mFakePointerController); + mFakePolicy->setPointerController(mFakePointerController); } void testMotionRotation(CursorInputMapper& mapper, int32_t originalX, int32_t originalY, @@ -7793,7 +7835,7 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) { fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1); fakePointerController->setPosition(100, 200); fakePointerController->setButtonState(0); - mFakePolicy->setPointerController(mDevice->getId(), fakePointerController); + mFakePolicy->setPointerController(fakePointerController); mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID); prepareSecondaryDisplay(ViewportType::EXTERNAL); @@ -7946,8 +7988,7 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { // Setup PointerController. std::shared_ptr<FakePointerController> fakePointerController = std::make_shared<FakePointerController>(); - mFakePolicy->setPointerController(mDevice->getId(), fakePointerController); - mFakePolicy->setPointerController(SECOND_DEVICE_ID, fakePointerController); + mFakePolicy->setPointerController(fakePointerController); // Setup policy for associated displays and show touches. const uint8_t hdmi1 = 0; @@ -8769,7 +8810,7 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) { mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0); mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0); mFakePolicy->setPointerCapture(true); - mFakePolicy->setPointerController(mDevice->getId(), fakePointerController); + mFakePolicy->setPointerController(fakePointerController); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); // captured touchpad should be a touchpad source @@ -8919,7 +8960,7 @@ TEST_F(MultiTouchInputMapperTest, Process_UnCapturedTouchpadPointer) { prepareAxes(POSITION | ID | SLOT); mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0); mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0); - mFakePolicy->setPointerController(mDevice->getId(), fakePointerController); + mFakePolicy->setPointerController(fakePointerController); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); // run uncaptured pointer tests - pushes out generic events // FINGER 0 DOWN @@ -8959,7 +9000,7 @@ TEST_F(MultiTouchInputMapperTest, WhenCapturedAndNotCaptured_GetSources) { prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | ID | SLOT); mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0); - mFakePolicy->setPointerController(mDevice->getId(), fakePointerController); + mFakePolicy->setPointerController(fakePointerController); mFakePolicy->setPointerCapture(false); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp index e7e1937235..89c0741dfd 100644 --- a/services/inputflinger/tests/LatencyTracker_test.cpp +++ b/services/inputflinger/tests/LatencyTracker_test.cpp @@ -16,6 +16,7 @@ #include "../dispatcher/LatencyTracker.h" +#include <android-base/properties.h> #include <binder/Binder.h> #include <gtest/gtest.h> #include <inttypes.h> @@ -23,11 +24,16 @@ #define TAG "LatencyTracker_test" +using android::base::HwTimeoutMultiplier; using android::inputdispatcher::InputEventTimeline; using android::inputdispatcher::LatencyTracker; namespace android::inputdispatcher { +const std::chrono::duration ANR_TIMEOUT = std::chrono::milliseconds( + android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS * + HwTimeoutMultiplier()); + InputEventTimeline getTestTimeline() { InputEventTimeline t( /*isDown*/ true, @@ -57,6 +63,8 @@ protected: } void TearDown() override {} + void triggerEventReporting(nsecs_t lastEventTime); + void assertReceivedTimeline(const InputEventTimeline& timeline); /** * Timelines can be received in any order (order is not guaranteed). So if we are expecting more @@ -72,8 +80,17 @@ private: std::deque<InputEventTimeline> mReceivedTimelines; }; +/** + * Send an event that would trigger the reporting of all of the events that are at least as old as + * the provided 'lastEventTime'. + */ +void LatencyTrackerTest::triggerEventReporting(nsecs_t lastEventTime) { + const nsecs_t triggerEventTime = + lastEventTime + std::chrono::nanoseconds(ANR_TIMEOUT).count() + 1; + mTracker->trackListener(1 /*inputEventId*/, true /*isDown*/, triggerEventTime, 3 /*readTime*/); +} + void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeline) { - mTracker->reportNow(); ASSERT_FALSE(mReceivedTimelines.empty()); const InputEventTimeline& t = mReceivedTimelines.front(); ASSERT_EQ(timeline, t); @@ -88,7 +105,6 @@ void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeli * equal element in B, and for every element in B there is an equal element in A. */ void LatencyTrackerTest::assertReceivedTimelines(const std::vector<InputEventTimeline>& timelines) { - mTracker->reportNow(); ASSERT_EQ(timelines.size(), mReceivedTimelines.size()); for (const InputEventTimeline& expectedTimeline : timelines) { bool found = false; @@ -121,6 +137,7 @@ void LatencyTrackerTest::assertReceivedTimelines(const std::vector<InputEventTim */ TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) { mTracker->trackListener(1 /*inputEventId*/, false /*isDown*/, 2 /*eventTime*/, 3 /*readTime*/); + triggerEventReporting(2 /*eventTime*/); assertReceivedTimeline(InputEventTimeline{false, 2, 3}); } @@ -130,6 +147,7 @@ TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) { TEST_F(LatencyTrackerTest, TrackFinishedEvent_DoesNotTriggerReporting) { mTracker->trackFinishedEvent(1 /*inputEventId*/, connection1, 2 /*deliveryTime*/, 3 /*consumeTime*/, 4 /*finishTime*/); + triggerEventReporting(4 /*eventTime*/); assertReceivedTimelines({}); } @@ -141,6 +159,7 @@ TEST_F(LatencyTrackerTest, TrackGraphicsLatency_DoesNotTriggerReporting) { graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2; graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 3; mTracker->trackGraphicsLatency(1 /*inputEventId*/, connection2, graphicsTimeline); + triggerEventReporting(3 /*eventTime*/); assertReceivedTimelines({}); } @@ -155,9 +174,30 @@ TEST_F(LatencyTrackerTest, TrackAllParameters_ReportsFullTimeline) { expectedCT.consumeTime, expectedCT.finishTime); mTracker->trackGraphicsLatency(inputEventId, connectionToken, expectedCT.graphicsTimeline); + triggerEventReporting(expected.eventTime); assertReceivedTimeline(expected); } +/** + * Send 2 events with the same inputEventId, but different eventTime's. Ensure that no crash occurs, + * and that the tracker drops such events completely. + */ +TEST_F(LatencyTrackerTest, WhenDuplicateEventsAreReported_DoesNotCrash) { + constexpr nsecs_t inputEventId = 1; + constexpr nsecs_t readTime = 3; // does not matter for this test + constexpr bool isDown = true; // does not matter for this test + + // In the following 2 calls to trackListener, the inputEventId's are the same, but event times + // are different. + mTracker->trackListener(inputEventId, isDown, 1 /*eventTime*/, readTime); + mTracker->trackListener(inputEventId, isDown, 2 /*eventTime*/, readTime); + + triggerEventReporting(2 /*eventTime*/); + // Since we sent duplicate input events, the tracker should just delete all of them, because it + // does not have enough information to properly track them. + assertReceivedTimelines({}); +} + TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) { constexpr int32_t inputEventId1 = 1; InputEventTimeline timeline1( @@ -204,6 +244,7 @@ TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) { mTracker->trackGraphicsLatency(inputEventId2, connection2, connectionTimeline2.graphicsTimeline); // Now both events should be completed + triggerEventReporting(timeline2.eventTime); assertReceivedTimelines({timeline1, timeline2}); } @@ -228,6 +269,7 @@ TEST_F(LatencyTrackerTest, IncompleteEvents_AreHandledConsistently) { mTracker->trackGraphicsLatency(1 /*inputEventId*/, token, expectedCT.graphicsTimeline); expectedTimelines[0].connectionTimelines.emplace(token, std::move(expectedCT)); + triggerEventReporting(timeline.eventTime); assertReceivedTimelines(expectedTimelines); } @@ -246,6 +288,7 @@ TEST_F(LatencyTrackerTest, EventsAreTracked_WhenTrackListenerIsCalledFirst) { mTracker->trackGraphicsLatency(inputEventId, connection1, expectedCT.graphicsTimeline); mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime); + triggerEventReporting(expected.eventTime); assertReceivedTimeline( InputEventTimeline{expected.isDown, expected.eventTime, expected.readTime}); } diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp new file mode 100644 index 0000000000..df4db199f8 --- /dev/null +++ b/services/inputflinger/tests/fuzzers/Android.bp @@ -0,0 +1,45 @@ +// Copyright (C) 2021 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. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + + +cc_fuzz { + name: "inputflinger_latencytracker_fuzzer", + defaults: [ + "inputflinger_defaults", + ], + include_dirs: [ + "frameworks/native/services/inputflinger", + ], + shared_libs: [ + "libbase", + "libbinder", + "liblog", + "libui", + "libutils", + "libinput", + "libinputflinger", + ], + srcs: [ + "LatencyTrackerFuzzer.cpp", + ], +} diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp new file mode 100644 index 0000000000..4f066ad513 --- /dev/null +++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2021 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 <fuzzer/FuzzedDataProvider.h> +#include "dispatcher/LatencyTracker.h" + +namespace android { + +namespace inputdispatcher { + +/** + * A processor of InputEventTimelines that does nothing with the provided data. + */ +class EmptyProcessor : public InputEventTimelineProcessor { +public: + /** + * Just ignore the provided timeline + */ + void processTimeline(const InputEventTimeline& timeline) override { + for (const auto& [token, connectionTimeline] : timeline.connectionTimelines) { + connectionTimeline.isComplete(); + } + }; +}; + +static sp<IBinder> getConnectionToken(FuzzedDataProvider& fdp, + std::array<sp<IBinder>, 10>& tokens) { + const bool useExistingToken = fdp.ConsumeBool(); + if (useExistingToken) { + return tokens[fdp.ConsumeIntegralInRange<size_t>(0ul, tokens.size() - 1)]; + } + return new BBinder(); +} + +extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { + FuzzedDataProvider fdp(data, size); + + EmptyProcessor emptyProcessor; + LatencyTracker tracker(&emptyProcessor); + + // Make some pre-defined tokens to ensure that some timelines are complete. + std::array<sp<IBinder> /*token*/, 10> predefinedTokens; + for (size_t i = 0; i < predefinedTokens.size(); i++) { + predefinedTokens[i] = new BBinder(); + } + + // Randomly invoke LatencyTracker api's until randomness is exhausted. + while (fdp.remaining_bytes() > 0) { + fdp.PickValueInArray<std::function<void()>>({ + [&]() -> void { + int32_t inputEventId = fdp.ConsumeIntegral<int32_t>(); + int32_t isDown = fdp.ConsumeBool(); + nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>(); + nsecs_t readTime = fdp.ConsumeIntegral<nsecs_t>(); + tracker.trackListener(inputEventId, isDown, eventTime, readTime); + }, + [&]() -> void { + int32_t inputEventId = fdp.ConsumeIntegral<int32_t>(); + sp<IBinder> connectionToken = getConnectionToken(fdp, predefinedTokens); + nsecs_t deliveryTime = fdp.ConsumeIntegral<nsecs_t>(); + nsecs_t consumeTime = fdp.ConsumeIntegral<nsecs_t>(); + nsecs_t finishTime = fdp.ConsumeIntegral<nsecs_t>(); + tracker.trackFinishedEvent(inputEventId, connectionToken, deliveryTime, + consumeTime, finishTime); + }, + [&]() -> void { + int32_t inputEventId = fdp.ConsumeIntegral<int32_t>(); + sp<IBinder> connectionToken = getConnectionToken(fdp, predefinedTokens); + std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline; + for (size_t i = 0; i < graphicsTimeline.size(); i++) { + graphicsTimeline[i] = fdp.ConsumeIntegral<nsecs_t>(); + } + tracker.trackGraphicsLatency(inputEventId, connectionToken, graphicsTimeline); + }, + })(); + } + + return 0; +} + +} // namespace inputdispatcher + +} // namespace android
\ No newline at end of file diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index 4151b4512f..bba6e22354 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -36,7 +36,7 @@ cc_library_shared { "-Wall", "-Werror", "-Wextra", - "-fvisibility=hidden" + "-fvisibility=hidden", ], header_libs: [ @@ -60,6 +60,7 @@ cc_library_shared { "libbase", "libhidlbase", "libfmq", + "packagemanager_aidl-cpp", "android.hardware.sensors@1.0", "android.hardware.sensors@2.0", "android.hardware.sensors@2.1", @@ -76,6 +77,7 @@ cc_library_shared { "libsensor", "libsensorprivacy", "libpermission", + "packagemanager_aidl-cpp", ], } diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index c233bf06cf..c351676b1d 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -168,7 +168,7 @@ void SensorDevice::initializeSensorList() { } } - // Sanity check and clamp power if it is 0 (or close) + // Check and clamp power if it is 0 (or close) constexpr float MIN_POWER_MA = 0.001; // 1 microAmp if (sensor.power < MIN_POWER_MA) { ALOGI("%s's reported power %f invalid, clamped to %f", diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp index 9ce8d9bb18..c58e992643 100644 --- a/services/sensorservice/SensorEventConnection.cpp +++ b/services/sensorservice/SensorEventConnection.cpp @@ -867,7 +867,7 @@ int SensorService::SensorEventConnection::handleEvent(int fd, int events, void* } else if (numBytesRead == sizeof(uint32_t)) { uint32_t numAcks = 0; memcpy(&numAcks, buf, numBytesRead); - // Sanity check to ensure there are no read errors in recv, numAcks is always + // Check to ensure there are no read errors in recv, numAcks is always // within the range and not zero. If any of the above don't hold reset // mWakeLockRefCount to zero. if (numAcks > 0 && numAcks < mWakeLockRefCount) { diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 726fe8ea84..3081b1abef 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -1197,7 +1197,7 @@ int32_t SensorService::getIdFromUuid(const Sensor::uuid_t &uuid) const { // We have a dynamic sensor. if (!sHmacGlobalKeyIsValid) { - // Rather than risk exposing UUIDs, we cripple dynamic sensors. + // Rather than risk exposing UUIDs, we slow down dynamic sensors. ALOGW("HMAC key failure; dynamic sensor getId() will be wrong."); return 0; } @@ -1223,7 +1223,7 @@ int32_t SensorService::getIdFromUuid(const Sensor::uuid_t &uuid) const { sHmacGlobalKey, sizeof(sHmacGlobalKey), uuidAndApp, sizeof(uuidAndApp), hash, &hashLen) == nullptr) { - // Rather than risk exposing UUIDs, we cripple dynamic sensors. + // Rather than risk exposing UUIDs, we slow down dynamic sensors. ALOGW("HMAC failure; dynamic sensor getId() will be wrong."); return 0; } diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index eeb3f3a288..af012cd11c 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -66,6 +66,7 @@ cc_defaults { "libinput", "libutils", "libSurfaceFlingerProp", + "server_configurable_flags", ], static_libs: [ "libcompositionengine", @@ -154,6 +155,7 @@ filegroup { "DisplayRenderArea.cpp", "Effects/Daltonizer.cpp", "EventLog/EventLog.cpp", + "FlagManager.cpp", "FpsReporter.cpp", "FrameTracer/FrameTracer.cpp", "FrameTracker.cpp", diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index 71db330f01..d805294b07 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -152,39 +152,10 @@ std::optional<compositionengine::LayerFE::LayerSettings> BufferLayer::prepareCli return result; } - if (CC_UNLIKELY(mBufferInfo.mBuffer == 0)) { - // the texture has not been created yet, this Layer has - // in fact never been drawn into. This happens frequently with - // SurfaceView because the WindowManager can't know when the client - // has drawn the first time. - - // If there is nothing under us, we paint the screen in black, otherwise - // we just skip this update. - - // figure out if there is something below us - Region under; - bool finished = false; - mFlinger->mDrawingState.traverseInZOrder([&](Layer* layer) { - if (finished || layer == static_cast<BufferLayer const*>(this)) { - finished = true; - return; - } - - under.orSelf(layer->getScreenBounds()); - }); - // if not everything below us is covered, we plug the holes! - Region holes(targetSettings.clip.subtract(under)); - if (!holes.isEmpty()) { - targetSettings.clearRegion.orSelf(holes); - } - - if (mSidebandStream != nullptr) { - // For surfaceview of tv sideband, there is no activeBuffer - // in bufferqueue, we need return LayerSettings. - return result; - } else { - return std::nullopt; - } + if (CC_UNLIKELY(mBufferInfo.mBuffer == 0) && mSidebandStream != nullptr) { + // For surfaceview of tv sideband, there is no activeBuffer + // in bufferqueue, we need return LayerSettings. + return result; } const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) || ((isSecure() || isProtected()) && !targetSettings.isSecure); @@ -316,7 +287,7 @@ void BufferLayer::preparePerFrameCompositionState() { // Sideband layers auto* compositionState = editCompositionState(); - if (compositionState->sidebandStream.get()) { + if (compositionState->sidebandStream.get() && !compositionState->sidebandStreamHasFrame) { compositionState->compositionType = Hwc2::IComposerClient::Composition::SIDEBAND; return; } else { @@ -332,6 +303,7 @@ void BufferLayer::preparePerFrameCompositionState() { ? 0 : mBufferInfo.mBufferSlot; compositionState->acquireFence = mBufferInfo.mFence; + compositionState->sidebandStreamHasFrame = false; } bool BufferLayer::onPreComposition(nsecs_t refreshStartTime) { diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp index 99e470dfe6..e4f642e1f6 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -153,11 +153,12 @@ bool BufferQueueLayer::fenceHasSignaled() const { } bool BufferQueueLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const { + Mutex::Autolock lock(mQueueItemLock); + if (!hasFrameUpdate() || isRemovedFromCurrentState()) { return true; } - Mutex::Autolock lock(mQueueItemLock); return mQueueItems[0].item.mTimestamp <= expectedPresentTime; } @@ -196,11 +197,10 @@ uint64_t BufferQueueLayer::getFrameNumber(nsecs_t expectedPresentTime) const { bool BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) { // We need to update the sideband stream if the layer has both a buffer and a sideband stream. - const bool updateSidebandStream = hasFrameUpdate() && mSidebandStream.get(); + editCompositionState()->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get(); bool sidebandStreamChanged = true; - if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false) || - updateSidebandStream) { + if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) { // mSidebandStreamChanged was changed to false mSidebandStream = mConsumer->getSidebandStream(); auto* layerCompositionState = editCompositionState(); @@ -269,13 +269,15 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t // and return early if (queuedBuffer) { Mutex::Autolock lock(mQueueItemLock); - mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage); - mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber); - if (mQueueItems[0].surfaceFrame) { - addSurfaceFrameDroppedForBuffer(mQueueItems[0].surfaceFrame); + if (mQueuedFrames > 0) { + mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage); + mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber); + if (mQueueItems[0].surfaceFrame) { + addSurfaceFrameDroppedForBuffer(mQueueItems[0].surfaceFrame); + } + mQueueItems.erase(mQueueItems.begin()); + mQueuedFrames--; } - mQueueItems.erase(mQueueItems.begin()); - mQueuedFrames--; } return BAD_VALUE; } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) { @@ -305,6 +307,7 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t return BAD_VALUE; } + bool more_frames_pending = false; if (queuedBuffer) { // Autolock scope auto currentFrameNumber = mConsumer->getFrameNumber(); @@ -313,7 +316,7 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t // Remove any stale buffers that have been dropped during // updateTexImage - while (mQueueItems[0].item.mFrameNumber != currentFrameNumber) { + while (mQueuedFrames > 0 && mQueueItems[0].item.mFrameNumber != currentFrameNumber) { mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage); mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber); if (mQueueItems[0].surfaceFrame) { @@ -334,11 +337,12 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime); } mQueueItems.erase(mQueueItems.begin()); + more_frames_pending = (mQueuedFrames.fetch_sub(1) > 1); } // Decrement the queued-frames count. Signal another event if we // have more frames pending. - if ((queuedBuffer && mQueuedFrames.fetch_sub(1) > 1) || mAutoRefresh) { + if ((queuedBuffer && more_frames_pending) || mAutoRefresh) { mFlinger->signalLayerUpdate(); } diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index cd531d6afc..7466c0638c 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -14,11 +14,6 @@ * limitations under the License. */ -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" -#pragma clang diagnostic ignored "-Wextra" - //#define LOG_NDEBUG 0 #undef LOG_TAG #define LOG_TAG "BufferStateLayer" @@ -596,7 +591,7 @@ bool BufferStateLayer::setTransparentRegionHint(const Region& transparent) { return true; } -Rect BufferStateLayer::getBufferSize(const State& s) const { +Rect BufferStateLayer::getBufferSize(const State& /*s*/) const { // for buffer state layers we use the display frame size as the buffer size. if (mBufferInfo.mBuffer == nullptr) { @@ -618,7 +613,7 @@ Rect BufferStateLayer::getBufferSize(const State& s) const { } } - return Rect(0, 0, bufWidth, bufHeight); + return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight)); } FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) const { @@ -702,9 +697,9 @@ void BufferStateLayer::setAutoRefresh(bool autoRefresh) { bool BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) { // We need to update the sideband stream if the layer has both a buffer and a sideband stream. - const bool updateSidebandStream = hasFrameUpdate() && mSidebandStream.get(); + editCompositionState()->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get(); - if (mSidebandStreamChanged.exchange(false) || updateSidebandStream) { + if (mSidebandStreamChanged.exchange(false)) { const State& s(getDrawingState()); // mSidebandStreamChanged was true mSidebandStream = s.sidebandStream; @@ -817,7 +812,7 @@ void BufferStateLayer::HwcSlotGenerator::bufferErased(const client_cache_t& clie eraseBufferLocked(clientCacheId); } -uint32_t BufferStateLayer::HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) { +int BufferStateLayer::HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) { std::lock_guard<std::mutex> lock(mMutex); auto itr = mCachedBuffers.find(clientCacheId); if (itr == mCachedBuffers.end()) { @@ -828,7 +823,7 @@ uint32_t BufferStateLayer::HwcSlotGenerator::getHwcCacheSlot(const client_cache_ return hwcCacheSlot; } -uint32_t BufferStateLayer::HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId) +int BufferStateLayer::HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex) { if (!clientCacheId.isValid()) { ALOGE("invalid process, returning invalid slot"); @@ -837,17 +832,17 @@ uint32_t BufferStateLayer::HwcSlotGenerator::addCachedBuffer(const client_cache_ ClientCache::getInstance().registerErasedRecipient(clientCacheId, wp<ErasedRecipient>(this)); - uint32_t hwcCacheSlot = getFreeHwcCacheSlot(); + int hwcCacheSlot = getFreeHwcCacheSlot(); mCachedBuffers[clientCacheId] = {hwcCacheSlot, mCounter++}; return hwcCacheSlot; } -uint32_t BufferStateLayer::HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) { +int BufferStateLayer::HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) { if (mFreeHwcCacheSlots.empty()) { evictLeastRecentlyUsed(); } - uint32_t hwcCacheSlot = mFreeHwcCacheSlots.top(); + int hwcCacheSlot = mFreeHwcCacheSlots.top(); mFreeHwcCacheSlots.pop(); return hwcCacheSlot; } @@ -934,8 +929,8 @@ bool BufferStateLayer::bufferNeedsFiltering() const { return false; } - uint32_t bufferWidth = s.buffer->getBuffer()->width; - uint32_t bufferHeight = s.buffer->getBuffer()->height; + int32_t bufferWidth = s.buffer->getBuffer()->width; + int32_t bufferHeight = s.buffer->getBuffer()->height; // Undo any transformations on the buffer and return the result. if (s.bufferTransform & ui::Transform::ROT_90) { @@ -994,6 +989,3 @@ Rect BufferStateLayer::getInputBounds() const { } } // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion -Wextra" diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index 0a0527c1a9..124e91ac61 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -178,19 +178,19 @@ private: class HwcSlotGenerator : public ClientCache::ErasedRecipient { public: HwcSlotGenerator() { - for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { mFreeHwcCacheSlots.push(i); } } void bufferErased(const client_cache_t& clientCacheId); - uint32_t getHwcCacheSlot(const client_cache_t& clientCacheId); + int getHwcCacheSlot(const client_cache_t& clientCacheId); private: friend class SlotGenerationTest; - uint32_t addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex); - uint32_t getFreeHwcCacheSlot() REQUIRES(mMutex); + int addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex); + int getFreeHwcCacheSlot() REQUIRES(mMutex); void evictLeastRecentlyUsed() REQUIRES(mMutex); void eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex); @@ -202,11 +202,10 @@ private: std::mutex mMutex; - std::unordered_map<client_cache_t, - std::pair<uint32_t /*HwcCacheSlot*/, uint32_t /*counter*/>, + std::unordered_map<client_cache_t, std::pair<int /*HwcCacheSlot*/, uint64_t /*counter*/>, CachedBufferHash> mCachedBuffers GUARDED_BY(mMutex); - std::stack<uint32_t /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex); + std::stack<int /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex); // The cache increments this counter value when a slot is updated or used. // Used to track the least recently-used buffer diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp index aac6c913cf..8da2e24aa4 100644 --- a/services/surfaceflinger/Client.cpp +++ b/services/surfaceflinger/Client.cpp @@ -14,10 +14,6 @@ * limitations under the License. */ -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - #include <stdint.h> #include <sys/types.h> @@ -132,6 +128,3 @@ status_t Client::getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outSt // --------------------------------------------------------------------------- }; // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp index f310738423..1ef8f78894 100644 --- a/services/surfaceflinger/ClientCache.cpp +++ b/services/surfaceflinger/ClientCache.cpp @@ -21,12 +21,12 @@ #include <cinttypes> +#include <android-base/stringprintf.h> + #include "ClientCache.h" namespace android { -using base::StringAppendF; - ANDROID_SINGLETON_STATIC_INSTANCE(ClientCache); ClientCache::ClientCache() : mDeathRecipient(new CacheDeathRecipient) {} @@ -212,16 +212,15 @@ void ClientCache::CacheDeathRecipient::binderDied(const wp<IBinder>& who) { void ClientCache::dump(std::string& result) { std::lock_guard lock(mMutex); - for (auto i : mBuffers) { - const sp<IBinder>& cacheOwner = i.second.first; - StringAppendF(&result," Cache owner: %p\n", cacheOwner.get()); - auto &buffers = i.second.second; - for (auto& [id, clientCacheBuffer] : buffers) { - StringAppendF(&result, "\t ID: %d, Width/Height: %d,%d\n", (int)id, - (int)clientCacheBuffer.buffer->getBuffer()->getWidth(), - (int)clientCacheBuffer.buffer->getBuffer()->getHeight()); + for (const auto& [_, cache] : mBuffers) { + base::StringAppendF(&result, " Cache owner: %p\n", cache.first.get()); + + for (const auto& [id, entry] : cache.second) { + const auto& buffer = entry.buffer->getBuffer(); + base::StringAppendF(&result, "\tID: %" PRIu64 ", size: %ux%u\n", id, buffer->getWidth(), + buffer->getHeight()); } } } -}; // namespace android +} // namespace android diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h index 554e2f4868..95d553d02f 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h @@ -47,9 +47,6 @@ struct CompositionRefreshArgs { // All the layers that have queued updates. Layers layersWithQueuedFrames; - // If true, forces the entire display to be considered dirty and repainted - bool repaintEverything{false}; - // Controls how the color mode is chosen for an output OutputColorSetting outputColorSetting{OutputColorSetting::kEnhanced}; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h index 14eddb1861..98c4af487e 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h @@ -36,18 +36,12 @@ class CompositionEngine; struct DisplayCreationArgs { DisplayId id; - // Unset for virtual displays - std::optional<ui::DisplayConnectionType> connectionType; - // Size of the display in pixels - ui::Size pixels = ui::Size::INVALID; + ui::Size pixels = ui::kInvalidSize; // True if this display should be considered secure bool isSecure = false; - // Gives the initial layer stack id to be used for the display - uint32_t layerStackId = ~0u; - // Optional pointer to the power advisor interface, if one is needed for // this display. Hwc2::PowerAdvisor* powerAdvisor = nullptr; @@ -69,11 +63,6 @@ public: return *this; } - DisplayCreationArgsBuilder& setConnectionType(ui::DisplayConnectionType connectionType) { - mArgs.connectionType = connectionType; - return *this; - } - DisplayCreationArgsBuilder& setPixels(ui::Size pixels) { mArgs.pixels = pixels; return *this; @@ -84,11 +73,6 @@ public: return *this; } - DisplayCreationArgsBuilder& setLayerStackId(uint32_t layerStackId) { - mArgs.layerStackId = layerStackId; - return *this; - } - DisplayCreationArgsBuilder& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) { mArgs.powerAdvisor = powerAdvisor; return *this; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h index e51019ae99..6d1017f678 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h @@ -115,10 +115,6 @@ public: // If set to true, the target buffer has protected content support. const bool supportsProtectedContent; - // Modified by each call to prepareClientComposition to indicate the - // region of the target buffer that should be cleared. - Region& clearRegion; - // Viewport of the target being rendered to. This is used to determine // the shadow light position. const Rect& viewport; @@ -177,11 +173,10 @@ using LayerFESet = std::unordered_set<sp<LayerFE>, LayerFESpHash>; static inline bool operator==(const LayerFE::ClientCompositionTargetSettings& lhs, const LayerFE::ClientCompositionTargetSettings& rhs) { - return lhs.clip.hasSameRects(rhs.clip) && - lhs.needsFiltering == rhs.needsFiltering && lhs.isSecure == rhs.isSecure && + return lhs.clip.hasSameRects(rhs.clip) && lhs.needsFiltering == rhs.needsFiltering && + lhs.isSecure == rhs.isSecure && lhs.supportsProtectedContent == rhs.supportsProtectedContent && - lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.viewport == rhs.viewport && - lhs.dataspace == rhs.dataspace && + lhs.viewport == rhs.viewport && lhs.dataspace == rhs.dataspace && lhs.realContentIsVisible == rhs.realContentIsVisible && lhs.clearContent == rhs.clearContent; } @@ -202,8 +197,6 @@ static inline void PrintTo(const LayerFE::ClientCompositionTargetSettings& setti *os << "\n .needsFiltering = " << settings.needsFiltering; *os << "\n .isSecure = " << settings.isSecure; *os << "\n .supportsProtectedContent = " << settings.supportsProtectedContent; - *os << "\n .clearRegion = "; - PrintTo(settings.clearRegion, os); *os << "\n .viewport = "; PrintTo(settings.viewport, os); *os << "\n .dataspace = "; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h index a45be8a7a2..a000661c79 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -22,6 +22,7 @@ #include <math/mat4.h> #include <ui/BlurRegion.h> #include <ui/FloatRect.h> +#include <ui/LayerStack.h> #include <ui/Rect.h> #include <ui/Region.h> #include <ui/Transform.h> @@ -93,11 +94,9 @@ struct LayerFECompositionState { /* * Visibility state */ - // the layer stack this layer belongs to - std::optional<uint32_t> layerStackId; - // If true, this layer should be only visible on the internal display - bool internalOnly{false}; + // The filter that determines which outputs include this layer + ui::LayerFilter outputFilter; // If false, this layer should not be considered visible bool isVisible{true}; @@ -167,6 +166,8 @@ struct LayerFECompositionState { // The handle to use for a sideband stream for this layer sp<NativeHandle> sidebandStream; + // If true, this sideband layer has a frame update + bool sidebandStreamHasFrame{false}; // The color for this layer half4 color; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h index 0ef0b995c3..a33b57d668 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -28,6 +28,7 @@ #include <renderengine/LayerSettings.h> #include <ui/Fence.h> #include <ui/GraphicTypes.h> +#include <ui/LayerStack.h> #include <ui/Region.h> #include <ui/Transform.h> #include <utils/StrongPointer.h> @@ -180,9 +181,8 @@ public: // output. virtual ui::Transform::RotationFlags getTransformHint() const = 0; - // Sets the layer stack filtering settings for this output. See - // belongsInOutput for full details. - virtual void setLayerStackFilter(uint32_t layerStackId, bool isInternal) = 0; + // Sets the filter for this output. See Output::includesLayer. + virtual void setLayerFilter(ui::LayerFilter) = 0; // Sets the output color mode virtual void setColorProfile(const ColorProfile&) = 0; @@ -221,20 +221,12 @@ public: virtual OutputCompositionState& editState() = 0; // Gets the dirty region in layer stack space. - // If repaintEverything is true, this will be the full display bounds. - virtual Region getDirtyRegion(bool repaintEverything) const = 0; + virtual Region getDirtyRegion() const = 0; - // Tests whether a given layerStackId belongs in this output. - // A layer belongs to the output if its layerStackId matches the of the output layerStackId, - // unless the layer should display on the primary output only and this is not the primary output - - // A layer belongs to the output if its layerStackId matches. Additionally - // if the layer should only show in the internal (primary) display only and - // this output allows that. - virtual bool belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const = 0; - - // Determines if a layer belongs to the output. - virtual bool belongsInOutput(const sp<LayerFE>&) const = 0; + // Returns whether the output includes a layer, based on their respective filters. + // See Output::setLayerFilter. + virtual bool includesLayer(ui::LayerFilter) const = 0; + virtual bool includesLayer(const sp<LayerFE>&) const = 0; // Returns a pointer to the output layer corresponding to the given layer on // this output, or nullptr if the layer does not have one @@ -294,7 +286,7 @@ protected: virtual bool getSkipColorTransform() const = 0; virtual FrameFences presentAndGetFrameFences() = 0; virtual std::vector<LayerFE::LayerSettings> generateClientCompositionRequests( - bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) = 0; + bool supportsProtectedContent, ui::Dataspace outputDataspace) = 0; virtual void appendRegionFlashRequests( const Region& flashRegion, std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h index 58bb41adbb..a63145a7fd 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h @@ -28,51 +28,36 @@ namespace compositionengine { // Geometrical space to which content is projected. // For example, this can be the layer space or the physical display space. -struct ProjectionSpace { +class ProjectionSpace { +public: ProjectionSpace() = default; - ProjectionSpace(ui::Size size, Rect content) - : bounds(std::move(size)), content(std::move(content)) {} - - // Bounds of this space. Always starts at (0,0). - Rect bounds; - - // Rect onto which content is projected. - Rect content; - - // The orientation of this space. This value is meaningful only in relation to the rotation - // of another projection space and it's used to determine the rotating transformation when - // mapping between the two. - // As a convention when using this struct orientation = 0 for the "oriented*" projection - // spaces. For example when the display is rotated 90 degress counterclockwise, the orientation - // of the display space will become 90, while the orientation of the layer stack space will - // remain the same. - ui::Rotation orientation = ui::ROTATION_0; + ProjectionSpace(ui::Size size, Rect content) : mBounds(size), mContent(std::move(content)) {} // Returns a transform which maps this.content into destination.content // and also rotates according to this.orientation and destination.orientation ui::Transform getTransform(const ProjectionSpace& destination) const { - ui::Rotation rotation = destination.orientation - orientation; + ui::Rotation rotation = destination.getOrientation() - mOrientation; // Compute a transformation which rotates the destination in a way it has the same // orientation as us. const uint32_t inverseRotationFlags = ui::Transform::toRotationFlags(-rotation); ui::Transform inverseRotatingTransform; - inverseRotatingTransform.set(inverseRotationFlags, destination.bounds.width(), - destination.bounds.height()); + inverseRotatingTransform.set(inverseRotationFlags, destination.getBounds().width, + destination.getBounds().height); // The destination content rotated so it has the same orientation as us. - Rect orientedDestContent = inverseRotatingTransform.transform(destination.content); + Rect orientedDestContent = inverseRotatingTransform.transform(destination.getContent()); // Compute translation from the source content to (0, 0). - const float sourceX = content.left; - const float sourceY = content.top; + const float sourceX = mContent.left; + const float sourceY = mContent.top; ui::Transform sourceTranslation; sourceTranslation.set(-sourceX, -sourceY); // Compute scaling transform which maps source content to destination content, assuming // they are both at (0, 0). ui::Transform scale; - const float scaleX = static_cast<float>(orientedDestContent.width()) / content.width(); - const float scaleY = static_cast<float>(orientedDestContent.height()) / content.height(); + const float scaleX = static_cast<float>(orientedDestContent.width()) / mContent.width(); + const float scaleY = static_cast<float>(orientedDestContent.height()) / mContent.height(); scale.set(scaleX, 0, 0, scaleY); // Compute translation from (0, 0) to the orientated destination content. @@ -83,8 +68,8 @@ struct ProjectionSpace { // Compute rotation transform. const uint32_t orientationFlags = ui::Transform::toRotationFlags(rotation); - auto orientedDestWidth = destination.bounds.width(); - auto orientedDestHeight = destination.bounds.height(); + auto orientedDestWidth = destination.getBounds().width; + auto orientedDestHeight = destination.getBounds().height; if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) { std::swap(orientedDestWidth, orientedDestHeight); } @@ -98,9 +83,39 @@ struct ProjectionSpace { } bool operator==(const ProjectionSpace& other) const { - return bounds == other.bounds && content == other.content && - orientation == other.orientation; + return mBounds == other.mBounds && mContent == other.mContent && + mOrientation == other.mOrientation; } + + void setBounds(ui::Size newBounds) { mBounds = std::move(newBounds); } + + void setContent(Rect newContent) { mContent = std::move(newContent); } + + void setOrientation(ui::Rotation newOrientation) { mOrientation = newOrientation; } + + Rect getBoundsAsRect() const { return Rect(mBounds.getWidth(), mBounds.getHeight()); } + + const ui::Size& getBounds() const { return mBounds; } + + const Rect& getContent() const { return mContent; } + + ui::Rotation getOrientation() const { return mOrientation; } + +private: + // Bounds of this space. Always starts at (0,0). + ui::Size mBounds = ui::Size(); + + // Rect onto which content is projected. + Rect mContent = Rect(); + + // The orientation of this space. This value is meaningful only in relation to the rotation + // of another projection space and it's used to determine the rotating transformation when + // mapping between the two. + // As a convention when using this struct orientation = 0 for the "oriented*" projection + // spaces. For example when the display is rotated 90 degress counterclockwise, the orientation + // of the display space will become 90, while the orientation of the layer stack space will + // remain the same. + ui::Rotation mOrientation = ui::ROTATION_0; }; } // namespace compositionengine @@ -108,8 +123,8 @@ struct ProjectionSpace { inline std::string to_string(const android::compositionengine::ProjectionSpace& space) { return android::base:: StringPrintf("ProjectionSpace(bounds = %s, content = %s, orientation = %s)", - to_string(space.bounds).c_str(), to_string(space.content).c_str(), - toCString(space.orientation)); + to_string(space.getBoundsAsRect()).c_str(), + to_string(space.getContent()).c_str(), toCString(space.getOrientation())); } // Defining PrintTo helps with Google Tests. diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h index bb540ea7ee..b407267560 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -83,9 +83,8 @@ public: std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const; private: - bool mIsVirtual = false; - bool mIsDisconnected = false; DisplayId mId; + bool mIsDisconnected = false; Hwc2::PowerAdvisor* mPowerAdvisor = nullptr; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h index 6b9597b2d5..7521324756 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h @@ -22,6 +22,7 @@ #include <math/mat4.h> #include <ui/FloatRect.h> +#include <ui/LayerStack.h> #include <ui/Rect.h> #include <ui/Region.h> #include <ui/StretchEffect.h> @@ -52,13 +53,14 @@ void dumpVal(std::string& out, const char* name, const std::string& valueName, E dumpVal(out, name, valueName, static_cast<std::underlying_type_t<EnumType>>(value)); } -void dumpVal(std::string& out, const char* name, const FloatRect& rect); -void dumpVal(std::string& out, const char* name, const Rect& rect); -void dumpVal(std::string& out, const char* name, const Region& region); -void dumpVal(std::string& out, const char* name, const ui::Transform&); -void dumpVal(std::string& out, const char* name, const ui::Size&); +void dumpVal(std::string& out, const char* name, ui::LayerFilter); +void dumpVal(std::string& out, const char* name, ui::Size); -void dumpVal(std::string& out, const char* name, const mat4& tr); +void dumpVal(std::string& out, const char* name, const FloatRect&); +void dumpVal(std::string& out, const char* name, const Rect&); +void dumpVal(std::string& out, const char* name, const Region&); +void dumpVal(std::string& out, const char* name, const ui::Transform&); +void dumpVal(std::string& out, const char* name, const mat4&); void dumpVal(std::string& out, const char* name, const StretchEffect&); } // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index ddcc907a91..6d49ce6965 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -23,6 +23,7 @@ #include <compositionengine/impl/planner/Planner.h> #include <renderengine/DisplaySettings.h> #include <renderengine/LayerSettings.h> + #include <memory> #include <utility> #include <vector> @@ -45,7 +46,7 @@ public: void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect, const Rect& orientedDisplaySpaceRect) override; void setDisplaySize(const ui::Size&) override; - void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override; + void setLayerFilter(ui::LayerFilter) override; ui::Transform::RotationFlags getTransformHint() const override; void setColorTransform(const compositionengine::CompositionRefreshArgs&) override; @@ -64,9 +65,10 @@ public: compositionengine::RenderSurface* getRenderSurface() const override; void setRenderSurface(std::unique_ptr<compositionengine::RenderSurface>) override; - Region getDirtyRegion(bool repaintEverything) const override; - bool belongsInOutput(std::optional<uint32_t>, bool) const override; - bool belongsInOutput(const sp<LayerFE>&) const override; + Region getDirtyRegion() const override; + + bool includesLayer(ui::LayerFilter) const override; + bool includesLayer(const sp<LayerFE>&) const override; compositionengine::OutputLayer* getOutputLayerForLayer(const sp<LayerFE>&) const override; @@ -111,8 +113,7 @@ protected: bool getSkipColorTransform() const override; compositionengine::Output::FrameFences presentAndGetFrameFences() override; std::vector<LayerFE::LayerSettings> generateClientCompositionRequests( - bool supportsProtectedContent, Region& clearRegion, - ui::Dataspace outputDataspace) override; + bool supportsProtectedContent, ui::Dataspace outputDataspace) override; void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override; void setExpensiveRenderingExpected(bool enabled) override; void dumpBase(std::string&) const; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h index f34cb94079..44f754f936 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h @@ -32,6 +32,7 @@ #pragma clang diagnostic pop // ignored "-Wconversion -Wextra" #include <compositionengine/ProjectionSpace.h> +#include <ui/LayerStack.h> #include <ui/Rect.h> #include <ui/Region.h> #include <ui/Transform.h> @@ -59,11 +60,8 @@ struct OutputCompositionState { // If true, the current frame reused the buffer from a previous client composition bool reusedClientComposition{false}; - // If true, this output displays layers that are internal-only - bool layerStackInternal{false}; - - // The layer stack to display on this display - uint32_t layerStackId{~0u}; + // The conditions for including a layer on this output + ui::LayerFilter layerFilter; // The common space for all layers in the layer stack. layerStackSpace.content is the Rect // which gets projected on the display. The orientation of this space is always ROTATION_0. diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h index a040fa93de..cff652755d 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h @@ -119,8 +119,8 @@ private: std::chrono::steady_clock::time_point now); // A Run is a sequence of CachedSets, which is a candidate for flattening into a single - // CachedSet. Because it is wasteful to flatten 1 CachedSet, a Run must contain more than 1 - // CachedSet + // CachedSet. Because it is wasteful to flatten 1 CachedSet, a run must contain more than + // 1 CachedSet or be used for a hole punch. class Run { public: // A builder for a Run, to aid in construction @@ -154,7 +154,13 @@ private: // Builds a Run instance, if a valid Run may be built. std::optional<Run> validateAndBuild() { - if (mLengths.size() <= 1) { + if (mLengths.size() == 0) { + return std::nullopt; + } + // Runs of length 1 which are hole punch candidates are allowed if the candidate is + // going to be used. + if (mLengths.size() == 1 && + (!mHolePunchCandidate || !(mHolePunchCandidate->requiresHolePunch()))) { return std::nullopt; } diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h index 8fdf3ae887..344b2f9456 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -40,9 +40,12 @@ public: MOCK_METHOD1(setLayerCachingTexturePoolEnabled, void(bool)); MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&)); MOCK_METHOD1(setDisplaySize, void(const ui::Size&)); - MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool)); MOCK_CONST_METHOD0(getTransformHint, ui::Transform::RotationFlags()); + MOCK_METHOD(void, setLayerFilter, (ui::LayerFilter)); + MOCK_METHOD(bool, includesLayer, (ui::LayerFilter), (const)); + MOCK_METHOD(bool, includesLayer, (const sp<compositionengine::LayerFE>&), (const)); + MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&)); MOCK_METHOD1(setColorProfile, void(const ColorProfile&)); MOCK_METHOD2(setDisplayBrightness, void(float, float)); @@ -62,9 +65,7 @@ public: MOCK_CONST_METHOD0(getState, const OutputCompositionState&()); MOCK_METHOD0(editState, OutputCompositionState&()); - MOCK_CONST_METHOD1(getDirtyRegion, Region(bool)); - MOCK_CONST_METHOD2(belongsInOutput, bool(std::optional<uint32_t>, bool)); - MOCK_CONST_METHOD1(belongsInOutput, bool(const sp<compositionengine::LayerFE>&)); + MOCK_METHOD(Region, getDirtyRegion, (), (const)); MOCK_CONST_METHOD1(getOutputLayerForLayer, compositionengine::OutputLayer*(const sp<compositionengine::LayerFE>&)); @@ -113,8 +114,8 @@ public: MOCK_METHOD1(renderCachedSets, void(const CompositionRefreshArgs&)); MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences()); - MOCK_METHOD3(generateClientCompositionRequests, - std::vector<LayerFE::LayerSettings>(bool, Region&, ui::Dataspace)); + MOCK_METHOD2(generateClientCompositionRequests, + std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace)); MOCK_METHOD2(appendRegionFlashRequests, void(const Region&, std::vector<LayerFE::LayerSettings>&)); MOCK_METHOD1(setExpensiveRenderingExpected, void(bool)); diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index 2f2c686805..02fa49f3c0 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -51,12 +51,9 @@ Display::~Display() = default; void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) { mId = args.id; - mIsVirtual = !args.connectionType; mPowerAdvisor = args.powerAdvisor; editState().isSecure = args.isSecure; - editState().displaySpace.bounds = Rect(args.pixels); - setLayerStackFilter(args.layerStackId, - args.connectionType == ui::DisplayConnectionType::Internal); + editState().displaySpace.setBounds(args.pixels); setName(args.name); } @@ -73,7 +70,7 @@ bool Display::isSecure() const { } bool Display::isVirtual() const { - return mIsVirtual; + return VirtualDisplayId::tryCast(mId).has_value(); } std::optional<DisplayId> Display::getDisplayId() const { @@ -117,8 +114,8 @@ void Display::setColorProfile(const ColorProfile& colorProfile) { return; } - if (mIsVirtual) { - ALOGW("%s: Invalid operation on virtual display", __FUNCTION__); + if (isVirtual()) { + ALOGW("%s: Invalid operation on virtual display", __func__); return; } @@ -136,7 +133,7 @@ void Display::dump(std::string& out) const { StringAppendF(&out, " Composition Display State: [\"%s\"]", getName().c_str()); out.append("\n "); - dumpVal(out, "isVirtual", mIsVirtual); + dumpVal(out, "isVirtual", isVirtual()); dumpVal(out, "DisplayId", to_string(mId)); out.append("\n"); @@ -364,8 +361,7 @@ void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refre // 1) It is being handled by hardware composer, which may need this to // keep its virtual display state machine in sync, or // 2) There is work to be done (the dirty region isn't empty) - if (GpuVirtualDisplayId::tryCast(mId) && - getDirtyRegion(refreshArgs.repaintEverything).isEmpty()) { + if (GpuVirtualDisplayId::tryCast(mId) && getDirtyRegion().isEmpty()) { ALOGV("Skipping display composition"); return; } diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp index 5565396922..01c368de88 100644 --- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp +++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp @@ -63,6 +63,18 @@ void dumpVal(std::string& out, const char* name, const std::string& valueName, i dumpVal(out, name, valueName.c_str(), value); } +void dumpVal(std::string& out, const char* name, ui::LayerFilter filter) { + out.append(name); + out.append("={"); + dumpVal(out, "layerStack", filter.layerStack.id); + dumpVal(out, "toInternalDisplay", filter.toInternalDisplay); + out.push_back('}'); +} + +void dumpVal(std::string& out, const char* name, ui::Size size) { + StringAppendF(&out, "%s=[%d %d] ", name, size.width, size.height); +} + void dumpVal(std::string& out, const char* name, const FloatRect& rect) { StringAppendF(&out, "%s=[%f %f %f %f] ", name, rect.left, rect.top, rect.right, rect.bottom); } @@ -80,10 +92,6 @@ void dumpVal(std::string& out, const char* name, const ui::Transform& transform) out.append(" "); } -void dumpVal(std::string& out, const char* name, const ui::Size& size) { - StringAppendF(&out, "%s=[%d %d] ", name, size.width, size.height); -} - void dumpVal(std::string& out, const char* name, const mat4& tr) { StringAppendF(&out, "%s=[" diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 95ae5e514e..eb3f3b1f8a 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -156,38 +156,40 @@ void Output::setProjection(ui::Rotation orientation, const Rect& layerStackSpace const Rect& orientedDisplaySpaceRect) { auto& outputState = editState(); - outputState.displaySpace.orientation = orientation; - LOG_FATAL_IF(outputState.displaySpace.bounds == Rect::INVALID_RECT, + outputState.displaySpace.setOrientation(orientation); + LOG_FATAL_IF(outputState.displaySpace.getBoundsAsRect() == Rect::INVALID_RECT, "The display bounds are unknown."); // Compute orientedDisplaySpace - ui::Size orientedSize = outputState.displaySpace.bounds.getSize(); + ui::Size orientedSize = outputState.displaySpace.getBounds(); if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) { std::swap(orientedSize.width, orientedSize.height); } - outputState.orientedDisplaySpace.bounds = Rect(orientedSize); - outputState.orientedDisplaySpace.content = orientedDisplaySpaceRect; + outputState.orientedDisplaySpace.setBounds(orientedSize); + outputState.orientedDisplaySpace.setContent(orientedDisplaySpaceRect); // Compute displaySpace.content const uint32_t transformOrientationFlags = ui::Transform::toRotationFlags(orientation); ui::Transform rotation; if (transformOrientationFlags != ui::Transform::ROT_INVALID) { - const auto displaySize = outputState.displaySpace.bounds; + const auto displaySize = outputState.displaySpace.getBoundsAsRect(); rotation.set(transformOrientationFlags, displaySize.width(), displaySize.height()); } - outputState.displaySpace.content = rotation.transform(orientedDisplaySpaceRect); + outputState.displaySpace.setContent(rotation.transform(orientedDisplaySpaceRect)); // Compute framebufferSpace - outputState.framebufferSpace.orientation = orientation; - LOG_FATAL_IF(outputState.framebufferSpace.bounds == Rect::INVALID_RECT, + outputState.framebufferSpace.setOrientation(orientation); + LOG_FATAL_IF(outputState.framebufferSpace.getBoundsAsRect() == Rect::INVALID_RECT, "The framebuffer bounds are unknown."); - const auto scale = - getScale(outputState.displaySpace.bounds, outputState.framebufferSpace.bounds); - outputState.framebufferSpace.content = outputState.displaySpace.content.scale(scale.x, scale.y); + const auto scale = getScale(outputState.displaySpace.getBoundsAsRect(), + outputState.framebufferSpace.getBoundsAsRect()); + outputState.framebufferSpace.setContent( + outputState.displaySpace.getContent().scale(scale.x, scale.y)); // Compute layerStackSpace - outputState.layerStackSpace.content = layerStackSpaceRect; - outputState.layerStackSpace.bounds = layerStackSpaceRect; + outputState.layerStackSpace.setContent(layerStackSpaceRect); + outputState.layerStackSpace.setBounds( + ui::Size(layerStackSpaceRect.getWidth(), layerStackSpaceRect.getHeight())); outputState.transform = outputState.layerStackSpace.getTransform(outputState.displaySpace); outputState.needsFiltering = outputState.transform.needsBilinearFiltering(); @@ -200,21 +202,21 @@ void Output::setDisplaySize(const ui::Size& size) { auto& state = editState(); // Update framebuffer space - const Rect newBounds(size); - state.framebufferSpace.bounds = newBounds; + const ui::Size newBounds(size); + state.framebufferSpace.setBounds(newBounds); // Update display space - state.displaySpace.bounds = newBounds; + state.displaySpace.setBounds(newBounds); state.transform = state.layerStackSpace.getTransform(state.displaySpace); // Update oriented display space - const auto orientation = state.displaySpace.orientation; + const auto orientation = state.displaySpace.getOrientation(); ui::Size orientedSize = size; if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) { std::swap(orientedSize.width, orientedSize.height); } - const Rect newOrientedBounds(orientedSize); - state.orientedDisplaySpace.bounds = newOrientedBounds; + const ui::Size newOrientedBounds(orientedSize); + state.orientedDisplaySpace.setBounds(newOrientedBounds); if (mPlanner) { mPlanner->setDisplaySize(size); @@ -227,11 +229,8 @@ ui::Transform::RotationFlags Output::getTransformHint() const { return static_cast<ui::Transform::RotationFlags>(getState().transform.getOrientation()); } -void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) { - auto& outputState = editState(); - outputState.layerStackId = layerStackId; - outputState.layerStackInternal = isInternal; - +void Output::setLayerFilter(ui::LayerFilter filter) { + editState().layerFilter = filter; dirtyEntireOutput(); } @@ -352,7 +351,7 @@ compositionengine::RenderSurface* Output::getRenderSurface() const { void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) { mRenderSurface = std::move(surface); const auto size = mRenderSurface->getSize(); - editState().framebufferSpace.bounds = Rect(size); + editState().framebufferSpace.setBounds(size); if (mPlanner) { mPlanner->setDisplaySize(size); } @@ -371,26 +370,18 @@ void Output::setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSu mRenderSurface = std::move(surface); } -Region Output::getDirtyRegion(bool repaintEverything) const { +Region Output::getDirtyRegion() const { const auto& outputState = getState(); - Region dirty(outputState.layerStackSpace.content); - if (!repaintEverything) { - dirty.andSelf(outputState.dirtyRegion); - } - return dirty; + return outputState.dirtyRegion.intersect(outputState.layerStackSpace.getContent()); } -bool Output::belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const { - // The layerStackId's must match, and also the layer must not be internal - // only when not on an internal output. - const auto& outputState = getState(); - return layerStackId && (*layerStackId == outputState.layerStackId) && - (!internalOnly || outputState.layerStackInternal); +bool Output::includesLayer(ui::LayerFilter filter) const { + return getState().layerFilter.includes(filter); } -bool Output::belongsInOutput(const sp<compositionengine::LayerFE>& layerFE) const { +bool Output::includesLayer(const sp<LayerFE>& layerFE) const { const auto* layerFEState = layerFE->getCompositionState(); - return layerFEState && belongsInOutput(layerFEState->layerStackId, layerFEState->internalOnly); + return layerFEState && includesLayer(layerFEState->outputFilter); } std::unique_ptr<compositionengine::OutputLayer> Output::createOutputLayer( @@ -461,7 +452,7 @@ void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& // Compute the resulting coverage for this output, and store it for later const ui::Transform& tr = outputState.transform; - Region undefinedRegion{outputState.displaySpace.bounds}; + Region undefinedRegion{outputState.displaySpace.getBoundsAsRect()}; undefinedRegion.subtractSelf(tr.transform(coverage.aboveOpaqueLayers)); outputState.undefinedRegion = undefinedRegion; @@ -496,8 +487,8 @@ void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE, layerFE->prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry); } - // Only consider the layers on the given layer stack - if (!belongsInOutput(layerFE)) { + // Only consider the layers on this output + if (!includesLayer(layerFE)) { return; } @@ -658,7 +649,7 @@ void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE, // TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below) const auto& outputState = getState(); Region drawRegion(outputState.transform.transform(visibleNonTransparentRegion)); - drawRegion.andSelf(outputState.displaySpace.bounds); + drawRegion.andSelf(outputState.displaySpace.getBoundsAsRect()); if (drawRegion.isEmpty()) { return; } @@ -676,7 +667,7 @@ void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE, outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion; outputLayerState.coveredRegion = coveredRegion; outputLayerState.outputSpaceVisibleRegion = outputState.transform.transform( - visibleNonShadowRegion.intersect(outputState.layerStackSpace.content)); + visibleNonShadowRegion.intersect(outputState.layerStackSpace.getContent())); outputLayerState.shadowRegion = shadowRegion; } @@ -911,7 +902,7 @@ compositionengine::Output::ColorProfile Output::pickColorProfile( void Output::beginFrame() { auto& outputState = editState(); - const bool dirty = !getDirtyRegion(false).isEmpty(); + const bool dirty = !getDirtyRegion().isEmpty(); const bool empty = getOutputLayerCount() == 0; const bool wasEmpty = !outputState.lastCompositionHadVisibleLayers; @@ -963,14 +954,9 @@ void Output::devOptRepaintFlash(const compositionengine::CompositionRefreshArgs& } if (getState().isEnabled) { - // transform the dirty region into this screen's coordinate space - const Region dirtyRegion = getDirtyRegion(refreshArgs.repaintEverything); - if (!dirtyRegion.isEmpty()) { - base::unique_fd readyFence; - // redraw the whole screen + if (const auto dirtyRegion = getDirtyRegion(); !dirtyRegion.isEmpty()) { static_cast<void>(composeSurfaces(dirtyRegion, refreshArgs)); - - mRenderSurface->queueBuffer(std::move(readyFence)); + mRenderSurface->queueBuffer(base::unique_fd()); } } @@ -1049,19 +1035,18 @@ std::optional<base::unique_fd> Output::composeSurfaces( } } - base::unique_fd readyFence; if (!hasClientComposition) { setExpensiveRenderingExpected(false); - return readyFence; + return base::unique_fd(); } ALOGV("hasClientComposition"); renderengine::DisplaySettings clientCompositionDisplay; - clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.content; - clientCompositionDisplay.clip = outputState.layerStackSpace.content; + clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.getContent(); + clientCompositionDisplay.clip = outputState.layerStackSpace.getContent(); clientCompositionDisplay.orientation = - ui::Transform::toRotationFlags(outputState.displaySpace.orientation); + ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation()); clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut() ? outputState.dataspace : ui::Dataspace::UNKNOWN; @@ -1078,13 +1063,9 @@ std::optional<base::unique_fd> Output::composeSurfaces( clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix; } - // Note: Updated by generateClientCompositionRequests - clientCompositionDisplay.clearRegion = Region::INVALID_REGION; - // Generate the client composition requests for the layers on this output. std::vector<LayerFE::LayerSettings> clientCompositionLayers = generateClientCompositionRequests(supportsProtectedContent, - clientCompositionDisplay.clearRegion, clientCompositionDisplay.outputDataspace); appendRegionFlashRequests(debugRegion, clientCompositionLayers); @@ -1096,7 +1077,7 @@ std::optional<base::unique_fd> Output::composeSurfaces( clientCompositionLayers)) { outputCompositionState.reusedClientComposition = true; setExpensiveRenderingExpected(false); - return readyFence; + return base::unique_fd(); } mClientCompositionRequestCache->add(tex->getBuffer()->getId(), clientCompositionDisplay, clientCompositionLayers); @@ -1129,10 +1110,12 @@ std::optional<base::unique_fd> Output::composeSurfaces( // bounds its framebuffer cache but Skia RenderEngine has no current policy. The best fix is // probably to encapsulate the output buffer into a structure that dispatches resource cleanup // over to RenderEngine, in which case this flag can be removed from the drawLayers interface. - const bool useFramebufferCache = outputState.layerStackInternal; - status_t status = - renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, tex, - useFramebufferCache, std::move(fd), &readyFence); + const bool useFramebufferCache = outputState.layerFilter.toInternalDisplay; + auto [status, drawFence] = + renderEngine + .drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, tex, + useFramebufferCache, std::move(fd)) + .get(); if (status != NO_ERROR && mClientCompositionRequestCache) { // If rendering was not successful, remove the request from the cache. @@ -1140,27 +1123,25 @@ std::optional<base::unique_fd> Output::composeSurfaces( } auto& timeStats = getCompositionEngine().getTimeStats(); - if (readyFence.get() < 0) { + if (drawFence.get() < 0) { timeStats.recordRenderEngineDuration(renderEngineStart, systemTime()); } else { timeStats.recordRenderEngineDuration(renderEngineStart, std::make_shared<FenceTime>( - new Fence(dup(readyFence.get())))); + new Fence(dup(drawFence.get())))); } - return readyFence; + return std::move(drawFence); } std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( - bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) { + bool supportsProtectedContent, ui::Dataspace outputDataspace) { std::vector<LayerFE::LayerSettings> clientCompositionLayers; ALOGV("Rendering client layers"); const auto& outputState = getState(); - const Region viewportRegion(outputState.layerStackSpace.content); + const Region viewportRegion(outputState.layerStackSpace.getContent()); bool firstLayer = true; - // Used when a layer clears part of the buffer. - Region stubRegion; bool disableBlurs = false; sp<GraphicBuffer> previousOverrideBuffer = nullptr; @@ -1222,8 +1203,7 @@ std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( outputState.needsFiltering, .isSecure = outputState.isSecure, .supportsProtectedContent = supportsProtectedContent, - .clearRegion = clientComposition ? clearRegion : stubRegion, - .viewport = outputState.layerStackSpace.content, + .viewport = outputState.layerStackSpace.getContent(), .dataspace = outputDataspace, .realContentIsVisible = realContentIsVisible, .clearContent = !clientComposition, @@ -1330,7 +1310,7 @@ void Output::renderCachedSets(const CompositionRefreshArgs& refreshArgs) { void Output::dirtyEntireOutput() { auto& outputState = editState(); - outputState.dirtyRegion.set(outputState.displaySpace.bounds); + outputState.dirtyRegion.set(outputState.displaySpace.getBoundsAsRect()); } void Output::chooseCompositionStrategy() { diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp index ee30ad8583..acc92162ac 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp @@ -28,9 +28,7 @@ void OutputCompositionState::dump(std::string& out) const { dumpVal(out, "usesDeviceComposition", usesDeviceComposition); dumpVal(out, "flipClientTarget", flipClientTarget); dumpVal(out, "reusedClientComposition", reusedClientComposition); - - dumpVal(out, "layerStack", layerStackId); - dumpVal(out, "layerStackInternal", layerStackInternal); + dumpVal(out, "layerFilter", layerFilter); out.append("\n "); diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 56e9d27f14..ecfd9018d3 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -79,7 +79,7 @@ Rect OutputLayer::calculateInitialCrop() const { FloatRect activeCropFloat = reduce(layerState.geomLayerBounds, layerState.transparentRegionHint); - const Rect& viewport = getOutput().getState().layerStackSpace.content; + const Rect& viewport = getOutput().getState().layerStackSpace.getContent(); const ui::Transform& layerTransform = layerState.geomLayerTransform; const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform; // Transform to screen space. @@ -136,7 +136,7 @@ FloatRect OutputLayer::calculateOutputSourceCrop() const { * buffer */ uint32_t invTransformOrient = - ui::Transform::toRotationFlags(outputState.displaySpace.orientation); + ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation()); // calculate the inverse transform if (invTransformOrient & HAL_TRANSFORM_ROT_90) { invTransformOrient ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H; @@ -192,7 +192,7 @@ Rect OutputLayer::calculateOutputDisplayFrame() const { Rect activeCrop = layerState.geomCrop; if (!activeCrop.isEmpty() && bufferSize.isValid()) { activeCrop = layerTransform.transform(activeCrop); - if (!activeCrop.intersect(outputState.layerStackSpace.content, &activeCrop)) { + if (!activeCrop.intersect(outputState.layerStackSpace.getContent(), &activeCrop)) { activeCrop.clear(); } activeCrop = inverseLayerTransform.transform(activeCrop, true); @@ -228,7 +228,7 @@ Rect OutputLayer::calculateOutputDisplayFrame() const { geomLayerBounds.bottom += outset; } Rect frame{layerTransform.transform(reduce(geomLayerBounds, activeTransparentRegion))}; - if (!frame.intersect(outputState.layerStackSpace.content, &frame)) { + if (!frame.intersect(outputState.layerStackSpace.getContent(), &frame)) { frame.clear(); } const ui::Transform displayTransform{outputState.transform}; @@ -628,7 +628,7 @@ void OutputLayer::writeCursorPositionToHWC() const { const auto& outputState = getOutput().getState(); Rect frame = layerFEState->cursorFrame; - frame.intersect(outputState.layerStackSpace.content, &frame); + frame.intersect(outputState.layerStackSpace.getContent(), &frame); Rect position = outputState.transform.transform(frame); if (auto error = hwcLayer->setCursorPosition(position.left, position.top); diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp index c1cd5ab5fd..e6b716e8f2 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp @@ -159,25 +159,23 @@ void CachedSet::updateAge(std::chrono::steady_clock::time_point now) { void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& texturePool, const OutputCompositionState& outputState) { ATRACE_CALL(); - const Rect& viewport = outputState.layerStackSpace.content; + const Rect& viewport = outputState.layerStackSpace.getContent(); const ui::Dataspace& outputDataspace = outputState.dataspace; const ui::Transform::RotationFlags orientation = - ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation); + ui::Transform::toRotationFlags(outputState.framebufferSpace.getOrientation()); renderengine::DisplaySettings displaySettings{ - .physicalDisplay = outputState.framebufferSpace.content, + .physicalDisplay = outputState.framebufferSpace.getContent(), .clip = viewport, .outputDataspace = outputDataspace, .orientation = orientation, }; - Region clearRegion = Region::INVALID_REGION; LayerFE::ClientCompositionTargetSettings targetSettings{ .clip = Region(viewport), .needsFiltering = false, .isSecure = outputState.isSecure, .supportsProtectedContent = false, - .clearRegion = clearRegion, .viewport = viewport, .dataspace = outputDataspace, .realContentIsVisible = true, @@ -274,17 +272,17 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& te bufferFence.reset(texture->getReadyFence()->dup()); } - base::unique_fd drawFence; - status_t result = - renderEngine.drawLayers(displaySettings, layerSettingsPointers, texture->get(), false, - std::move(bufferFence), &drawFence); + auto [status, drawFence] = renderEngine + .drawLayers(displaySettings, layerSettingsPointers, + texture->get(), false, std::move(bufferFence)) + .get(); - if (result == NO_ERROR) { + if (status == NO_ERROR) { mDrawFence = new Fence(drawFence.release()); mOutputSpace = outputState.framebufferSpace; mTexture = texture; mTexture->setReadyFence(mDrawFence); - mOutputSpace.orientation = outputState.framebufferSpace.orientation; + mOutputSpace.setOrientation(outputState.framebufferSpace.getOrientation()); mOutputDataspace = outputDataspace; mOrientation = orientation; mSkipCount = 0; diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp index 936dba3b29..2532e3df5d 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp @@ -93,11 +93,7 @@ Flags<LayerStateField> LayerState::getDifferingFields(const LayerState& other) c void LayerState::dump(std::string& result) const { for (const StateInterface* field : getNonUniqueFields()) { - if (auto viewOpt = flag_name(field->getField()); viewOpt) { - base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str()); - } else { - result.append("<UNKNOWN FIELD>:\n"); - } + base::StringAppendF(&result, " %16s: ", ftl::flag_string(field->getField()).c_str()); bool first = true; for (const std::string& line : field->toStrings()) { @@ -126,11 +122,7 @@ std::optional<std::string> LayerState::compare(const LayerState& other) const { continue; } - if (auto viewOpt = flag_name(thisField->getField()); viewOpt) { - base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str()); - } else { - result.append("<UNKNOWN FIELD>:\n"); - } + base::StringAppendF(&result, " %16s: ", ftl::flag_string(thisField->getField()).c_str()); const auto& thisStrings = thisField->toStrings(); const auto& otherStrings = otherField->toStrings(); diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index c037cc6173..ed235b8b57 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -60,8 +60,7 @@ constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(123 constexpr HalVirtualDisplayId HAL_VIRTUAL_DISPLAY_ID{456u}; constexpr GpuVirtualDisplayId GPU_VIRTUAL_DISPLAY_ID{789u}; -const ui::Size DEFAULT_RESOLUTION{1920, 1080}; -constexpr uint32_t DEFAULT_LAYER_STACK = 42; +constexpr ui::Size DEFAULT_RESOLUTION{1920, 1080}; struct Layer { Layer() { @@ -161,13 +160,11 @@ struct DisplayTestCommon : public testing::Test { EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); } - DisplayCreationArgs getDisplayCreationArgsForPhysicalHWCDisplay() { + DisplayCreationArgs getDisplayCreationArgsForPhysicalDisplay() { return DisplayCreationArgsBuilder() .setId(DEFAULT_DISPLAY_ID) - .setConnectionType(ui::DisplayConnectionType::Internal) .setPixels(DEFAULT_RESOLUTION) .setIsSecure(true) - .setLayerStackId(DEFAULT_LAYER_STACK) .setPowerAdvisor(&mPowerAdvisor) .build(); } @@ -177,7 +174,6 @@ struct DisplayTestCommon : public testing::Test { .setId(GPU_VIRTUAL_DISPLAY_ID) .setPixels(DEFAULT_RESOLUTION) .setIsSecure(false) - .setLayerStackId(DEFAULT_LAYER_STACK) .setPowerAdvisor(&mPowerAdvisor) .build(); } @@ -193,14 +189,13 @@ struct PartialMockDisplayTestCommon : public DisplayTestCommon { using Display = DisplayTestCommon::PartialMockDisplay; std::shared_ptr<Display> mDisplay = createPartialMockDisplay<Display>(mCompositionEngine, - getDisplayCreationArgsForPhysicalHWCDisplay()); + getDisplayCreationArgsForPhysicalDisplay()); }; struct FullDisplayImplTestCommon : public DisplayTestCommon { using Display = DisplayTestCommon::FullImplDisplay; std::shared_ptr<Display> mDisplay = - createDisplay<Display>(mCompositionEngine, - getDisplayCreationArgsForPhysicalHWCDisplay()); + createDisplay<Display>(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay()); }; struct DisplayWithLayersTestCommon : public FullDisplayImplTestCommon { @@ -218,8 +213,7 @@ struct DisplayWithLayersTestCommon : public FullDisplayImplTestCommon { LayerNoHWC2Layer mLayer3; StrictMock<HWC2::mock::Layer> hwc2LayerUnknown; std::shared_ptr<Display> mDisplay = - createDisplay<Display>(mCompositionEngine, - getDisplayCreationArgsForPhysicalHWCDisplay()); + createDisplay<Display>(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay()); }; /* @@ -232,7 +226,7 @@ struct DisplayCreationTest : public DisplayTestCommon { TEST_F(DisplayCreationTest, createPhysicalInternalDisplay) { auto display = - impl::createDisplay(mCompositionEngine, getDisplayCreationArgsForPhysicalHWCDisplay()); + impl::createDisplay(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay()); EXPECT_TRUE(display->isSecure()); EXPECT_FALSE(display->isVirtual()); EXPECT_EQ(DEFAULT_DISPLAY_ID, display->getId()); @@ -252,13 +246,11 @@ TEST_F(DisplayCreationTest, createGpuVirtualDisplay) { using DisplaySetConfigurationTest = PartialMockDisplayTestCommon; -TEST_F(DisplaySetConfigurationTest, configuresInternalSecurePhysicalDisplay) { +TEST_F(DisplaySetConfigurationTest, configuresPhysicalDisplay) { mDisplay->setConfiguration(DisplayCreationArgsBuilder() .setId(DEFAULT_DISPLAY_ID) - .setConnectionType(ui::DisplayConnectionType::Internal) .setPixels(DEFAULT_RESOLUTION) .setIsSecure(true) - .setLayerStackId(DEFAULT_LAYER_STACK) .setPowerAdvisor(&mPowerAdvisor) .setName(getDisplayNameFromCurrentTest()) .build()); @@ -266,28 +258,11 @@ TEST_F(DisplaySetConfigurationTest, configuresInternalSecurePhysicalDisplay) { EXPECT_EQ(DEFAULT_DISPLAY_ID, mDisplay->getId()); EXPECT_TRUE(mDisplay->isSecure()); EXPECT_FALSE(mDisplay->isVirtual()); - EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId); - EXPECT_TRUE(mDisplay->getState().layerStackInternal); EXPECT_FALSE(mDisplay->isValid()); -} - -TEST_F(DisplaySetConfigurationTest, configuresExternalInsecurePhysicalDisplay) { - mDisplay->setConfiguration(DisplayCreationArgsBuilder() - .setId(DEFAULT_DISPLAY_ID) - .setConnectionType(ui::DisplayConnectionType::External) - .setPixels(DEFAULT_RESOLUTION) - .setIsSecure(false) - .setLayerStackId(DEFAULT_LAYER_STACK) - .setPowerAdvisor(&mPowerAdvisor) - .setName(getDisplayNameFromCurrentTest()) - .build()); - EXPECT_EQ(DEFAULT_DISPLAY_ID, mDisplay->getId()); - EXPECT_FALSE(mDisplay->isSecure()); - EXPECT_FALSE(mDisplay->isVirtual()); - EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId); - EXPECT_FALSE(mDisplay->getState().layerStackInternal); - EXPECT_FALSE(mDisplay->isValid()); + const auto& filter = mDisplay->getState().layerFilter; + EXPECT_EQ(ui::INVALID_LAYER_STACK, filter.layerStack); + EXPECT_FALSE(filter.toInternalDisplay); } TEST_F(DisplaySetConfigurationTest, configuresHalVirtualDisplay) { @@ -295,7 +270,6 @@ TEST_F(DisplaySetConfigurationTest, configuresHalVirtualDisplay) { .setId(HAL_VIRTUAL_DISPLAY_ID) .setPixels(DEFAULT_RESOLUTION) .setIsSecure(false) - .setLayerStackId(DEFAULT_LAYER_STACK) .setPowerAdvisor(&mPowerAdvisor) .setName(getDisplayNameFromCurrentTest()) .build()); @@ -303,9 +277,11 @@ TEST_F(DisplaySetConfigurationTest, configuresHalVirtualDisplay) { EXPECT_EQ(HAL_VIRTUAL_DISPLAY_ID, mDisplay->getId()); EXPECT_FALSE(mDisplay->isSecure()); EXPECT_TRUE(mDisplay->isVirtual()); - EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId); - EXPECT_FALSE(mDisplay->getState().layerStackInternal); EXPECT_FALSE(mDisplay->isValid()); + + const auto& filter = mDisplay->getState().layerFilter; + EXPECT_EQ(ui::INVALID_LAYER_STACK, filter.layerStack); + EXPECT_FALSE(filter.toInternalDisplay); } TEST_F(DisplaySetConfigurationTest, configuresGpuVirtualDisplay) { @@ -313,7 +289,6 @@ TEST_F(DisplaySetConfigurationTest, configuresGpuVirtualDisplay) { .setId(GPU_VIRTUAL_DISPLAY_ID) .setPixels(DEFAULT_RESOLUTION) .setIsSecure(false) - .setLayerStackId(DEFAULT_LAYER_STACK) .setPowerAdvisor(&mPowerAdvisor) .setName(getDisplayNameFromCurrentTest()) .build()); @@ -321,9 +296,11 @@ TEST_F(DisplaySetConfigurationTest, configuresGpuVirtualDisplay) { EXPECT_EQ(GPU_VIRTUAL_DISPLAY_ID, mDisplay->getId()); EXPECT_FALSE(mDisplay->isSecure()); EXPECT_TRUE(mDisplay->isVirtual()); - EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId); - EXPECT_FALSE(mDisplay->getState().layerStackInternal); EXPECT_FALSE(mDisplay->isValid()); + + const auto& filter = mDisplay->getState().layerFilter; + EXPECT_EQ(ui::INVALID_LAYER_STACK, filter.layerStack); + EXPECT_FALSE(filter.toInternalDisplay); } /* @@ -899,13 +876,10 @@ TEST_F(DisplayFinishFrameTest, doesNotSkipCompositionIfNotDirtyOnHwcDisplay) { mDisplay->editState().isEnabled = true; mDisplay->editState().usesClientComposition = false; - mDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1); + mDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1)); mDisplay->editState().dirtyRegion = Region::INVALID_REGION; - CompositionRefreshArgs refreshArgs; - refreshArgs.repaintEverything = false; - - mDisplay->finishFrame(refreshArgs); + mDisplay->finishFrame({}); } TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) { @@ -920,13 +894,10 @@ TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) { gpuDisplay->editState().isEnabled = true; gpuDisplay->editState().usesClientComposition = false; - gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1); + gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1)); gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION; - CompositionRefreshArgs refreshArgs; - refreshArgs.repaintEverything = false; - - gpuDisplay->finishFrame(refreshArgs); + gpuDisplay->finishFrame({}); } TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) { @@ -941,34 +912,9 @@ TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) { gpuDisplay->editState().isEnabled = true; gpuDisplay->editState().usesClientComposition = false; - gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1); + gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1)); gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1)); - - CompositionRefreshArgs refreshArgs; - refreshArgs.repaintEverything = false; - - gpuDisplay->finishFrame(refreshArgs); -} - -TEST_F(DisplayFinishFrameTest, performsCompositionIfRepaintEverything) { - auto args = getDisplayCreationArgsForGpuVirtualDisplay(); - std::shared_ptr<impl::Display> gpuDisplay = impl::createDisplay(mCompositionEngine, args); - - mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>(); - gpuDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface)); - - // We expect a single call to queueBuffer when composition is not skipped. - EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1); - - gpuDisplay->editState().isEnabled = true; - gpuDisplay->editState().usesClientComposition = false; - gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1); - gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION; - - CompositionRefreshArgs refreshArgs; - refreshArgs.repaintEverything = true; - - gpuDisplay->finishFrame(refreshArgs); + gpuDisplay->finishFrame({}); } /* @@ -998,10 +944,8 @@ struct DisplayFunctionalTest : public testing::Test { Display>(mCompositionEngine, DisplayCreationArgsBuilder() .setId(DEFAULT_DISPLAY_ID) - .setConnectionType(ui::DisplayConnectionType::Internal) .setPixels(DEFAULT_RESOLUTION) .setIsSecure(true) - .setLayerStackId(DEFAULT_LAYER_STACK) .setPowerAdvisor(&mPowerAdvisor) .build()); diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index ada5adfa12..224dad1647 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -49,6 +49,7 @@ public: MOCK_CONST_METHOD0(getMaxVirtualDisplayDimension, size_t()); MOCK_METHOD3(allocateVirtualDisplay, bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*)); MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId)); + MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId)); MOCK_METHOD5(getDeviceCompositionChanges, status_t(HalDisplayId, bool, std::chrono::steady_clock::time_point, @@ -110,11 +111,15 @@ public: MOCK_CONST_METHOD1(dump, void(std::string&)); MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*()); - MOCK_CONST_METHOD1(getHwcDisplayId, std::optional<hal::HWDisplayId>(int32_t)); - MOCK_CONST_METHOD0(getInternalHwcDisplayId, std::optional<hal::HWDisplayId>()); - MOCK_CONST_METHOD0(getExternalHwcDisplayId, std::optional<hal::HWDisplayId>()); - MOCK_CONST_METHOD1(toPhysicalDisplayId, std::optional<PhysicalDisplayId>(hal::HWDisplayId)); - MOCK_CONST_METHOD1(fromPhysicalDisplayId, std::optional<hal::HWDisplayId>(PhysicalDisplayId)); + + MOCK_METHOD(hal::HWDisplayId, getPrimaryHwcDisplayId, (), (const, override)); + MOCK_METHOD(PhysicalDisplayId, getPrimaryDisplayId, (), (const, override)); + MOCK_METHOD(bool, isHeadless, (), (const, override)); + + MOCK_METHOD(std::optional<PhysicalDisplayId>, toPhysicalDisplayId, (hal::HWDisplayId), + (const, override)); + MOCK_METHOD(std::optional<hal::HWDisplayId>, fromPhysicalDisplayId, (PhysicalDisplayId), + (const, override)); }; } // namespace mock diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index c8c6012ef9..53b71b7d94 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -158,7 +158,7 @@ struct OutputLayerSourceCropTest : public OutputLayerTest { mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080}; mLayerFEState.geomBufferTransform = TR_IDENT; - mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080}; + mOutputState.layerStackSpace.setContent(Rect{0, 0, 1920, 1080}); } FloatRect calculateOutputSourceCrop() { @@ -229,7 +229,7 @@ TEST_F(OutputLayerSourceCropTest, calculateOutputSourceCropWorksWithATransformed mLayerFEState.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay; mLayerFEState.geomBufferTransform = entry.buffer; - mOutputState.displaySpace.orientation = toRotation(entry.display); + mOutputState.displaySpace.setOrientation(toRotation(entry.display)); EXPECT_THAT(calculateOutputSourceCrop(), entry.expected) << "entry " << i; } @@ -243,7 +243,7 @@ TEST_F(OutputLayerSourceCropTest, geomContentCropAffectsCrop) { } TEST_F(OutputLayerSourceCropTest, viewportAffectsCrop) { - mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540}; + mOutputState.layerStackSpace.setContent(Rect{0, 0, 960, 540}); const FloatRect expected{0.f, 0.f, 960.f, 540.f}; EXPECT_THAT(calculateOutputSourceCrop(), expected); @@ -265,7 +265,7 @@ struct OutputLayerDisplayFrameTest : public OutputLayerTest { mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080}; mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f}; - mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080}; + mOutputState.layerStackSpace.setContent(Rect{0, 0, 1920, 1080}); mOutputState.transform = ui::Transform{TR_IDENT}; } @@ -313,7 +313,7 @@ TEST_F(OutputLayerDisplayFrameTest, geomLayerBoundsAffectsFrame) { } TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) { - mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540}; + mOutputState.layerStackSpace.setContent(Rect{0, 0, 960, 540}); const Rect expected{0, 0, 960, 540}; EXPECT_THAT(calculateOutputDisplayFrame(), expected); } @@ -399,7 +399,7 @@ TEST_F(OutputLayerTest, calculateOutputRelativeBufferTransformTestsNeeded) { mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080); mLayerFEState.geomBufferTransform = entry.buffer; - mOutputState.displaySpace.orientation = toRotation(entry.display); + mOutputState.displaySpace.setOrientation(toRotation(entry.display)); mOutputState.transform = ui::Transform{entry.display}; const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.display); @@ -511,7 +511,7 @@ TEST_F(OutputLayerTest, mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080); mLayerFEState.geomBufferTransform = entry.buffer; - mOutputState.displaySpace.orientation = toRotation(entry.display); + mOutputState.displaySpace.setOrientation(toRotation(entry.display)); mOutputState.transform = ui::Transform{entry.display}; const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.internal); @@ -942,7 +942,7 @@ TEST_F(OutputLayerTest, displayInstallOrientationBufferTransformSetTo90) { // This test simulates a scenario where displayInstallOrientation is set to // ROT_90. This only has an effect on the transform; orientation stays 0 (see // DisplayDevice::setProjection). - mOutputState.displaySpace.orientation = ui::ROTATION_0; + mOutputState.displaySpace.setOrientation(ui::ROTATION_0); mOutputState.transform = ui::Transform{TR_ROT_90}; // Buffers are pre-rotated based on the transform hint (ROT_90); their // geomBufferTransform is set to the inverse transform. @@ -1237,7 +1237,7 @@ struct OutputLayerWriteCursorPositionToHWCTest : public OutputLayerTest { mLayerFEState.cursorFrame = kDefaultCursorFrame; - mOutputState.layerStackSpace.content = kDefaultDisplayViewport; + mOutputState.layerStackSpace.setContent(kDefaultDisplayViewport); mOutputState.transform = ui::Transform{kDefaultTransform}; } diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 09f5a5e51d..bec747908f 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -35,6 +35,7 @@ #include "CallOrderStateMachineHelper.h" #include "MockHWC2.h" #include "RegionMatcher.h" +#include "TestUtils.h" #include "renderengine/ExternalTexture.h" namespace android::compositionengine { @@ -142,7 +143,8 @@ struct OutputTest : public testing::Test { std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile)); mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface)); - mOutput->editState().displaySpace.bounds = kDefaultDisplaySize; + mOutput->editState().displaySpace.setBounds( + ui::Size(kDefaultDisplaySize.getWidth(), kDefaultDisplaySize.getHeight())); EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine)); } @@ -288,8 +290,10 @@ TEST_F(OutputTest, setLayerCachingEnabled_disablesCachingAndResetsOverrideInfo) TEST_F(OutputTest, setProjectionWorks) { const Rect displayRect{0, 0, 1000, 2000}; - mOutput->editState().displaySpace.bounds = displayRect; - mOutput->editState().framebufferSpace.bounds = displayRect; + mOutput->editState().displaySpace.setBounds( + ui::Size(displayRect.getWidth(), displayRect.getHeight())); + mOutput->editState().framebufferSpace.setBounds( + ui::Size(displayRect.getWidth(), displayRect.getHeight())); const ui::Rotation orientation = ui::ROTATION_90; const Rect frame{50, 60, 100, 100}; @@ -297,28 +301,29 @@ TEST_F(OutputTest, setProjectionWorks) { mOutput->setProjection(orientation, viewport, frame); - EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation); - EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content); - EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content); + EXPECT_EQ(orientation, mOutput->getState().displaySpace.getOrientation()); + EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.getContent()); + EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.getContent()); const auto state = mOutput->getState(); - EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation); - EXPECT_EQ(viewport, state.layerStackSpace.content); - EXPECT_EQ(viewport, state.layerStackSpace.bounds); + EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.getOrientation()); + EXPECT_EQ(viewport, state.layerStackSpace.getContent()); + EXPECT_EQ(Rect(0, 0, 20, 20), state.layerStackSpace.getBoundsAsRect()); - EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation); - EXPECT_EQ(frame, state.orientedDisplaySpace.content); - EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds); + EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.getOrientation()); + EXPECT_EQ(frame, state.orientedDisplaySpace.getContent()); + EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.getBoundsAsRect()); - EXPECT_EQ(displayRect, state.displaySpace.bounds); - EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content); - EXPECT_EQ(orientation, state.displaySpace.orientation); + EXPECT_EQ(displayRect, state.displaySpace.getBoundsAsRect()); + EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.getContent()); + EXPECT_EQ(orientation, state.displaySpace.getOrientation()); - EXPECT_EQ(displayRect, state.framebufferSpace.bounds); - EXPECT_EQ(Rect(900, 50, 940, 100), state.framebufferSpace.content); - EXPECT_EQ(orientation, state.framebufferSpace.orientation); + EXPECT_EQ(displayRect, state.framebufferSpace.getBoundsAsRect()); + EXPECT_EQ(Rect(900, 50, 940, 100), state.framebufferSpace.getContent()); + EXPECT_EQ(orientation, state.framebufferSpace.getOrientation()); - EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content)); + EXPECT_EQ(state.displaySpace.getContent(), + state.transform.transform(state.layerStackSpace.getContent())); EXPECT_EQ(ui::Transform::ROT_90, mOutput->getTransformHint()); } @@ -326,8 +331,10 @@ TEST_F(OutputTest, setProjectionWorks) { TEST_F(OutputTest, setProjectionWithSmallFramebufferWorks) { const Rect displayRect{0, 0, 1000, 2000}; const Rect framebufferRect{0, 0, 500, 1000}; - mOutput->editState().displaySpace.bounds = displayRect; - mOutput->editState().framebufferSpace.bounds = framebufferRect; + mOutput->editState().displaySpace.setBounds( + ui::Size(displayRect.getWidth(), displayRect.getHeight())); + mOutput->editState().framebufferSpace.setBounds( + ui::Size(framebufferRect.getWidth(), framebufferRect.getHeight())); const ui::Rotation orientation = ui::ROTATION_90; const Rect frame{50, 60, 100, 100}; @@ -335,28 +342,29 @@ TEST_F(OutputTest, setProjectionWithSmallFramebufferWorks) { mOutput->setProjection(orientation, viewport, frame); - EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation); - EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content); - EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content); + EXPECT_EQ(orientation, mOutput->getState().displaySpace.getOrientation()); + EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.getContent()); + EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.getContent()); const auto state = mOutput->getState(); - EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation); - EXPECT_EQ(viewport, state.layerStackSpace.content); - EXPECT_EQ(viewport, state.layerStackSpace.bounds); + EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.getOrientation()); + EXPECT_EQ(viewport, state.layerStackSpace.getContent()); + EXPECT_EQ(Rect(0, 0, 20, 20), state.layerStackSpace.getBoundsAsRect()); - EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation); - EXPECT_EQ(frame, state.orientedDisplaySpace.content); - EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds); + EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.getOrientation()); + EXPECT_EQ(frame, state.orientedDisplaySpace.getContent()); + EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.getBoundsAsRect()); - EXPECT_EQ(displayRect, state.displaySpace.bounds); - EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content); - EXPECT_EQ(orientation, state.displaySpace.orientation); + EXPECT_EQ(displayRect, state.displaySpace.getBoundsAsRect()); + EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.getContent()); + EXPECT_EQ(orientation, state.displaySpace.getOrientation()); - EXPECT_EQ(framebufferRect, state.framebufferSpace.bounds); - EXPECT_EQ(Rect(450, 25, 470, 50), state.framebufferSpace.content); - EXPECT_EQ(orientation, state.framebufferSpace.orientation); + EXPECT_EQ(framebufferRect, state.framebufferSpace.getBoundsAsRect()); + EXPECT_EQ(Rect(450, 25, 470, 50), state.framebufferSpace.getContent()); + EXPECT_EQ(orientation, state.framebufferSpace.getOrientation()); - EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content)); + EXPECT_EQ(state.displaySpace.getContent(), + state.transform.transform(state.layerStackSpace.getContent())); } /* @@ -364,16 +372,16 @@ TEST_F(OutputTest, setProjectionWithSmallFramebufferWorks) { */ TEST_F(OutputTest, setDisplaySpaceSizeUpdatesOutputStateAndDirtiesEntireOutput) { - mOutput->editState().layerStackSpace.content = Rect(0, 0, 2000, 1000); - mOutput->editState().layerStackSpace.bounds = Rect(0, 0, 2000, 1000); - mOutput->editState().orientedDisplaySpace.content = Rect(0, 0, 1800, 900); - mOutput->editState().orientedDisplaySpace.bounds = Rect(0, 0, 2000, 1000); - mOutput->editState().framebufferSpace.content = Rect(0, 0, 900, 1800); - mOutput->editState().framebufferSpace.bounds = Rect(0, 0, 1000, 2000); - mOutput->editState().framebufferSpace.orientation = ui::ROTATION_90; - mOutput->editState().displaySpace.content = Rect(0, 0, 900, 1800); - mOutput->editState().displaySpace.bounds = Rect(0, 0, 1000, 2000); - mOutput->editState().displaySpace.orientation = ui::ROTATION_90; + mOutput->editState().layerStackSpace.setContent(Rect(0, 0, 2000, 1000)); + mOutput->editState().layerStackSpace.setBounds(ui::Size(2000, 1000)); + mOutput->editState().orientedDisplaySpace.setContent(Rect(0, 0, 1800, 900)); + mOutput->editState().orientedDisplaySpace.setBounds(ui::Size(2000, 1000)); + mOutput->editState().framebufferSpace.setContent(Rect(0, 0, 900, 1800)); + mOutput->editState().framebufferSpace.setBounds(ui::Size(1000, 2000)); + mOutput->editState().framebufferSpace.setOrientation(ui::ROTATION_90); + mOutput->editState().displaySpace.setContent(Rect(0, 0, 900, 1800)); + mOutput->editState().displaySpace.setBounds(ui::Size(1000, 2000)); + mOutput->editState().displaySpace.setOrientation(ui::ROTATION_90); const ui::Size newDisplaySize{500, 1000}; @@ -384,36 +392,38 @@ TEST_F(OutputTest, setDisplaySpaceSizeUpdatesOutputStateAndDirtiesEntireOutput) const auto state = mOutput->getState(); const Rect displayRect(newDisplaySize); - EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation); - EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.content); - EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.bounds); + EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.getOrientation()); + EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.getContent()); + EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.getBoundsAsRect()); - EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation); - EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.bounds); + EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.getOrientation()); + EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.getBoundsAsRect()); - EXPECT_EQ(displayRect, state.displaySpace.bounds); - EXPECT_EQ(ui::ROTATION_90, state.displaySpace.orientation); + EXPECT_EQ(displayRect, state.displaySpace.getBoundsAsRect()); + EXPECT_EQ(ui::ROTATION_90, state.displaySpace.getOrientation()); - EXPECT_EQ(displayRect, state.framebufferSpace.bounds); - EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.orientation); + EXPECT_EQ(displayRect, state.framebufferSpace.getBoundsAsRect()); + EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.getOrientation()); - EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content)); + EXPECT_EQ(state.displaySpace.getContent(), + state.transform.transform(state.layerStackSpace.getContent())); EXPECT_THAT(state.dirtyRegion, RegionEq(Region(displayRect))); } /* - * Output::setLayerStackFilter() + * Output::setLayerFilter() */ -TEST_F(OutputTest, setLayerStackFilterSetsFilterAndDirtiesEntireOutput) { - const uint32_t layerStack = 123u; - mOutput->setLayerStackFilter(layerStack, true); +TEST_F(OutputTest, setLayerFilterSetsFilterAndDirtiesEntireOutput) { + constexpr ui::LayerFilter kFilter{ui::LayerStack{123u}, true}; + mOutput->setLayerFilter(kFilter); - EXPECT_TRUE(mOutput->getState().layerStackInternal); - EXPECT_EQ(layerStack, mOutput->getState().layerStackId); + const auto& state = mOutput->getState(); + EXPECT_EQ(kFilter.layerStack, state.layerFilter.layerStack); + EXPECT_TRUE(state.layerFilter.toInternalDisplay); - EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); + EXPECT_THAT(state.dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); } /* @@ -560,133 +570,107 @@ TEST_F(OutputTest, setRenderSurfaceResetsBounds) { mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface)); - EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.bounds); + EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.getBoundsAsRect()); } /* * Output::getDirtyRegion() */ -TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) { - const Rect viewport{100, 200}; - mOutput->editState().layerStackSpace.content = viewport; - mOutput->editState().dirtyRegion.set(50, 300); - - { - Region result = mOutput->getDirtyRegion(true); - - EXPECT_THAT(result, RegionEq(Region(viewport))); - } -} - -TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) { +TEST_F(OutputTest, getDirtyRegion) { const Rect viewport{100, 200}; - mOutput->editState().layerStackSpace.content = viewport; + mOutput->editState().layerStackSpace.setContent(viewport); mOutput->editState().dirtyRegion.set(50, 300); - { - Region result = mOutput->getDirtyRegion(false); - - // The dirtyRegion should be clipped to the display bounds. - EXPECT_THAT(result, RegionEq(Region(Rect(50, 200)))); - } + // The dirty region should be clipped to the display bounds. + EXPECT_THAT(mOutput->getDirtyRegion(), RegionEq(Region(Rect(50, 200)))); } /* - * Output::belongsInOutput() + * Output::includesLayer() */ -TEST_F(OutputTest, belongsInOutputFiltersAsExpected) { - const uint32_t layerStack1 = 123u; - const uint32_t layerStack2 = 456u; +TEST_F(OutputTest, layerFiltering) { + const ui::LayerStack layerStack1{123u}; + const ui::LayerStack layerStack2{456u}; - // If the output accepts layerStack1 and internal-only layers.... - mOutput->setLayerStackFilter(layerStack1, true); + // If the output is associated to layerStack1 and to an internal display... + mOutput->setLayerFilter({layerStack1, true}); - // A layer with no layerStack does not belong to it, internal-only or not. - EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, false)); - EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, true)); + // It excludes layers with no layer stack, internal-only or not. + EXPECT_FALSE(mOutput->includesLayer({ui::INVALID_LAYER_STACK, false})); + EXPECT_FALSE(mOutput->includesLayer({ui::INVALID_LAYER_STACK, true})); - // Any layer with layerStack1 belongs to it, internal-only or not. - EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, false)); - EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, true)); - EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, true)); - EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false)); + // It includes layers on layerStack1, internal-only or not. + EXPECT_TRUE(mOutput->includesLayer({layerStack1, false})); + EXPECT_TRUE(mOutput->includesLayer({layerStack1, true})); + EXPECT_FALSE(mOutput->includesLayer({layerStack2, true})); + EXPECT_FALSE(mOutput->includesLayer({layerStack2, false})); - // If the output accepts layerStack21 but not internal-only layers... - mOutput->setLayerStackFilter(layerStack1, false); + // If the output is associated to layerStack1 but not to an internal display... + mOutput->setLayerFilter({layerStack1, false}); - // Only non-internal layers with layerStack1 belong to it. - EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, false)); - EXPECT_FALSE(mOutput->belongsInOutput(layerStack1, true)); - EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, true)); - EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false)); + // It includes layers on layerStack1, unless they are internal-only. + EXPECT_TRUE(mOutput->includesLayer({layerStack1, false})); + EXPECT_FALSE(mOutput->includesLayer({layerStack1, true})); + EXPECT_FALSE(mOutput->includesLayer({layerStack2, true})); + EXPECT_FALSE(mOutput->includesLayer({layerStack2, false})); } -TEST_F(OutputTest, belongsInOutputHandlesLayerWithNoCompositionState) { +TEST_F(OutputTest, layerFilteringWithoutCompositionState) { NonInjectedLayer layer; sp<LayerFE> layerFE(layer.layerFE); - // If the layer has no composition state, it does not belong to any output. + // Layers without composition state are excluded. EXPECT_CALL(*layer.layerFE, getCompositionState).WillOnce(Return(nullptr)); - EXPECT_FALSE(mOutput->belongsInOutput(layerFE)); + EXPECT_FALSE(mOutput->includesLayer(layerFE)); } -TEST_F(OutputTest, belongsInOutputFiltersLayersAsExpected) { +TEST_F(OutputTest, layerFilteringWithCompositionState) { NonInjectedLayer layer; sp<LayerFE> layerFE(layer.layerFE); - const uint32_t layerStack1 = 123u; - const uint32_t layerStack2 = 456u; + const ui::LayerStack layerStack1{123u}; + const ui::LayerStack layerStack2{456u}; - // If the output accepts layerStack1 and internal-only layers.... - mOutput->setLayerStackFilter(layerStack1, true); + // If the output is associated to layerStack1 and to an internal display... + mOutput->setLayerFilter({layerStack1, true}); - // A layer with no layerStack does not belong to it, internal-only or not. - layer.layerFEState.layerStackId = std::nullopt; - layer.layerFEState.internalOnly = false; - EXPECT_FALSE(mOutput->belongsInOutput(layerFE)); + // It excludes layers with no layer stack, internal-only or not. + layer.layerFEState.outputFilter = {ui::INVALID_LAYER_STACK, false}; + EXPECT_FALSE(mOutput->includesLayer(layerFE)); - layer.layerFEState.layerStackId = std::nullopt; - layer.layerFEState.internalOnly = true; - EXPECT_FALSE(mOutput->belongsInOutput(layerFE)); + layer.layerFEState.outputFilter = {ui::INVALID_LAYER_STACK, true}; + EXPECT_FALSE(mOutput->includesLayer(layerFE)); - // Any layer with layerStack1 belongs to it, internal-only or not. - layer.layerFEState.layerStackId = layerStack1; - layer.layerFEState.internalOnly = false; - EXPECT_TRUE(mOutput->belongsInOutput(layerFE)); + // It includes layers on layerStack1, internal-only or not. + layer.layerFEState.outputFilter = {layerStack1, false}; + EXPECT_TRUE(mOutput->includesLayer(layerFE)); - layer.layerFEState.layerStackId = layerStack1; - layer.layerFEState.internalOnly = true; - EXPECT_TRUE(mOutput->belongsInOutput(layerFE)); + layer.layerFEState.outputFilter = {layerStack1, true}; + EXPECT_TRUE(mOutput->includesLayer(layerFE)); - layer.layerFEState.layerStackId = layerStack2; - layer.layerFEState.internalOnly = true; - EXPECT_FALSE(mOutput->belongsInOutput(layerFE)); + layer.layerFEState.outputFilter = {layerStack2, true}; + EXPECT_FALSE(mOutput->includesLayer(layerFE)); - layer.layerFEState.layerStackId = layerStack2; - layer.layerFEState.internalOnly = false; - EXPECT_FALSE(mOutput->belongsInOutput(layerFE)); + layer.layerFEState.outputFilter = {layerStack2, false}; + EXPECT_FALSE(mOutput->includesLayer(layerFE)); - // If the output accepts layerStack1 but not internal-only layers... - mOutput->setLayerStackFilter(layerStack1, false); + // If the output is associated to layerStack1 but not to an internal display... + mOutput->setLayerFilter({layerStack1, false}); - // Only non-internal layers with layerStack1 belong to it. - layer.layerFEState.layerStackId = layerStack1; - layer.layerFEState.internalOnly = false; - EXPECT_TRUE(mOutput->belongsInOutput(layerFE)); + // It includes layers on layerStack1, unless they are internal-only. + layer.layerFEState.outputFilter = {layerStack1, false}; + EXPECT_TRUE(mOutput->includesLayer(layerFE)); - layer.layerFEState.layerStackId = layerStack1; - layer.layerFEState.internalOnly = true; - EXPECT_FALSE(mOutput->belongsInOutput(layerFE)); + layer.layerFEState.outputFilter = {layerStack1, true}; + EXPECT_FALSE(mOutput->includesLayer(layerFE)); - layer.layerFEState.layerStackId = layerStack2; - layer.layerFEState.internalOnly = true; - EXPECT_FALSE(mOutput->belongsInOutput(layerFE)); + layer.layerFEState.outputFilter = {layerStack2, true}; + EXPECT_FALSE(mOutput->includesLayer(layerFE)); - layer.layerFEState.layerStackId = layerStack2; - layer.layerFEState.internalOnly = false; - EXPECT_FALSE(mOutput->belongsInOutput(layerFE)); + layer.layerFEState.outputFilter = {layerStack2, false}; + EXPECT_FALSE(mOutput->includesLayer(layerFE)); } /* @@ -1079,7 +1063,8 @@ struct OutputRebuildLayerStacksTest : public testing::Test { OutputRebuildLayerStacksTest() { mOutput.mState.isEnabled = true; mOutput.mState.transform = kIdentityTransform; - mOutput.mState.displaySpace.bounds = kOutputBounds; + mOutput.mState.displaySpace.setBounds( + ui::Size(kOutputBounds.getWidth(), kOutputBounds.getHeight())); mRefreshArgs.updatingOutputGeometryThisFrame = true; @@ -1268,21 +1253,22 @@ struct OutputEnsureOutputLayerIfVisibleTest : public testing::Test { struct OutputPartialMock : public OutputPartialMockBase { // Sets up the helper functions called by the function under test to use // mock implementations. - MOCK_CONST_METHOD1(belongsInOutput, bool(const sp<compositionengine::LayerFE>&)); + MOCK_METHOD(bool, includesLayer, (const sp<compositionengine::LayerFE>&), + (const, override)); MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, OutputLayer*(size_t)); MOCK_METHOD2(ensureOutputLayer, compositionengine::OutputLayer*(std::optional<size_t>, const sp<LayerFE>&)); }; OutputEnsureOutputLayerIfVisibleTest() { - EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE))) + EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))) .WillRepeatedly(Return(true)); EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)) .WillRepeatedly(Return(&mLayer.outputLayer)); - mOutput.mState.displaySpace.bounds = Rect(0, 0, 200, 300); - mOutput.mState.layerStackSpace.content = Rect(0, 0, 200, 300); + mOutput.mState.displaySpace.setBounds(ui::Size(200, 300)); + mOutput.mState.layerStackSpace.setContent(Rect(0, 0, 200, 300)); mOutput.mState.transform = ui::Transform(TR_IDENT, 200, 300); mLayer.layerFEState.isVisible = true; @@ -1326,8 +1312,8 @@ const Region OutputEnsureOutputLayerIfVisibleTest::kLowerHalfBoundsNoRotation = const Region OutputEnsureOutputLayerIfVisibleTest::kFullBounds90Rotation = Region(Rect(0, 0, 200, 100)); -TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerBelongs) { - EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false)); +TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) { + EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false)); EXPECT_CALL(*mLayer.layerFE, prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry)); @@ -1337,8 +1323,8 @@ TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLa } TEST_F(OutputEnsureOutputLayerIfVisibleTest, - skipsLatchIfAlreadyLatchedBeforeCheckingIfLayerBelongs) { - EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false)); + skipsLatchIfAlreadyLatchedBeforeCheckingIfLayerIncluded) { + EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false)); ensureOutputLayerIfVisible(); } @@ -1362,7 +1348,7 @@ TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerHasEmptyVisible } TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifDrawRegionEmpty) { - mOutput.mState.displaySpace.bounds = Rect(0, 0, 0, 0); + mOutput.mState.displaySpace.setBounds(ui::Size(0, 0)); ensureOutputLayerIfVisible(); } @@ -1559,7 +1545,7 @@ TEST_F(OutputEnsureOutputLayerIfVisibleTest, mLayer.layerFEState.contentDirty = true; mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200); - mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200); + mOutput.mState.layerStackSpace.setContent(Rect(0, 0, 300, 200)); mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300); EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); @@ -1585,7 +1571,7 @@ TEST_F(OutputEnsureOutputLayerIfVisibleTest, mLayer.layerFEState.contentDirty = true; mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200); - mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200); + mOutput.mState.layerStackSpace.setContent(Rect(0, 0, 300, 200)); mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300); EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE))) @@ -2530,7 +2516,7 @@ struct OutputBeginFrameTest : public ::testing::Test { struct OutputPartialMock : public OutputPartialMockBase { // Sets up the helper functions called by the function under test to use // mock implementations. - MOCK_CONST_METHOD1(getDirtyRegion, Region(bool)); + MOCK_METHOD(Region, getDirtyRegion, (), (const)); }; OutputBeginFrameTest() { @@ -2542,8 +2528,7 @@ struct OutputBeginFrameTest : public ::testing::Test { struct IfGetDirtyRegionExpectationState : public CallOrderStateMachineHelper<TestType, IfGetDirtyRegionExpectationState> { [[nodiscard]] auto ifGetDirtyRegionReturns(Region dirtyRegion) { - EXPECT_CALL(getInstance()->mOutput, getDirtyRegion(false)) - .WillOnce(Return(dirtyRegion)); + EXPECT_CALL(getInstance()->mOutput, getDirtyRegion()).WillOnce(Return(dirtyRegion)); return nextState<AndIfGetOutputLayerCountExpectationState>(); } }; @@ -2683,7 +2668,7 @@ struct OutputDevOptRepaintFlashTest : public testing::Test { struct OutputPartialMock : public OutputPartialMockBase { // Sets up the helper functions called by the function under test to use // mock implementations. - MOCK_CONST_METHOD1(getDirtyRegion, Region(bool)); + MOCK_METHOD(Region, getDirtyRegion, (), (const)); MOCK_METHOD2(composeSurfaces, std::optional<base::unique_fd>( const Region&, const compositionengine::CompositionRefreshArgs&)); @@ -2711,7 +2696,6 @@ const Region OutputDevOptRepaintFlashTest::kNotEmptyRegion{Rect{0, 0, 1, 1}}; TEST_F(OutputDevOptRepaintFlashTest, doesNothingIfFlashDelayNotSet) { mRefreshArgs.devOptFlashDirtyRegionsDelay = {}; - mRefreshArgs.repaintEverything = true; mOutput.mState.isEnabled = true; mOutput.devOptRepaintFlash(mRefreshArgs); @@ -2719,7 +2703,6 @@ TEST_F(OutputDevOptRepaintFlashTest, doesNothingIfFlashDelayNotSet) { TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotEnabled) { mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1); - mRefreshArgs.repaintEverything = true; mOutput.mState.isEnabled = false; InSequence seq; @@ -2729,13 +2712,12 @@ TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotEnabled) { mOutput.devOptRepaintFlash(mRefreshArgs); } -TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotDirty) { +TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfEnabled) { mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1); - mRefreshArgs.repaintEverything = true; mOutput.mState.isEnabled = true; InSequence seq; - EXPECT_CALL(mOutput, getDirtyRegion(true)).WillOnce(Return(kEmptyRegion)); + EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kEmptyRegion)); EXPECT_CALL(mOutput, postFramebuffer()); EXPECT_CALL(mOutput, prepareFrame()); @@ -2744,11 +2726,10 @@ TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotDirty) { TEST_F(OutputDevOptRepaintFlashTest, alsoComposesSurfacesAndQueuesABufferIfDirty) { mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1); - mRefreshArgs.repaintEverything = false; mOutput.mState.isEnabled = true; InSequence seq; - EXPECT_CALL(mOutput, getDirtyRegion(false)).WillOnce(Return(kNotEmptyRegion)); + EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kNotEmptyRegion)); EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs))); EXPECT_CALL(*mRenderSurface, queueBuffer(_)); EXPECT_CALL(mOutput, postFramebuffer()); @@ -2992,8 +2973,8 @@ struct OutputComposeSurfacesTest : public testing::Test { // Sets up the helper functions called by the function under test to use // mock implementations. MOCK_CONST_METHOD0(getSkipColorTransform, bool()); - MOCK_METHOD3(generateClientCompositionRequests, - std::vector<LayerFE::LayerSettings>(bool, Region&, ui::Dataspace)); + MOCK_METHOD2(generateClientCompositionRequests, + std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace)); MOCK_METHOD2(appendRegionFlashRequests, void(const Region&, std::vector<LayerFE::LayerSettings>&)); MOCK_METHOD1(setExpensiveRenderingExpected, void(bool)); @@ -3005,11 +2986,11 @@ struct OutputComposeSurfacesTest : public testing::Test { mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface)); mOutput.cacheClientCompositionRequests(MAX_CLIENT_COMPOSITION_CACHE_SIZE); - mOutput.mState.orientedDisplaySpace.content = kDefaultOutputFrame; - mOutput.mState.layerStackSpace.content = kDefaultOutputViewport; - mOutput.mState.framebufferSpace.content = kDefaultOutputDestinationClip; - mOutput.mState.displaySpace.content = kDefaultOutputDestinationClip; - mOutput.mState.displaySpace.orientation = kDefaultOutputOrientation; + mOutput.mState.orientedDisplaySpace.setContent(kDefaultOutputFrame); + mOutput.mState.layerStackSpace.setContent(kDefaultOutputViewport); + mOutput.mState.framebufferSpace.setContent(kDefaultOutputDestinationClip); + mOutput.mState.displaySpace.setContent(kDefaultOutputDestinationClip); + mOutput.mState.displaySpace.setOrientation(kDefaultOutputOrientation); mOutput.mState.transform = ui::Transform{kDefaultOutputOrientationFlags}; mOutput.mState.dataspace = kDefaultOutputDataspace; mOutput.mState.colorTransformMatrix = kDefaultColorTransformMat; @@ -3142,15 +3123,20 @@ TEST_F(OutputComposeSurfacesTest, handlesZeroCompositionRequests) { EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); - EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) + EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, false, _, _)) - .WillRepeatedly(Return(NO_ERROR)); - + EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, false, _)) + .WillRepeatedly([&](const renderengine::DisplaySettings&, + const std::vector<const renderengine::LayerSettings*>&, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd &&) + -> std::future<renderengine::RenderEngineResult> { + return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); + }); verify().execute().expectAFenceWasReturned(); } @@ -3165,7 +3151,7 @@ TEST_F(OutputComposeSurfacesTest, buildsAndRendersRequestList) { EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); - EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) + EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly( @@ -3175,8 +3161,14 @@ TEST_F(OutputComposeSurfacesTest, buildsAndRendersRequestList) { })); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _)) - .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _)) + .WillRepeatedly([&](const renderengine::DisplaySettings&, + const std::vector<const renderengine::LayerSettings*>&, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd &&) + -> std::future<renderengine::RenderEngineResult> { + return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); + }); verify().execute().expectAFenceWasReturned(); } @@ -3188,14 +3180,13 @@ TEST_F(OutputComposeSurfacesTest, r1.geometry.boundaries = FloatRect{1, 2, 3, 4}; r2.geometry.boundaries = FloatRect{5, 6, 7, 8}; - const constexpr uint32_t kInternalLayerStack = 1234; - mOutput.setLayerStackFilter(kInternalLayerStack, true); + mOutput.setLayerFilter({ui::LayerStack{1234u}, true}); EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); - EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) + EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly( @@ -3205,8 +3196,14 @@ TEST_F(OutputComposeSurfacesTest, })); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _)) - .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _)) + .WillRepeatedly([&](const renderengine::DisplaySettings&, + const std::vector<const renderengine::LayerSettings*>&, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd &&) + -> std::future<renderengine::RenderEngineResult> { + return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); + }); verify().execute().expectAFenceWasReturned(); } @@ -3223,15 +3220,18 @@ TEST_F(OutputComposeSurfacesTest, renderDuplicateClientCompositionRequestsWithou EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); - EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) + EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _)) .Times(2) - .WillOnce(Return(NO_ERROR)); + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); verify().execute().expectAFenceWasReturned(); EXPECT_FALSE(mOutput.mState.reusedClientComposition); @@ -3252,14 +3252,15 @@ TEST_F(OutputComposeSurfacesTest, skipDuplicateClientCompositionRequests) { EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); - EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) + EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _)) - .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false)); verify().execute().expectAFenceWasReturned(); @@ -3281,7 +3282,7 @@ TEST_F(OutputComposeSurfacesTest, clientCompositionIfBufferChanges) { EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); - EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) + EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); @@ -3293,8 +3294,14 @@ TEST_F(OutputComposeSurfacesTest, clientCompositionIfBufferChanges) { EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)) .WillOnce(Return(mOutputBuffer)) .WillOnce(Return(otherOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _)) - .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _)) + .WillRepeatedly([&](const renderengine::DisplaySettings&, + const std::vector<const renderengine::LayerSettings*>&, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd &&) + -> std::future<renderengine::RenderEngineResult> { + return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); + }); verify().execute().expectAFenceWasReturned(); EXPECT_FALSE(mOutput.mState.reusedClientComposition); @@ -3316,17 +3323,19 @@ TEST_F(OutputComposeSurfacesTest, clientCompositionIfRequestChanges) { EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); - EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) + EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace)) .WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r2})) .WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r3})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _)) - .WillOnce(Return(NO_ERROR)); - EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r3)), _, false, _, _)) - .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r3)), _, false, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); verify().execute().expectAFenceWasReturned(); EXPECT_FALSE(mOutput.mState.reusedClientComposition); @@ -3339,7 +3348,7 @@ struct OutputComposeSurfacesTest_UsesExpectedDisplaySettings : public OutputComp OutputComposeSurfacesTest_UsesExpectedDisplaySettings() { EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); - EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) + EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); @@ -3375,8 +3384,9 @@ struct OutputComposeSurfacesTest_UsesExpectedDisplaySettings : public OutputComp struct ExpectDisplaySettingsState : public CallOrderStateMachineHelper<TestType, ExpectDisplaySettingsState> { auto thenExpectDisplaySettingsUsed(renderengine::DisplaySettings settings) { - EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, false, _, _)) - .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, false, _)) + .WillOnce(Return(ByMove(futureOf<renderengine::RenderEngineResult>( + {NO_ERROR, base::unique_fd()})))); return nextState<ExecuteState>(); } }; @@ -3391,7 +3401,7 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrMixedComposi .andIfSkipColorTransform(false) .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(), - Region::INVALID_REGION, kDefaultOutputOrientationFlags}) + kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); } @@ -3402,7 +3412,7 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrMixedComp .andIfSkipColorTransform(false) .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(), - Region::INVALID_REGION, kDefaultOutputOrientationFlags}) + kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); } @@ -3413,7 +3423,7 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrOnlyClientCo .andIfSkipColorTransform(false) .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, - kDefaultColorTransformMat, Region::INVALID_REGION, + kDefaultColorTransformMat, kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); @@ -3425,7 +3435,7 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrOnlyClien .andIfSkipColorTransform(false) .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, - kDefaultColorTransformMat, Region::INVALID_REGION, + kDefaultColorTransformMat, kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); @@ -3438,7 +3448,7 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, .andIfSkipColorTransform(true) .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(), - Region::INVALID_REGION, kDefaultOutputOrientationFlags}) + kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); } @@ -3469,13 +3479,20 @@ struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeS EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); - EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, _)) + EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _)) .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)) - .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _)) + .WillRepeatedly( + [&](const renderengine::DisplaySettings&, + const std::vector<const renderengine::LayerSettings*>&, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> { + return futureOf<renderengine::RenderEngineResult>( + {NO_ERROR, base::unique_fd()}); + }); } Layer mLayer1; @@ -3527,7 +3544,9 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) { EXPECT_CALL(*mRenderSurface, setProtected(true)); // Must happen after setting the protected content state. EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } @@ -3590,14 +3609,16 @@ struct OutputComposeSurfacesTest_SetsExpensiveRendering : public OutputComposeSu TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering, IfExepensiveOutputDataspaceIsUsed) { mOutput.mState.dataspace = kExpensiveOutputDataspace; - EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kExpensiveOutputDataspace)) + EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kExpensiveOutputDataspace)) .WillOnce(Return(std::vector<LayerFE::LayerSettings>{})); // For this test, we also check the call order of key functions. InSequence seq; EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } @@ -3613,9 +3634,11 @@ struct OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur EXPECT_CALL(mLayer.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); - EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) + EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace)) .WillOnce(Return(std::vector<LayerFE::LayerSettings>{})); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _)) + .WillOnce(Return(ByMove(futureOf<renderengine::RenderEngineResult>( + {NO_ERROR, base::unique_fd()})))); EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u)); EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)) .WillRepeatedly(Return(&mLayer.outputLayer)); @@ -3653,10 +3676,9 @@ struct GenerateClientCompositionRequestsTest : public testing::Test { struct OutputPartialMock : public OutputPartialMockBase { // compositionengine::Output overrides std::vector<LayerFE::LayerSettings> generateClientCompositionRequests( - bool supportsProtectedContent, Region& clearRegion, - ui::Dataspace dataspace) override { + bool supportsProtectedContent, ui::Dataspace dataspace) override { return impl::Output::generateClientCompositionRequests(supportsProtectedContent, - clearRegion, dataspace); + dataspace); } }; @@ -3691,12 +3713,12 @@ struct GenerateClientCompositionRequestsTest : public testing::Test { struct GenerateClientCompositionRequestsTest_ThreeLayers : public GenerateClientCompositionRequestsTest { GenerateClientCompositionRequestsTest_ThreeLayers() { - mOutput.mState.orientedDisplaySpace.content = kDisplayFrame; - mOutput.mState.layerStackSpace.content = kDisplayViewport; - mOutput.mState.displaySpace.content = kDisplayDestinationClip; + mOutput.mState.orientedDisplaySpace.setContent(kDisplayFrame); + mOutput.mState.layerStackSpace.setContent(kDisplayViewport); + mOutput.mState.displaySpace.setContent(kDisplayDestinationClip); mOutput.mState.transform = ui::Transform{ui::Transform::toRotationFlags(kDisplayOrientation)}; - mOutput.mState.displaySpace.orientation = kDisplayOrientation; + mOutput.mState.displaySpace.setOrientation(kDisplayOrientation); mOutput.mState.needsFiltering = false; mOutput.mState.isSecure = false; @@ -3740,11 +3762,9 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, handlesNoClientCompost EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); - Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); EXPECT_EQ(0u, requests.size()); - EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, requiresVisibleRegionAfterViewportClip) { @@ -3752,11 +3772,9 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, requiresVisibleRegionA mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(4000, 0, 4010, 10)); mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 0, 0)); - Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); EXPECT_EQ(0u, requests.size()); - EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, gathersClientCompositionRequests) { @@ -3771,16 +3789,13 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, gathersClientCompositi .WillOnce(Return(std::vector<LayerFE::LayerSettings>( {mShadowSettings, mLayers[2].mLayerSettings}))); - Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); ASSERT_EQ(3u, requests.size()); EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]); EXPECT_EQ(mShadowSettings, requests[1]); EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]); - EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); - // Check that a timestamp was set for the layers that generated requests EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp); EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp); @@ -3811,16 +3826,13 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, overridesBlur) { .WillOnce(Return(std::vector<LayerFE::LayerSettings>( {mShadowSettings, mLayers[2].mLayerSettings}))); - Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); ASSERT_EQ(3u, requests.size()); EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]); EXPECT_EQ(mShadowSettings, requests[1]); EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]); - EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); - // Check that a timestamp was set for the layers that generated requests EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp); EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp); @@ -3844,13 +3856,10 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_)) .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings}))); - Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); ASSERT_EQ(1u, requests.size()); EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]); - - EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, @@ -3870,13 +3879,10 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_)) .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings}))); - Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); ASSERT_EQ(1u, requests.size()); EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]); - - EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqueAndNotFirst) { @@ -3897,15 +3903,12 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqu mLayers[0].mLayerFEState.isOpaque = true; mLayers[1].mLayerFEState.isOpaque = true; mLayers[2].mLayerFEState.isOpaque = true; - Region accumClearRegion(Rect(10, 11, 12, 13)); - Region stubRegion; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - stubRegion, /* clear region */ kDisplayViewport, kDisplayDataspace, false /* realContentIsVisible */, @@ -3917,7 +3920,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqu false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -3937,15 +3939,13 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqu .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings}))); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); ASSERT_EQ(2u, requests.size()); // The second layer is expected to be rendered as alpha=0 black with no blending EXPECT_EQ(mBlackoutSettings, requests[0]); EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]); - - EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, @@ -3954,14 +3954,11 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 30, 30)); mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, 0, 40, 4000)); - Region accumClearRegion(Rect(10, 11, 12, 13)); - compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{ Region(Rect(10, 10, 20, 20)), false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -3973,7 +3970,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -3985,7 +3981,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4002,7 +3997,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, static_cast<void>( mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace)); + kDisplayDataspace)); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, @@ -4010,14 +4005,11 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, mOutput.mState.needsFiltering = false; EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true)); - Region accumClearRegion(Rect(10, 11, 12, 13)); - compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{ Region(kDisplayFrame), true, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4029,7 +4021,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4041,7 +4032,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4058,7 +4048,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, static_cast<void>( mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace)); + kDisplayDataspace)); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, @@ -4066,14 +4056,11 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, mOutput.mState.needsFiltering = true; EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true)); - Region accumClearRegion(Rect(10, 11, 12, 13)); - compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{ Region(kDisplayFrame), true, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4086,7 +4073,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, true, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4098,7 +4084,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, true, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4115,21 +4100,18 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, static_cast<void>( mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace)); + kDisplayDataspace)); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, wholeOutputSecurityUsedToGenerateRequests) { mOutput.mState.isSecure = true; - Region accumClearRegion(Rect(10, 11, 12, 13)); - compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{ Region(kDisplayFrame), false, /* needs filtering */ true, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4141,7 +4123,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ true, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4153,7 +4134,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ true, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4170,19 +4150,17 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, static_cast<void>( mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace)); + kDisplayDataspace)); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, protectedContentSupportUsedToGenerateRequests) { - Region accumClearRegion(Rect(10, 11, 12, 13)); compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{ Region(kDisplayFrame), false, /* needs filtering */ false, /* secure */ true, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4194,7 +4172,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ true, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4206,7 +4183,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ true, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4222,7 +4198,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); static_cast<void>(mOutput.generateClientCompositionRequests(true /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace)); } @@ -4343,11 +4318,11 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq const ui::Rotation kPortraitOrientation = ui::ROTATION_90; constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3; - mOutput.mState.orientedDisplaySpace.content = kPortraitFrame; - mOutput.mState.layerStackSpace.content = kPortraitViewport; - mOutput.mState.displaySpace.content = kPortraitDestinationClip; + mOutput.mState.orientedDisplaySpace.setContent(kPortraitFrame); + mOutput.mState.layerStackSpace.setContent(kPortraitViewport); + mOutput.mState.displaySpace.setContent(kPortraitDestinationClip); mOutput.mState.transform = ui::Transform{ui::Transform::toRotationFlags(kPortraitOrientation)}; - mOutput.mState.displaySpace.orientation = kPortraitOrientation; + mOutput.mState.displaySpace.setOrientation(kPortraitOrientation); mOutput.mState.needsFiltering = false; mOutput.mState.isSecure = true; @@ -4370,14 +4345,11 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u)) .WillRepeatedly(Return(&rightLayer.mOutputLayer)); - Region accumClearRegion(Rect(10, 11, 12, 13)); - compositionengine::LayerFE::ClientCompositionTargetSettings leftLayerSettings{ Region(Rect(0, 0, 1000, 1000)), false, /* needs filtering */ true, /* secure */ true, /* supports protected content */ - accumClearRegion, kPortraitViewport, kOutputDataspace, true /* realContentIsVisible */, @@ -4395,7 +4367,6 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq false, /* needs filtering */ true, /* secure */ true, /* supports protected content */ - accumClearRegion, kPortraitViewport, kOutputDataspace, true /* realContentIsVisible */, @@ -4409,8 +4380,8 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq .WillOnce(Return(std::vector<LayerFE::LayerSettings>({rightLayer.mLayerSettings}))); constexpr bool supportsProtectedContent = true; - auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, - accumClearRegion, kOutputDataspace); + auto requests = + mOutput.generateClientCompositionRequests(supportsProtectedContent, kOutputDataspace); ASSERT_EQ(2u, requests.size()); EXPECT_EQ(leftLayer.mLayerSettings, requests[0]); EXPECT_EQ(rightLayer.mLayerSettings, requests[1]); @@ -4423,13 +4394,11 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, const Region kShadowRegion = Region(kContentWithShadow).subtract(kContent); const Region kPartialShadowRegion = Region(kContentWithShadow).subtract(Rect(40, 40, 60, 80)); - Region accumClearRegion(Rect(10, 11, 12, 13)); compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{ Region(Rect(60, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */ false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, false /* realContentIsVisible */, @@ -4449,7 +4418,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mShadowSettings}))); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); ASSERT_EQ(1u, requests.size()); EXPECT_EQ(mShadowSettings, requests[0]); @@ -4469,13 +4438,11 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, mLayers[2].mOutputLayerState.visibleRegion = kPartialContentWithPartialShadowRegion; mLayers[2].mOutputLayerState.shadowRegion = kShadowRegion; - Region accumClearRegion(Rect(10, 11, 12, 13)); compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{ Region(Rect(50, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */ false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4490,7 +4457,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, {mShadowSettings, mLayers[2].mLayerSettings}))); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); ASSERT_EQ(2u, requests.size()); EXPECT_EQ(mShadowSettings, requests[0]); diff --git a/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp index 704f5a8c07..19b3928434 100644 --- a/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp @@ -53,40 +53,40 @@ Rect getSideStrip(const Rect& rect, ui::Rotation rotation) { TEST(ProjectionSpaceTest, getTransformToSelfIsIdentity) { ProjectionSpace space; - space.content = Rect(100, 200); - space.bounds = Rect(100, 200); + space.setContent(Rect(100, 200)); + space.setBounds(ui::Size(100, 200)); const ui::Transform identity; for (int rotation = 0; rotation <= 3; rotation++) { - space.orientation = ui::Rotation(rotation); + space.setOrientation(ui::Rotation(rotation)); EXPECT_EQ(space.getTransform(space), identity); } } TEST(ProjectionSpaceTest, getTransformWhenTranslationIsNeeded) { ProjectionSpace source; - source.content = Rect(10, 10, 20, 20); - source.bounds = Rect(100, 200); + source.setContent(Rect(10, 10, 20, 20)); + source.setBounds(ui::Size(100, 200)); ProjectionSpace dest; - dest.content = Rect(10, 20, 30, 20); - dest.bounds = source.bounds; + dest.setContent(Rect(10, 20, 30, 20)); + dest.setBounds(source.getBounds()); const auto transform = source.getTransform(dest); - EXPECT_EQ(transform.transform(source.content), dest.content); + EXPECT_EQ(transform.transform(source.getContent()), dest.getContent()); } TEST(ProjectionSpaceTest, getTransformWhenScaleIsNeeded) { ProjectionSpace source; - source.content = Rect(0, 0, 20, 20); - source.bounds = Rect(100, 200); + source.setContent(Rect(0, 0, 20, 20)); + source.setBounds(ui::Size(100, 200)); ProjectionSpace dest; - dest.content = Rect(0, 0, 40, 30); - dest.bounds = source.bounds; + dest.setContent(Rect(0, 0, 40, 30)); + dest.setBounds(source.getBounds()); const auto transform = source.getTransform(dest); - EXPECT_EQ(transform.transform(source.content), dest.content); + EXPECT_EQ(transform.transform(source.getContent()), dest.getContent()); } TEST(ProjectionSpaceTest, getSideStripTest) { @@ -99,7 +99,7 @@ TEST(ProjectionSpaceTest, getSideStripTest) { void testTransform(const ProjectionSpace& source, const ProjectionSpace& dest) { const auto transform = source.getTransform(dest); - EXPECT_EQ(transform.transform(source.content), dest.content) + EXPECT_EQ(transform.transform(source.getContent()), dest.getContent()) << "Source content doesn't map to dest content when projecting " << to_string(source) << " onto " << to_string(dest); @@ -113,8 +113,8 @@ void testTransform(const ProjectionSpace& source, const ProjectionSpace& dest) { // | | | * // +-----+ +-------* // source(ROTATION_0) dest (ROTATION_90) - const auto sourceStrip = getSideStrip(source.content, source.orientation); - const auto destStrip = getSideStrip(dest.content, dest.orientation); + const auto sourceStrip = getSideStrip(source.getContent(), source.getOrientation()); + const auto destStrip = getSideStrip(dest.getContent(), dest.getOrientation()); ASSERT_NE(sourceStrip, Rect::INVALID_RECT); ASSERT_NE(destStrip, Rect::INVALID_RECT); const auto mappedStrip = transform.transform(sourceStrip); @@ -126,16 +126,16 @@ void testTransform(const ProjectionSpace& source, const ProjectionSpace& dest) { TEST(ProjectionSpaceTest, getTransformWithOrienations) { ProjectionSpace source; - source.bounds = Rect(12, 13, 678, 789); - source.content = Rect(40, 50, 234, 343); + source.setBounds(ui::Size(666, 776)); + source.setContent(Rect(40, 50, 234, 343)); ProjectionSpace dest; - dest.bounds = Rect(17, 18, 879, 564); - dest.content = Rect(43, 52, 432, 213); + dest.setBounds(ui::Size(862, 546)); + dest.setContent(Rect(43, 52, 432, 213)); for (int sourceRot = 0; sourceRot <= 3; sourceRot++) { - source.orientation = ui::Rotation(sourceRot); + source.setOrientation(ui::Rotation(sourceRot)); for (int destRot = 0; destRot <= 3; destRot++) { - dest.orientation = ui::Rotation(destRot); + dest.setOrientation(ui::Rotation(destRot)); testTransform(source, dest); } } diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp index 5090bb280f..431cc93514 100644 --- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp @@ -35,7 +35,7 @@ namespace { constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920; constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080; -constexpr DisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId(123u); +constexpr DisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(123u); const std::string DEFAULT_DISPLAY_NAME = "Mock Display"; using testing::_; diff --git a/services/surfaceflinger/CompositionEngine/tests/TestUtils.h b/services/surfaceflinger/CompositionEngine/tests/TestUtils.h new file mode 100644 index 0000000000..c80fde6ead --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/tests/TestUtils.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2021 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 <future> + +namespace android::compositionengine { +namespace { + +template <class T> +std::future<T> futureOf(T obj) { + std::promise<T> resultPromise; + std::future<T> resultFuture = resultPromise.get_future(); + resultPromise.set_value(std::move(obj)); + return resultFuture; +} +} // namespace +} // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp index ec81322afa..ecb05f8e5f 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp @@ -27,6 +27,8 @@ #include <utils/Errors.h> #include <memory> +#include "tests/TestUtils.h" + namespace android::compositionengine { using namespace std::chrono_literals; @@ -121,7 +123,7 @@ void CachedSetTest::SetUp() { // set up minimium params needed for rendering mOutputState.dataspace = ui::Dataspace::SRGB; mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5)); - mOutputState.framebufferSpace.orientation = ui::ROTATION_90; + mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90); mOutputState.layerStackSpace = ProjectionSpace(ui::Size(20, 10), Rect(5, 10)); } } @@ -342,19 +344,19 @@ TEST_F(CachedSetTest, renderUnsecureOutput) { clientCompList2.push_back({}); clientCompList2[0].alpha = 0.75f; - const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, - const std::vector<const renderengine::LayerSettings*>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&, base::unique_fd*) -> size_t { - EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay); - EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip); - EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation), + const auto drawLayers = + [&](const renderengine::DisplaySettings& displaySettings, + const std::vector<const renderengine::LayerSettings*>& layers, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> { + EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay); + EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip); + EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()), displaySettings.orientation); EXPECT_EQ(0.5f, layers[0]->alpha); EXPECT_EQ(0.75f, layers[1]->alpha); EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace); - - return NO_ERROR; + return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); }; EXPECT_CALL(*layerFE1, @@ -363,7 +365,7 @@ TEST_F(CachedSetTest, renderUnsecureOutput) { EXPECT_CALL(*layerFE2, prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(false))) .WillOnce(Return(clientCompList2)); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); mOutputState.isSecure = false; cachedSet.render(mRenderEngine, mTexturePool, mOutputState); expectReadyBuffer(cachedSet); @@ -394,19 +396,20 @@ TEST_F(CachedSetTest, renderSecureOutput) { clientCompList2.push_back({}); clientCompList2[0].alpha = 0.75f; - const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, - const std::vector<const renderengine::LayerSettings*>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&, base::unique_fd*) -> size_t { - EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay); - EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip); - EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation), + const auto drawLayers = + [&](const renderengine::DisplaySettings& displaySettings, + const std::vector<const renderengine::LayerSettings*>& layers, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> { + EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay); + EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip); + EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()), displaySettings.orientation); EXPECT_EQ(0.5f, layers[0]->alpha); EXPECT_EQ(0.75f, layers[1]->alpha); EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace); - return NO_ERROR; + return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); }; EXPECT_CALL(*layerFE1, @@ -415,7 +418,7 @@ TEST_F(CachedSetTest, renderSecureOutput) { EXPECT_CALL(*layerFE2, prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(true))) .WillOnce(Return(clientCompList2)); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); mOutputState.isSecure = true; cachedSet.render(mRenderEngine, mTexturePool, mOutputState); expectReadyBuffer(cachedSet); @@ -448,24 +451,25 @@ TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) { mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(2, 3, 10, 5)); - const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, - const std::vector<const renderengine::LayerSettings*>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&, base::unique_fd*) -> size_t { - EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay); - EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip); - EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation), + const auto drawLayers = + [&](const renderengine::DisplaySettings& displaySettings, + const std::vector<const renderengine::LayerSettings*>& layers, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> { + EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay); + EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip); + EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()), displaySettings.orientation); EXPECT_EQ(0.5f, layers[0]->alpha); EXPECT_EQ(0.75f, layers[1]->alpha); EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace); - return NO_ERROR; + return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); }; EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1)); EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2)); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); cachedSet.render(mRenderEngine, mTexturePool, mOutputState); expectReadyBuffer(cachedSet); @@ -650,10 +654,11 @@ TEST_F(CachedSetTest, addHolePunch) { EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2)); EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3)); - const auto drawLayers = [&](const renderengine::DisplaySettings&, - const std::vector<const renderengine::LayerSettings*>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&, base::unique_fd*) -> size_t { + const auto drawLayers = + [&](const renderengine::DisplaySettings&, + const std::vector<const renderengine::LayerSettings*>& layers, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> { // If the highlight layer is enabled, it will increase the size by 1. // We're interested in the third layer either way. EXPECT_GE(layers.size(), 4u); @@ -673,10 +678,10 @@ TEST_F(CachedSetTest, addHolePunch) { EXPECT_EQ(1.0f, holePunchBackgroundSettings->alpha); } - return NO_ERROR; + return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); }; - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); cachedSet.render(mRenderEngine, mTexturePool, mOutputState); } @@ -710,10 +715,11 @@ TEST_F(CachedSetTest, addHolePunch_noBuffer) { EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2)); EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3)); - const auto drawLayers = [&](const renderengine::DisplaySettings&, - const std::vector<const renderengine::LayerSettings*>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&, base::unique_fd*) -> size_t { + const auto drawLayers = + [&](const renderengine::DisplaySettings&, + const std::vector<const renderengine::LayerSettings*>& layers, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> { // If the highlight layer is enabled, it will increase the size by 1. // We're interested in the third layer either way. EXPECT_GE(layers.size(), 4u); @@ -734,10 +740,10 @@ TEST_F(CachedSetTest, addHolePunch_noBuffer) { EXPECT_EQ(1.0f, holePunchBackgroundSettings->alpha); } - return NO_ERROR; + return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); }; - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); cachedSet.render(mRenderEngine, mTexturePool, mOutputState); } @@ -859,10 +865,11 @@ TEST_F(CachedSetTest, addBlur) { BackgroundBlurOnly))) .WillOnce(Return(clientCompList3)); - const auto drawLayers = [&](const renderengine::DisplaySettings&, - const std::vector<const renderengine::LayerSettings*>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&, base::unique_fd*) -> int32_t { + const auto drawLayers = + [&](const renderengine::DisplaySettings&, + const std::vector<const renderengine::LayerSettings*>& layers, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> { // If the highlight layer is enabled, it will increase the size by 1. // We're interested in the third layer either way. EXPECT_GE(layers.size(), 3u); @@ -871,10 +878,10 @@ TEST_F(CachedSetTest, addBlur) { EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), blurSettings->source.solidColor); EXPECT_EQ(0.0f, blurSettings->alpha); - return NO_ERROR; + return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); }; - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); cachedSet.render(mRenderEngine, mTexturePool, mOutputState); } diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp index a28fb2c652..9b0a75fd47 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp @@ -26,6 +26,8 @@ #include <renderengine/mock/RenderEngine.h> #include <chrono> +#include "tests/TestUtils.h" + namespace android::compositionengine { using namespace std::chrono_literals; using impl::planner::CachedSet; @@ -141,7 +143,7 @@ void FlattenerTest::SetUp() { // set up minimium params needed for rendering mOutputState.dataspace = ui::Dataspace::SRGB; mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5)); - mOutputState.framebufferSpace.orientation = ui::ROTATION_90; + mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90); } } @@ -167,7 +169,9 @@ void FlattenerTest::initializeFlattener(const std::vector<const LayerState*>& la void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) { // layers would be flattened but the buffer would not be overridden - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), @@ -401,7 +405,9 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { // caleed for Layer2 and Layer3 layerState1->resetFramesSinceBufferUpdate(); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -423,7 +429,9 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { layerState1->incrementFramesSinceBufferUpdate(); mTime += 200ms; - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -474,7 +482,9 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { // called for Layer1 and Layer2 layerState3->resetFramesSinceBufferUpdate(); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -487,11 +497,13 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { EXPECT_EQ(nullptr, overrideBuffer5); // Layers 1 and 2 will be flattened a new drawFrame would be called for Layer4 and Layer5 - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mOutputState.framebufferSpace.orientation = ui::ROTATION_90; + mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90); mFlattener->renderCachedSets(mOutputState, std::nullopt); EXPECT_NE(nullptr, overrideBuffer1); @@ -504,7 +516,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mOutputState.framebufferSpace.orientation = ui::ROTATION_180; + mOutputState.framebufferSpace.setOrientation(ui::ROTATION_180); mFlattener->renderCachedSets(mOutputState, std::nullopt); EXPECT_NE(nullptr, overrideBuffer1); @@ -515,8 +527,9 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { layerState3->incrementFramesSinceBufferUpdate(); mTime += 200ms; - - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -531,7 +544,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mOutputState.framebufferSpace.orientation = ui::ROTATION_270; + mOutputState.framebufferSpace.setOrientation(ui::ROTATION_270); mFlattener->renderCachedSets(mOutputState, std::nullopt); EXPECT_NE(nullptr, overrideBuffer1); @@ -570,7 +583,9 @@ TEST_F(FlattenerTest, flattenLayers_pipRequiresRoundedCorners) { mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); // This will render a CachedSet. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); mFlattener->renderCachedSets(mOutputState, std::nullopt); // We've rendered a CachedSet, but we haven't merged it in. @@ -580,7 +595,7 @@ TEST_F(FlattenerTest, flattenLayers_pipRequiresRoundedCorners) { // This time we merge the CachedSet in, so we have a new hash, and we should // only have two sets. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -632,7 +647,9 @@ TEST_F(FlattenerTest, flattenLayers_pip) { mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); // This will render a CachedSet. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); mFlattener->renderCachedSets(mOutputState, std::nullopt); // We've rendered a CachedSet, but we haven't merged it in. @@ -642,7 +659,7 @@ TEST_F(FlattenerTest, flattenLayers_pip) { // This time we merge the CachedSet in, so we have a new hash, and we should // only have two sets. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -660,6 +677,75 @@ TEST_F(FlattenerTest, flattenLayers_pip) { EXPECT_EQ(peekThroughLayer1, peekThroughLayer2); } +// A test that verifies the hole puch optimization can be done on a single layer. +TEST_F(FlattenerTest, flattenLayers_holePunchSingleLayer) { + mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5); + + // An opaque static background + auto& layerState0 = mTestLayers[0]->layerState; + const auto& overrideBuffer0 = layerState0->getOutputLayer()->getState().overrideInfo.buffer; + + // a rounded updating layer + auto& layerState1 = mTestLayers[1]->layerState; + const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer; + + EXPECT_CALL(*mTestLayers[1]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); + + std::vector<LayerFE::LayerSettings> clientCompositionList = { + LayerFE::LayerSettings{}, + }; + clientCompositionList[0].source.buffer.buffer = std::make_shared< + renderengine::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer, + mRenderEngine, + renderengine::ExternalTexture::Usage::READABLE); + EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientCompositionList(_)) + .WillOnce(Return(clientCompositionList)); + + const std::vector<const LayerState*> layers = { + layerState0.get(), + layerState1.get(), + }; + + initializeFlattener(layers); + + // layer 1 satisfies every condition in CachedSet::requiresHolePunch() + mTime += 200ms; + layerState1->resetFramesSinceBufferUpdate(); // it is updating + + initializeOverrideBuffer(layers); + // Expect no cache invalidation the first time (there's no cache yet) + EXPECT_EQ(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + + // This will render a CachedSet of layer 0. Though it is just one layer, it satisfies the + // exception that there would be a hole punch above it. + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); + mFlattener->renderCachedSets(mOutputState, std::nullopt); + + // We've rendered a CachedSet, but we haven't merged it in. + EXPECT_EQ(nullptr, overrideBuffer0); + + // This time we merge the CachedSet in and we should still have only two sets. + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); + initializeOverrideBuffer(layers); + EXPECT_EQ(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + mFlattener->renderCachedSets(mOutputState, std::nullopt); + + EXPECT_NE(nullptr, overrideBuffer0); // got overridden + EXPECT_EQ(nullptr, overrideBuffer1); // did not + + // expect 0's peek though layer to be 1's output layer + const auto* peekThroughLayer0 = + layerState0->getOutputLayer()->getState().overrideInfo.peekThroughLayer; + const auto* peekThroughLayer1 = + layerState1->getOutputLayer()->getState().overrideInfo.peekThroughLayer; + EXPECT_EQ(&mTestLayers[1]->outputLayer, peekThroughLayer0); + EXPECT_EQ(nullptr, peekThroughLayer1); +} + TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) { auto& layerState1 = mTestLayers[0]->layerState; @@ -685,7 +771,9 @@ TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) { layerState3->resetFramesSinceBufferUpdate(); // layers would be flattened but the buffer would not be overridden - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), @@ -729,7 +817,9 @@ TEST_F(FlattenerTest, flattenLayers_doesNotFlattenBlurBehindRun) { layerState1->resetFramesSinceBufferUpdate(); // layers would be flattened but the buffer would not be overridden - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillRepeatedly(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), @@ -781,7 +871,9 @@ TEST_F(FlattenerTest, flattenLayers_flattenSkipsLayerWithBlurBehind) { layerState1->resetFramesSinceBufferUpdate(); // layers would be flattened but the buffer would not be overridden - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), @@ -828,7 +920,9 @@ TEST_F(FlattenerTest, flattenLayers_whenBlurLayerIsChanging_appliesBlurToInactiv layerStateWithBlurBehind->resetFramesSinceBufferUpdate(); // layers would be flattened but the buffer would not be overridden - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), @@ -869,7 +963,9 @@ TEST_F(FlattenerTest, flattenLayers_renderCachedSets_doesNotRenderTwice) { // Mark the layers inactive mTime += 200ms; // layers would be flattened but the buffer would not be overridden - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), @@ -881,7 +977,7 @@ TEST_F(FlattenerTest, flattenLayers_renderCachedSets_doesNotRenderTwice) { // Simulate attempting to render prior to merging the new cached set with the layer stack. // Here we should not try to re-render. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); mFlattener->renderCachedSets(mOutputState, std::nullopt); // We provide the override buffer now that it's rendered @@ -928,13 +1024,15 @@ TEST_F(FlattenerRenderSchedulingTest, flattenLayers_renderCachedSets_defersUpToM mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); for (size_t i = 0; i < kMaxDeferRenderAttempts; i++) { - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); mFlattener->renderCachedSets(mOutputState, std::chrono::steady_clock::now() - (kCachedSetRenderDuration + 10ms)); } - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); mFlattener->renderCachedSets(mOutputState, std::chrono::steady_clock::now() - (kCachedSetRenderDuration + 10ms)); @@ -968,7 +1066,9 @@ TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) { mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); // This will render a CachedSet. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); mFlattener->renderCachedSets(mOutputState, std::nullopt); // We've rendered a CachedSet, but we haven't merged it in. @@ -978,7 +1078,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) { // This time we merge the CachedSet in, so we have a new hash, and we should // only have two sets. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -1017,7 +1117,9 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR) { mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); // This will render a CachedSet. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); mFlattener->renderCachedSets(mOutputState, std::nullopt); // We've rendered a CachedSet, but we haven't merged it in. @@ -1027,7 +1129,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR) { // This time we merge the CachedSet in, so we have a new hash, and we should // only have two sets. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -1066,7 +1168,9 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR2) { mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); // This will render a CachedSet. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .WillOnce(Return(ByMove( + futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); mFlattener->renderCachedSets(mOutputState, std::nullopt); // We've rendered a CachedSet, but we haven't merged it in. @@ -1076,7 +1180,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR2) { // This time we merge the CachedSet in, so we have a new hash, and we should // only have two sets. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 4445eea604..3397118c05 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -116,11 +116,11 @@ void DisplayDevice::disconnect() { } int DisplayDevice::getWidth() const { - return mCompositionDisplay->getState().displaySpace.bounds.getWidth(); + return mCompositionDisplay->getState().displaySpace.getBounds().width; } int DisplayDevice::getHeight() const { - return mCompositionDisplay->getState().displaySpace.bounds.getHeight(); + return mCompositionDisplay->getState().displaySpace.getBounds().height; } void DisplayDevice::setDisplayName(const std::string& displayName) { @@ -139,6 +139,34 @@ uint32_t DisplayDevice::getPageFlipCount() const { return mCompositionDisplay->getRenderSurface()->getPageFlipCount(); } +std::pair<gui::DisplayInfo, ui::Transform> DisplayDevice::getInputInfo() const { + gui::DisplayInfo info; + info.displayId = getLayerStack().id; + + // The physical orientation is set when the orientation of the display panel is + // different than the default orientation of the device. Other services like + // InputFlinger do not know about this, so we do not need to expose the physical + // orientation of the panel outside of SurfaceFlinger. + const ui::Rotation inversePhysicalOrientation = ui::ROTATION_0 - mPhysicalOrientation; + auto width = getWidth(); + auto height = getHeight(); + if (inversePhysicalOrientation == ui::ROTATION_90 || + inversePhysicalOrientation == ui::ROTATION_270) { + std::swap(width, height); + } + const ui::Transform undoPhysicalOrientation(ui::Transform::toRotationFlags( + inversePhysicalOrientation), + width, height); + const auto& displayTransform = undoPhysicalOrientation * getTransform(); + // Send the inverse display transform to input so it can convert display coordinates to + // logical display. + info.transform = displayTransform.inverse(); + + info.logicalWidth = getLayerStackSpaceRect().width(); + info.logicalHeight = getLayerStackSpaceRect().height(); + return {info, displayTransform}; +} + // ---------------------------------------------------------------------------- void DisplayDevice::setPowerMode(hal::PowerMode mode) { mPowerMode = mode; @@ -232,7 +260,7 @@ ui::Dataspace DisplayDevice::getCompositionDataSpace() const { } void DisplayDevice::setLayerStack(ui::LayerStack stack) { - mCompositionDisplay->setLayerStackFilter(stack, isInternal()); + mCompositionDisplay->setLayerFilter({stack, isInternal()}); if (mRefreshRateOverlay) { mRefreshRateOverlay->setLayerStack(stack); } @@ -262,13 +290,15 @@ void DisplayDevice::setProjection(ui::Rotation orientation, Rect layerStackSpace if (!orientedDisplaySpaceRect.isValid()) { // The destination frame can be invalid if it has never been set, // in that case we assume the whole display size. - orientedDisplaySpaceRect = getCompositionDisplay()->getState().displaySpace.bounds; + orientedDisplaySpaceRect = + getCompositionDisplay()->getState().displaySpace.getBoundsAsRect(); } if (layerStackSpaceRect.isEmpty()) { // The layerStackSpaceRect can be invalid if it has never been set, in that case // we assume the whole framebuffer size. - layerStackSpaceRect = getCompositionDisplay()->getState().framebufferSpace.bounds; + layerStackSpaceRect = + getCompositionDisplay()->getState().framebufferSpace.getBoundsAsRect(); if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) { std::swap(layerStackSpaceRect.right, layerStackSpaceRect.bottom); } @@ -336,8 +366,8 @@ bool DisplayDevice::isSecure() const { return mCompositionDisplay->isSecure(); } -const Rect& DisplayDevice::getBounds() const { - return mCompositionDisplay->getState().displaySpace.bounds; +const Rect DisplayDevice::getBounds() const { + return mCompositionDisplay->getState().displaySpace.getBoundsAsRect(); } const Region& DisplayDevice::getUndefinedRegion() const { @@ -349,7 +379,7 @@ bool DisplayDevice::needsFiltering() const { } ui::LayerStack DisplayDevice::getLayerStack() const { - return mCompositionDisplay->getState().layerStackId; + return mCompositionDisplay->getState().layerFilter.layerStack; } ui::Transform::RotationFlags DisplayDevice::getTransformHint() const { @@ -361,11 +391,11 @@ const ui::Transform& DisplayDevice::getTransform() const { } const Rect& DisplayDevice::getLayerStackSpaceRect() const { - return mCompositionDisplay->getState().layerStackSpace.content; + return mCompositionDisplay->getState().layerStackSpace.getContent(); } const Rect& DisplayDevice::getOrientedDisplaySpaceRect() const { - return mCompositionDisplay->getState().orientedDisplaySpace.content; + return mCompositionDisplay->getState().orientedDisplaySpace.getContent(); } bool DisplayDevice::hasWideColorGamut() const { diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 4d435c7e47..4a731bddf1 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -86,9 +86,7 @@ public: bool isVirtual() const { return !mConnectionType; } bool isPrimary() const { return mIsPrimary; } - bool isInternal() const { - return !isVirtual() && mConnectionType == ui::DisplayConnectionType::Internal; - } + bool isInternal() const { return mConnectionType == ui::DisplayConnectionType::Internal; } // isSecure indicates whether this display can be trusted to display // secure surfaces. @@ -158,8 +156,8 @@ public: // Return true if intent is supported by the display. bool hasRenderIntent(ui::RenderIntent intent) const; - const Rect& getBounds() const; - const Rect& bounds() const { return getBounds(); } + const Rect getBounds() const; + const Rect bounds() const { return getBounds(); } void setDisplayName(const std::string& displayName); const std::string& getDisplayName() const { return mDisplayName; } @@ -169,6 +167,10 @@ public: return mDeviceProductInfo; } + // Get the DisplayInfo that will be sent to InputFlinger, and the display transform that should + // be applied to all the input windows on the display. + std::pair<gui::DisplayInfo, ui::Transform> getInputInfo() const; + /* ------------------------------------------------------------------------ * Display power mode management. */ @@ -273,7 +275,7 @@ private: std::atomic<nsecs_t> mLastHwVsync = 0; - // TODO(b/74619554): Remove special cases for primary display. + // TODO(b/182939859): Remove special cases for primary display. const bool mIsPrimary; uint32_t mFlags = 0; @@ -311,7 +313,7 @@ struct DisplayDeviceState { int32_t sequenceId = sNextSequenceId++; std::optional<Physical> physical; sp<IGraphicBufferProducer> surface; - ui::LayerStack layerStack = ui::NO_LAYER_STACK; + ui::LayerStack layerStack; uint32_t flags = 0; Rect layerStackSpaceRect; Rect orientedDisplaySpaceRect; @@ -354,16 +356,4 @@ struct DisplayDeviceCreationArgs { DisplayModeId activeModeId; }; -// Predicates for display lookup. - -struct WithLayerStack { - explicit WithLayerStack(ui::LayerStack layerStack) : layerStack(layerStack) {} - - bool operator()(const DisplayDevice& display) const { - return display.getLayerStack() == layerStack; - } - - ui::LayerStack layerStack; -}; - } // namespace android diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index a790b4c11e..256bca9570 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -212,8 +212,6 @@ bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) { RETURN_IF_INVALID_DISPLAY(*displayId, false); auto& displayData = mDisplayData[*displayId]; - LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s", - __FUNCTION__, to_string(*displayId).c_str()); { // There have been reports of HWCs that signal several vsync events @@ -271,7 +269,6 @@ bool HWComposer::allocateVirtualDisplay(HalVirtualDisplayId displayId, ui::Size display->setConnected(true); auto& displayData = mDisplayData[displayId]; displayData.hwcDisplay = std::move(display); - displayData.isVirtual = true; return true; } @@ -279,10 +276,8 @@ void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, PhysicalDisplayId displayId) { mPhysicalDisplayIdMap[hwcDisplayId] = displayId; - if (!mInternalHwcDisplayId) { - mInternalHwcDisplayId = hwcDisplayId; - } else if (mInternalHwcDisplayId != hwcDisplayId && !mExternalHwcDisplayId) { - mExternalHwcDisplayId = hwcDisplayId; + if (!mPrimaryHwcDisplayId) { + mPrimaryHwcDisplayId = hwcDisplayId; } auto& displayData = mDisplayData[displayId]; @@ -372,7 +367,7 @@ ui::DisplayConnectionType HWComposer::getDisplayConnectionType(PhysicalDisplayId ui::DisplayConnectionType type; const auto error = hwcDisplay->getConnectionType(&type); - const auto FALLBACK_TYPE = hwcDisplay->getId() == mInternalHwcDisplayId + const auto FALLBACK_TYPE = hwcDisplay->getId() == mPrimaryHwcDisplayId ? ui::DisplayConnectionType::Internal : ui::DisplayConnectionType::External; @@ -428,9 +423,6 @@ void HWComposer::setVsyncEnabled(PhysicalDisplayId displayId, hal::Vsync enabled RETURN_IF_INVALID_DISPLAY(displayId); auto& displayData = mDisplayData[displayId]; - LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s", - __FUNCTION__, to_string(displayId).c_str()); - // NOTE: we use our own internal lock here because we have to call // into the HWC with the lock held, and we want to make sure // that even if HWC blocks (which it shouldn't), it won't @@ -597,14 +589,11 @@ status_t HWComposer::presentAndGetReleaseFences( status_t HWComposer::setPowerMode(PhysicalDisplayId displayId, hal::PowerMode mode) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); - const auto& displayData = mDisplayData[displayId]; - LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s", - __FUNCTION__, to_string(displayId).c_str()); - if (mode == hal::PowerMode::OFF) { setVsyncEnabled(displayId, hal::Vsync::DISABLE); } + const auto& displayData = mDisplayData[displayId]; auto& hwcDisplay = displayData.hwcDisplay; switch (mode) { case hal::PowerMode::OFF: @@ -678,12 +667,6 @@ void HWComposer::disconnectDisplay(HalDisplayId displayId) { auto& displayData = mDisplayData[displayId]; const auto hwcDisplayId = displayData.hwcDisplay->getId(); - // TODO(b/74619554): Select internal/external display from remaining displays. - if (hwcDisplayId == mInternalHwcDisplayId) { - mInternalHwcDisplayId.reset(); - } else if (hwcDisplayId == mExternalHwcDisplayId) { - mExternalHwcDisplayId.reset(); - } mPhysicalDisplayIdMap.erase(hwcDisplayId); mDisplayData.erase(displayId); } @@ -693,9 +676,6 @@ status_t HWComposer::setOutputBuffer(HalVirtualDisplayId displayId, const sp<Fen RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); const auto& displayData = mDisplayData[displayId]; - LOG_FATAL_IF(!displayData.isVirtual, "%s: Invalid operation on physical display with ID %s", - __FUNCTION__, to_string(displayId).c_str()); - auto error = displayData.hwcDisplay->setOutputBuffer(buffer, acquireFence); RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); return NO_ERROR; @@ -853,8 +833,7 @@ std::optional<PhysicalDisplayId> HWComposer::toPhysicalDisplayId( std::optional<hal::HWDisplayId> HWComposer::fromPhysicalDisplayId( PhysicalDisplayId displayId) const { - if (const auto it = mDisplayData.find(displayId); - it != mDisplayData.end() && !it->second.isVirtual) { + if (const auto it = mDisplayData.find(displayId); it != mDisplayData.end()) { return it->second.hwcDisplay->getId(); } return {}; @@ -868,7 +847,8 @@ bool HWComposer::shouldIgnoreHotplugConnect(hal::HWDisplayId hwcDisplayId, return true; } - if (!mHasMultiDisplaySupport && mInternalHwcDisplayId && mExternalHwcDisplayId) { + // Legacy mode only supports IDs LEGACY_DISPLAY_TYPE_PRIMARY and LEGACY_DISPLAY_TYPE_EXTERNAL. + if (!mHasMultiDisplaySupport && mPhysicalDisplayIdMap.size() == 2) { ALOGE("Ignoring connection of tertiary display %" PRIu64, hwcDisplayId); return true; } @@ -909,7 +889,7 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect( } info = [this, hwcDisplayId, &port, &data, hasDisplayIdentificationData] { - const bool isPrimary = !mInternalHwcDisplayId; + const bool isPrimary = !mPrimaryHwcDisplayId; if (mHasMultiDisplaySupport) { if (const auto info = parseDisplayIdentificationData(port, data)) { return *info; @@ -922,8 +902,8 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect( } return DisplayIdentificationInfo{.id = PhysicalDisplayId::fromPort(port), - .name = isPrimary ? "Internal display" - : "External display", + .name = isPrimary ? "Primary display" + : "Secondary display", .deviceProductInfo = std::nullopt}; }(); } @@ -936,9 +916,12 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect( std::optional<DisplayIdentificationInfo> HWComposer::onHotplugDisconnect( hal::HWDisplayId hwcDisplayId) { + LOG_ALWAYS_FATAL_IF(hwcDisplayId == mPrimaryHwcDisplayId, + "Primary display cannot be disconnected."); + const auto displayId = toPhysicalDisplayId(hwcDisplayId); if (!displayId) { - ALOGE("Ignoring disconnection of invalid HWC display %" PRIu64, hwcDisplayId); + LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display"); return {}; } @@ -947,7 +930,7 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplugDisconnect( if (isConnected(*displayId)) { mDisplayData[*displayId].hwcDisplay->setConnected(false); } else { - ALOGW("Attempted to disconnect unknown display %" PRIu64, hwcDisplayId); + LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Already disconnected"); } // The cleanup of Disconnect is handled through HWComposer::disconnectDisplay // via SurfaceFlinger's onHotplugReceived callback handling diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 49f96d9614..0a090da01e 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef ANDROID_SF_HWCOMPOSER_H -#define ANDROID_SF_HWCOMPOSER_H +#pragma once #include <cstdint> #include <future> @@ -228,14 +227,18 @@ public: virtual const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata() const = 0; - // for debugging ---------------------------------------------------------- virtual void dump(std::string& out) const = 0; virtual Hwc2::Composer* getComposer() const = 0; - // TODO(b/74619554): Remove special cases for internal/external display. - virtual std::optional<hal::HWDisplayId> getInternalHwcDisplayId() const = 0; - virtual std::optional<hal::HWDisplayId> getExternalHwcDisplayId() const = 0; + // Returns the first display connected at boot. It cannot be disconnected, which implies an + // internal connection type. Its connection via HWComposer::onHotplug, which in practice is + // immediately after HWComposer construction, must occur before any call to this function. + // + // TODO(b/182939859): Remove special cases for primary display. + virtual hal::HWDisplayId getPrimaryHwcDisplayId() const = 0; + virtual PhysicalDisplayId getPrimaryDisplayId() const = 0; + virtual bool isHeadless() const = 0; virtual std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const = 0; virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const = 0; @@ -366,14 +369,19 @@ public: Hwc2::Composer* getComposer() const override { return mComposer.get(); } - // TODO(b/74619554): Remove special cases for internal/external display. - std::optional<hal::HWDisplayId> getInternalHwcDisplayId() const override { - return mInternalHwcDisplayId; + hal::HWDisplayId getPrimaryHwcDisplayId() const override { + LOG_ALWAYS_FATAL_IF(!mPrimaryHwcDisplayId, "Missing HWC primary display"); + return *mPrimaryHwcDisplayId; } - std::optional<hal::HWDisplayId> getExternalHwcDisplayId() const override { - return mExternalHwcDisplayId; + + PhysicalDisplayId getPrimaryDisplayId() const override { + const auto id = toPhysicalDisplayId(getPrimaryHwcDisplayId()); + LOG_ALWAYS_FATAL_IF(!id, "Missing primary display"); + return *id; } + virtual bool isHeadless() const override { return !mPrimaryHwcDisplayId; } + std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const override; std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const override; @@ -382,12 +390,9 @@ private: friend TestableSurfaceFlinger; struct DisplayData { - bool isVirtual = false; std::unique_ptr<HWC2::Display> hwcDisplay; sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences; - buffer_handle_t outbufHandle = nullptr; - sp<Fence> outbufAcquireFence = Fence::NO_FENCE; bool validateWasSkipped; hal::Error presentError; @@ -418,8 +423,7 @@ private: bool mRegisteredCallback = false; std::unordered_map<hal::HWDisplayId, PhysicalDisplayId> mPhysicalDisplayIdMap; - std::optional<hal::HWDisplayId> mInternalHwcDisplayId; - std::optional<hal::HWDisplayId> mExternalHwcDisplayId; + std::optional<hal::HWDisplayId> mPrimaryHwcDisplayId; bool mHasMultiDisplaySupport = false; const size_t mMaxVirtualDisplayDimension; @@ -428,5 +432,3 @@ private: } // namespace impl } // namespace android - -#endif // ANDROID_SF_HWCOMPOSER_H diff --git a/services/surfaceflinger/FlagManager.cpp b/services/surfaceflinger/FlagManager.cpp new file mode 100644 index 0000000000..f0c5b583cf --- /dev/null +++ b/services/surfaceflinger/FlagManager.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2021 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 "FlagManager.h" + +#include <SurfaceFlingerProperties.sysprop.h> +#include <android-base/parsebool.h> +#include <android-base/parseint.h> +#include <android-base/stringprintf.h> +#include <log/log.h> +#include <server_configurable_flags/get_flags.h> +#include <cinttypes> + +namespace android { +static constexpr const char* kExperimentNamespace = "surface_flinger_native_boot"; +static constexpr const int64_t kDemoFlag = -1; + +FlagManager::~FlagManager() = default; + +void FlagManager::dump(std::string& result) const { + base::StringAppendF(&result, "FlagManager values: \n"); + base::StringAppendF(&result, "demo_flag: %" PRId64 "\n", demo_flag()); +} + +namespace { +template <typename T> +std::optional<T> doParse(const char* str); + +template <> +[[maybe_unused]] std::optional<int32_t> doParse(const char* str) { + int32_t ret; + return base::ParseInt(str, &ret) ? std::make_optional(ret) : std::nullopt; +} + +template <> +[[maybe_unused]] std::optional<int64_t> doParse(const char* str) { + int64_t ret; + return base::ParseInt(str, &ret) ? std::make_optional(ret) : std::nullopt; +} + +template <> +[[maybe_unused]] std::optional<bool> doParse(const char* str) { + base::ParseBoolResult parseResult = base::ParseBool(str); + switch (parseResult) { + case base::ParseBoolResult::kTrue: + return std::make_optional(true); + case base::ParseBoolResult::kFalse: + return std::make_optional(false); + case base::ParseBoolResult::kError: + return std::nullopt; + } +} +} // namespace + +std::string FlagManager::getServerConfigurableFlag(const std::string& experimentFlagName) const { + return server_configurable_flags::GetServerConfigurableFlag(kExperimentNamespace, + experimentFlagName, ""); +} + +template int32_t FlagManager::getValue<int32_t>(const std::string&, std::optional<int32_t>, + int32_t) const; +template int64_t FlagManager::getValue<int64_t>(const std::string&, std::optional<int64_t>, + int64_t) const; +template bool FlagManager::getValue<bool>(const std::string&, std::optional<bool>, bool) const; +template <typename T> +T FlagManager::getValue(const std::string& experimentFlagName, std::optional<T> systemPropertyOpt, + T defaultValue) const { + // System property takes precedence over the experiment config server value. + if (systemPropertyOpt.has_value()) { + return *systemPropertyOpt; + } + std::string str = getServerConfigurableFlag(experimentFlagName); + return str.empty() ? defaultValue : doParse<T>(str.c_str()).value_or(defaultValue); +} + +int64_t FlagManager::demo_flag() const { + std::optional<int64_t> sysPropVal = std::nullopt; + return getValue("DemoFeature__demo_flag", sysPropVal, kDemoFlag); +} +} // namespace android diff --git a/services/surfaceflinger/FlagManager.h b/services/surfaceflinger/FlagManager.h new file mode 100644 index 0000000000..65e30a45be --- /dev/null +++ b/services/surfaceflinger/FlagManager.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 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 <cstdint> +#include <optional> +#include <string> + +namespace android { +// Manages flags for SurfaceFlinger, including default values, system properties, and Mendel +// experiment configuration values. +class FlagManager { +public: + FlagManager() = default; + virtual ~FlagManager(); + void dump(std::string& result) const; + + int64_t demo_flag() const; + +private: + friend class FlagManagerTest; + + // Wrapper for mocking in test. + virtual std::string getServerConfigurableFlag(const std::string& experimentFlagName) const; + + template <typename T> + T getValue(const std::string& experimentFlagName, std::optional<T> systemPropertyOpt, + T defaultValue) const; +}; +} // namespace android diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 638bb9b60f..ef6f115fdc 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -107,7 +107,7 @@ Layer::Layer(const LayerCreationArgs& args) mDrawingState.requestedCrop = mDrawingState.crop; mDrawingState.z = 0; mDrawingState.color.a = 1.0f; - mDrawingState.layerStack = 0; + mDrawingState.layerStack = ui::DEFAULT_LAYER_STACK; mDrawingState.sequence = 0; mDrawingState.requested_legacy = mDrawingState.active_legacy; mDrawingState.width = UINT32_MAX; @@ -392,7 +392,6 @@ void Layer::setupRoundedCornersCropCoordinates(Rect win, void Layer::prepareBasicGeometryCompositionState() { const auto& drawingState{getDrawingState()}; - const uint32_t layerStack = getLayerStack(); const auto alpha = static_cast<float>(getAlpha()); const bool opaque = isOpaque(drawingState); const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f; @@ -404,9 +403,7 @@ void Layer::prepareBasicGeometryCompositionState() { } auto* compositionState = editCompositionState(); - compositionState->layerStackId = - (layerStack != ~0u) ? std::make_optional(layerStack) : std::nullopt; - compositionState->internalOnly = getPrimaryDisplayOnly(); + compositionState->outputFilter = getOutputFilter(); compositionState->isVisible = isVisible(); compositionState->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f; compositionState->shadowRadius = mEffectiveShadowRadius; @@ -729,14 +726,14 @@ void Layer::commitTransaction(State&) { mDrawingState.bufferlessSurfaceFramesTX.clear(); } -uint32_t Layer::getTransactionFlags(uint32_t flags) { - auto ret = mTransactionFlags & flags; - mTransactionFlags &= ~flags; - return ret; +uint32_t Layer::clearTransactionFlags(uint32_t mask) { + const auto flags = mTransactionFlags & mask; + mTransactionFlags &= ~mask; + return flags; } -uint32_t Layer::setTransactionFlags(uint32_t flags) { - return mTransactionFlags |= flags; +void Layer::setTransactionFlags(uint32_t mask) { + mTransactionFlags |= mask; } bool Layer::setPosition(float x, float y) { @@ -1006,7 +1003,7 @@ bool Layer::setMetadata(const LayerMetadata& data) { return true; } -bool Layer::setLayerStack(uint32_t layerStack) { +bool Layer::setLayerStack(ui::LayerStack layerStack) { if (mDrawingState.layerStack == layerStack) return false; mDrawingState.sequence++; mDrawingState.layerStack = layerStack; @@ -1053,12 +1050,11 @@ bool Layer::isLayerFocusedBasedOnPriority(int32_t priority) { return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE; }; -uint32_t Layer::getLayerStack() const { - auto p = mDrawingParent.promote(); - if (p == nullptr) { - return getDrawingState().layerStack; +ui::LayerStack Layer::getLayerStack() const { + if (const auto parent = mDrawingParent.promote()) { + return parent->getLayerStack(); } - return p->getLayerStack(); + return getDrawingState().layerStack; } bool Layer::setShadowRadius(float shadowRadius) { @@ -1372,7 +1368,7 @@ LayerDebugInfo Layer::getLayerDebugInfo(const DisplayDevice* display) const { info.mVisibleRegion = getVisibleRegion(display); info.mSurfaceDamageRegion = surfaceDamageRegion; - info.mLayerStack = getLayerStack(); + info.mLayerStack = getLayerStack().id; info.mX = ds.transform.tx(); info.mY = ds.transform.ty(); info.mZ = ds.z; @@ -2089,7 +2085,7 @@ void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy, [&]() { return layerInfo->mutable_transparent_region(); }); - layerInfo->set_layer_stack(getLayerStack()); + layerInfo->set_layer_stack(getLayerStack().id); layerInfo->set_z(state.z); LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), @@ -2136,7 +2132,7 @@ void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet if (traceFlags & SurfaceTracing::TRACE_INPUT) { WindowInfo info; if (useDrawing) { - info = fillInputInfo({nullptr}); + info = fillInputInfo(ui::Transform()); } else { info = state.inputInfo; } @@ -2165,7 +2161,7 @@ Rect Layer::getInputBounds() const { return getCroppedBufferSize(getDrawingState()); } -void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& toNonRotatedDisplay) { +void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& displayTransform) { // Transform layer size to screen space and inset it by surface insets. // If this is a portal window, set the touchableRegion to the layerBounds. Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE @@ -2185,13 +2181,13 @@ void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& toNonRotat return; } - ui::Transform layerToDisplay = getInputTransform(); + const ui::Transform layerTransform = getInputTransform(); // Transform that takes window coordinates to non-rotated display coordinates - ui::Transform t = toNonRotatedDisplay * layerToDisplay; + ui::Transform t = displayTransform * layerTransform; int32_t xSurfaceInset = info.surfaceInset; int32_t ySurfaceInset = info.surfaceInset; - // Bring screenBounds into non-rotated space - Rect screenBounds = toNonRotatedDisplay.transform(Rect{mScreenBounds}); + // Bring screenBounds into non-unrotated space + Rect screenBounds = displayTransform.transform(Rect{mScreenBounds}); const float xScale = t.getScaleX(); const float yScale = t.getScaleY(); @@ -2270,47 +2266,21 @@ void Layer::fillTouchOcclusionMode(WindowInfo& info) { } } -WindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) { +WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform) { if (!hasInputInfo()) { mDrawingState.inputInfo.name = getName(); mDrawingState.inputInfo.ownerUid = mOwnerUid; mDrawingState.inputInfo.ownerPid = mOwnerPid; mDrawingState.inputInfo.inputFeatures = WindowInfo::Feature::NO_INPUT_CHANNEL; mDrawingState.inputInfo.flags = WindowInfo::Flag::NOT_TOUCH_MODAL; - mDrawingState.inputInfo.displayId = getLayerStack(); + mDrawingState.inputInfo.displayId = getLayerStack().id; } WindowInfo info = mDrawingState.inputInfo; info.id = sequence; - info.displayId = getLayerStack(); - - // Transform that goes from "logical(rotated)" display to the non-rotated display. - ui::Transform toNonRotatedDisplay; - if (display) { - // The physical orientation is set when the orientation of the display panel is different - // than the default orientation of the device. We do not need to expose the physical - // orientation of the panel outside of SurfaceFlinger. - const ui::Rotation inversePhysicalOrientation = - ui::ROTATION_0 - display->getPhysicalOrientation(); - auto width = display->getWidth(); - auto height = display->getHeight(); - if (inversePhysicalOrientation == ui::ROTATION_90 || - inversePhysicalOrientation == ui::ROTATION_270) { - std::swap(width, height); - } - const auto rotationFlags = ui::Transform::toRotationFlags(inversePhysicalOrientation); - const ui::Transform undoPhysicalOrientation(rotationFlags, width, height); - toNonRotatedDisplay = undoPhysicalOrientation * display->getTransform(); - - // Send the inverse of the display orientation so that input can transform points back to - // the rotated display space. - const ui::Rotation inverseOrientation = ui::ROTATION_0 - display->getOrientation(); - info.displayOrientation = ui::Transform::toRotationFlags(inverseOrientation); + info.displayId = getLayerStack().id; - info.displayWidth = width; - info.displayHeight = height; - } - fillInputFrameInfo(info, toNonRotatedDisplay); + fillInputFrameInfo(info, displayTransform); // For compatibility reasons we let layers which can receive input // receive input before they have actually submitted a buffer. Because @@ -2326,15 +2296,11 @@ WindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) { auto cropLayer = mDrawingState.touchableRegionCrop.promote(); if (info.replaceTouchableRegionWithCrop) { - if (cropLayer == nullptr) { - info.touchableRegion = Region(toNonRotatedDisplay.transform(Rect{mScreenBounds})); - } else { - info.touchableRegion = - Region(toNonRotatedDisplay.transform(Rect{cropLayer->mScreenBounds})); - } + const Rect bounds(cropLayer ? cropLayer->mScreenBounds : mScreenBounds); + info.touchableRegion = Region(displayTransform.transform(bounds)); } else if (cropLayer != nullptr) { info.touchableRegion = info.touchableRegion.intersect( - toNonRotatedDisplay.transform(Rect{cropLayer->mScreenBounds})); + displayTransform.transform(Rect{cropLayer->mScreenBounds})); } // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state @@ -2345,9 +2311,8 @@ WindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) { // If the layer is a clone, we need to crop the input region to cloned root to prevent // touches from going outside the cloned area. if (isClone()) { - sp<Layer> clonedRoot = getClonedRoot(); - if (clonedRoot != nullptr) { - Rect rect = toNonRotatedDisplay.transform(Rect{clonedRoot->mScreenBounds}); + if (const sp<Layer> clonedRoot = getClonedRoot()) { + const Rect rect = displayTransform.transform(Rect{clonedRoot->mScreenBounds}); info.touchableRegion = info.touchableRegion.intersect(rect); } } @@ -2543,14 +2508,14 @@ scheduler::Seamlessness Layer::FrameRate::convertChangeFrameRateStrategy(int8_t } } -bool Layer::getPrimaryDisplayOnly() const { +bool Layer::isInternalDisplayOverlay() const { const State& s(mDrawingState); if (s.flags & layer_state_t::eLayerSkipScreenshot) { return true; } sp<Layer> parent = mDrawingParent.promote(); - return parent == nullptr ? false : parent->getPrimaryDisplayOnly(); + return parent && parent->isInternalDisplayOverlay(); } void Layer::setClonedChild(const sp<Layer>& clonedChild) { diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 470103c784..3c3c7d0b88 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -151,12 +151,7 @@ public: Geometry requested_legacy; int32_t z; - // The identifier of the layer stack this layer belongs to. A layer can - // only be associated to a single layer stack. A layer stack is a - // z-ordered group of layers which can be associated to one or more - // displays. Using the same layer stack on different displays is a way - // to achieve mirroring. - uint32_t layerStack; + ui::LayerStack layerStack; uint32_t flags; uint8_t reserved[2]; @@ -405,8 +400,8 @@ public: virtual bool setTransparentRegionHint(const Region& transparent); virtual bool setTrustedOverlay(bool); virtual bool setFlags(uint32_t flags, uint32_t mask); - virtual bool setLayerStack(uint32_t layerStack); - virtual uint32_t getLayerStack() const; + virtual bool setLayerStack(ui::LayerStack); + virtual ui::LayerStack getLayerStack() const; virtual bool setMetadata(const LayerMetadata& data); virtual void setChildrenDrawingParent(const sp<Layer>&); virtual bool reparent(const sp<IBinder>& newParentHandle); @@ -645,13 +640,12 @@ public: bool isLegacyDataSpace() const; uint32_t getTransactionFlags() const { return mTransactionFlags; } - uint32_t getTransactionFlags(uint32_t flags); - uint32_t setTransactionFlags(uint32_t flags); - // Deprecated, please use compositionengine::Output::belongsInOutput() - // instead. - // TODO(lpique): Move the remaining callers (screencap) to the new function. - bool belongsToDisplay(uint32_t layerStack) const { return getLayerStack() == layerStack; } + // Sets the masked bits. + void setTransactionFlags(uint32_t mask); + + // Clears and returns the masked bits. + uint32_t clearTransactionFlags(uint32_t mask); FloatRect getBounds(const Region& activeTransparentRegion) const; FloatRect getBounds() const; @@ -684,6 +678,14 @@ public: */ bool isHiddenByPolicy() const; + // True if the layer should be skipped in screenshots, screen recordings, + // and mirroring to external or virtual displays. + bool isInternalDisplayOverlay() const; + + ui::LayerFilter getOutputFilter() const { + return {getLayerStack(), isInternalDisplayOverlay()}; + } + bool isRemovedFromCurrentState() const; LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags, const DisplayDevice*); @@ -700,8 +702,6 @@ public: gui::WindowInfo::Type getWindowType() const { return mWindowType; } - bool getPrimaryDisplayOnly() const; - void updateMirrorInfo(); /* @@ -851,7 +851,8 @@ public: bool getPremultipledAlpha() const; void setInputInfo(const gui::WindowInfo& info); - gui::WindowInfo fillInputInfo(const sp<DisplayDevice>& display); + gui::WindowInfo fillInputInfo(const ui::Transform& displayTransform); + /** * Returns whether this layer has an explicitly set input-info. */ @@ -1072,8 +1073,8 @@ private: // hasInputInfo() or no-op if no such parent is found. void fillTouchOcclusionMode(gui::WindowInfo& info); - // Fills in the frame and transform info for the gui::WindowInfo - void fillInputFrameInfo(gui::WindowInfo& info, const ui::Transform& toNonRotatedDisplay); + // Fills in the frame and transform info for the gui::WindowInfo. + void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& displayTransform); // Cached properties computed from drawing state // Effective transform taking into account parent transforms and any parent scaling, which is diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp index aee820a88c..f52e60deda 100644 --- a/services/surfaceflinger/LayerVector.cpp +++ b/services/surfaceflinger/LayerVector.cpp @@ -45,8 +45,8 @@ int LayerVector::do_compare(const void* lhs, const void* rhs) const const auto& lState = l->getDrawingState(); const auto& rState = r->getDrawingState(); - uint32_t ls = lState.layerStack; - uint32_t rs = rState.layerStack; + const auto ls = lState.layerStack; + const auto rs = rState.layerStack; if (ls != rs) return (ls > rs) ? 1 : -1; diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index bc32a1d66c..0789e8dcb4 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -25,6 +25,10 @@ #include "Client.h" #include "Layer.h" +#include <SkBlendMode.h> +#include <SkPaint.h> +#include <SkRect.h> +#include <SkSurface.h> #include <gui/IProducerListener.h> #undef LOG_TAG @@ -32,85 +36,64 @@ namespace android { -void RefreshRateOverlay::SevenSegmentDrawer::drawRect(const Rect& r, const half4& color, - const sp<GraphicBuffer>& buffer, - uint8_t* pixels) { - for (int32_t j = r.top; j < r.bottom; j++) { - if (j >= buffer->getHeight()) { - break; - } - - for (int32_t i = r.left; i < r.right; i++) { - if (i >= buffer->getWidth()) { - break; - } - - uint8_t* iter = pixels + 4 * (i + (buffer->getStride() * j)); - iter[0] = uint8_t(color.r * 255); - iter[1] = uint8_t(color.g * 255); - iter[2] = uint8_t(color.b * 255); - iter[3] = uint8_t(color.a * 255); - } - } -} - -void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, - const half4& color, - const sp<GraphicBuffer>& buffer, - uint8_t* pixels) { - const Rect rect = [&]() { +void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, SkColor& color, + SkCanvas& canvas) { + const SkRect rect = [&]() { switch (segment) { case Segment::Upper: - return Rect(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE); + return SkRect::MakeLTRB(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE); case Segment::UpperLeft: - return Rect(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2); + return SkRect::MakeLTRB(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2); case Segment::UpperRight: - return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH, - DIGIT_HEIGHT / 2); + return SkRect::MakeLTRB(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH, + DIGIT_HEIGHT / 2); case Segment::Middle: - return Rect(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2, left + DIGIT_WIDTH, - DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2); + return SkRect::MakeLTRB(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2, + left + DIGIT_WIDTH, DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2); case Segment::LowerLeft: - return Rect(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT); + return SkRect::MakeLTRB(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT); case Segment::LowerRight: - return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, DIGIT_HEIGHT / 2, left + DIGIT_WIDTH, - DIGIT_HEIGHT); - case Segment::Buttom: - return Rect(left, DIGIT_HEIGHT - DIGIT_SPACE, left + DIGIT_WIDTH, DIGIT_HEIGHT); + return SkRect::MakeLTRB(left + DIGIT_WIDTH - DIGIT_SPACE, DIGIT_HEIGHT / 2, + left + DIGIT_WIDTH, DIGIT_HEIGHT); + case Segment::Bottom: + return SkRect::MakeLTRB(left, DIGIT_HEIGHT - DIGIT_SPACE, left + DIGIT_WIDTH, + DIGIT_HEIGHT); } }(); - drawRect(rect, color, buffer, pixels); + SkPaint paint; + paint.setColor(color); + paint.setBlendMode(SkBlendMode::kSrc); + canvas.drawRect(rect, paint); } -void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, const half4& color, - const sp<GraphicBuffer>& buffer, - uint8_t* pixels) { +void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, SkColor& color, + SkCanvas& canvas) { if (digit < 0 || digit > 9) return; if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 || digit == 8 || digit == 9) - drawSegment(Segment::Upper, left, color, buffer, pixels); + drawSegment(Segment::Upper, left, color, canvas); if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9) - drawSegment(Segment::UpperLeft, left, color, buffer, pixels); + drawSegment(Segment::UpperLeft, left, color, canvas); if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 || digit == 8 || digit == 9) - drawSegment(Segment::UpperRight, left, color, buffer, pixels); + drawSegment(Segment::UpperRight, left, color, canvas); if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9) - drawSegment(Segment::Middle, left, color, buffer, pixels); + drawSegment(Segment::Middle, left, color, canvas); if (digit == 0 || digit == 2 || digit == 6 || digit == 8) - drawSegment(Segment::LowerLeft, left, color, buffer, pixels); + drawSegment(Segment::LowerLeft, left, color, canvas); if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 7 || digit == 8 || digit == 9) - drawSegment(Segment::LowerRight, left, color, buffer, pixels); + drawSegment(Segment::LowerRight, left, color, canvas); if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 || digit == 9) - drawSegment(Segment::Buttom, left, color, buffer, pixels); + drawSegment(Segment::Bottom, left, color, canvas); } -std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::drawNumber( - int number, const half4& color, bool showSpinner) { +std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::draw( + int number, SkColor& color, ui::Transform::RotationFlags rotation, bool showSpinner) { if (number < 0 || number > 1000) return {}; const auto hundreds = number / 100; @@ -120,55 +103,76 @@ std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::drawNumbe std::vector<sp<GraphicBuffer>> buffers; const auto loopCount = showSpinner ? 6 : 1; for (int i = 0; i < loopCount; i++) { + // Pre-rotate the buffer before it reaches SurfaceFlinger. + SkMatrix canvasTransform = SkMatrix(); + auto [bufferWidth, bufferHeight] = [&] { + switch (rotation) { + case ui::Transform::ROT_90: + canvasTransform.setTranslate(BUFFER_HEIGHT, 0); + canvasTransform.preRotate(90); + return std::make_tuple(BUFFER_HEIGHT, BUFFER_WIDTH); + case ui::Transform::ROT_270: + canvasTransform.setRotate(270, BUFFER_WIDTH / 2.0, BUFFER_WIDTH / 2.0); + return std::make_tuple(BUFFER_HEIGHT, BUFFER_WIDTH); + default: + return std::make_tuple(BUFFER_WIDTH, BUFFER_HEIGHT); + } + }(); sp<GraphicBuffer> buffer = - new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1, + new GraphicBuffer(bufferWidth, bufferHeight, HAL_PIXEL_FORMAT_RGBA_8888, 1, GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE, "RefreshRateOverlayBuffer"); const status_t bufferStatus = buffer->initCheck(); LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "RefreshRateOverlay: Buffer failed to allocate: %d", bufferStatus); - uint8_t* pixels; - buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels)); - // Clear buffer content - drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels); + + sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(bufferWidth, bufferHeight); + SkCanvas* canvas = surface->getCanvas(); + canvas->setMatrix(canvasTransform); + int left = 0; if (hundreds != 0) { - drawDigit(hundreds, left, color, buffer, pixels); + drawDigit(hundreds, left, color, *canvas); } left += DIGIT_WIDTH + DIGIT_SPACE; if (tens != 0) { - drawDigit(tens, left, color, buffer, pixels); + drawDigit(tens, left, color, *canvas); } left += DIGIT_WIDTH + DIGIT_SPACE; - drawDigit(ones, left, color, buffer, pixels); + drawDigit(ones, left, color, *canvas); left += DIGIT_WIDTH + DIGIT_SPACE; if (showSpinner) { switch (i) { case 0: - drawSegment(Segment::Upper, left, color, buffer, pixels); + drawSegment(Segment::Upper, left, color, *canvas); break; case 1: - drawSegment(Segment::UpperRight, left, color, buffer, pixels); + drawSegment(Segment::UpperRight, left, color, *canvas); break; case 2: - drawSegment(Segment::LowerRight, left, color, buffer, pixels); + drawSegment(Segment::LowerRight, left, color, *canvas); break; case 3: - drawSegment(Segment::Buttom, left, color, buffer, pixels); + drawSegment(Segment::Bottom, left, color, *canvas); break; case 4: - drawSegment(Segment::LowerLeft, left, color, buffer, pixels); + drawSegment(Segment::LowerLeft, left, color, *canvas); break; case 5: - drawSegment(Segment::UpperLeft, left, color, buffer, pixels); + drawSegment(Segment::UpperLeft, left, color, *canvas); break; } } + void* pixels = nullptr; + buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels)); + const SkImageInfo& imageInfo = surface->imageInfo(); + size_t dstRowBytes = buffer->getStride() * imageInfo.bytesPerPixel(); + canvas->readPixels(imageInfo, pixels, dstRowBytes, 0, 0); buffer->unlock(); buffers.emplace_back(buffer); } @@ -214,7 +218,22 @@ bool RefreshRateOverlay::createLayer() { const std::vector<std::shared_ptr<renderengine::ExternalTexture>>& RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) { - if (mBufferCache.find(fps) == mBufferCache.end()) { + ui::Transform::RotationFlags transformHint = mLayer->getTransformHint(); + // Tell SurfaceFlinger about the pre-rotation on the buffer. + const auto transform = [&] { + switch (transformHint) { + case ui::Transform::ROT_90: + return ui::Transform::ROT_270; + case ui::Transform::ROT_270: + return ui::Transform::ROT_90; + default: + return ui::Transform::ROT_0; + } + }(); + mLayer->setTransform(transform); + + if (mBufferCache.find(transformHint) == mBufferCache.end() || + mBufferCache.at(transformHint).find(fps) == mBufferCache.at(transformHint).end()) { // Ensure the range is > 0, so we don't divide by 0. const auto rangeLength = std::max(1u, mHighFps - mLowFps); // Clip values outside the range [mLowFps, mHighFps]. The current fps may be outside @@ -222,12 +241,14 @@ RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) { fps = std::max(fps, mLowFps); fps = std::min(fps, mHighFps); const auto fpsScale = static_cast<float>(fps - mLowFps) / rangeLength; - half4 color; - color.r = HIGH_FPS_COLOR.r * fpsScale + LOW_FPS_COLOR.r * (1 - fpsScale); - color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale); - color.b = HIGH_FPS_COLOR.b * fpsScale + LOW_FPS_COLOR.b * (1 - fpsScale); - color.a = ALPHA; - auto buffers = SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner); + SkColor4f colorBase = SkColor4f::FromColor(HIGH_FPS_COLOR) * fpsScale; + SkColor4f lowFpsColor = SkColor4f::FromColor(LOW_FPS_COLOR) * (1 - fpsScale); + colorBase.fR = colorBase.fR + lowFpsColor.fR; + colorBase.fG = colorBase.fG + lowFpsColor.fG; + colorBase.fB = colorBase.fB + lowFpsColor.fB; + colorBase.fA = ALPHA; + SkColor color = colorBase.toSkColor(); + auto buffers = SevenSegmentDrawer::draw(fps, color, transformHint, mShowSpinner); std::vector<std::shared_ptr<renderengine::ExternalTexture>> textures; std::transform(buffers.begin(), buffers.end(), std::back_inserter(textures), [&](const auto& buffer) -> std::shared_ptr<renderengine::ExternalTexture> { @@ -237,10 +258,10 @@ RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) { renderengine::ExternalTexture:: Usage::READABLE); }); - mBufferCache.emplace(fps, textures); + mBufferCache[transformHint].emplace(fps, textures); } - return mBufferCache[fps]; + return mBufferCache[transformHint][fps]; } void RefreshRateOverlay::setViewport(ui::Size viewport) { @@ -260,7 +281,7 @@ void RefreshRateOverlay::setViewport(ui::Size viewport) { mFlinger.mTransactionFlags.fetch_or(eTransactionMask); } -void RefreshRateOverlay::setLayerStack(uint32_t stack) { +void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) { mLayer->setLayerStack(stack); mFlinger.mTransactionFlags.fetch_or(eTransactionMask); } diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h index f9baa898dc..63ae383c8c 100644 --- a/services/surfaceflinger/RefreshRateOverlay.h +++ b/services/surfaceflinger/RefreshRateOverlay.h @@ -18,10 +18,13 @@ #include <math/vec4.h> #include <renderengine/RenderEngine.h> +#include <ui/LayerStack.h> #include <ui/Rect.h> #include <ui/Size.h> #include <utils/StrongPointer.h> +#include <SkCanvas.h> +#include <SkColor.h> #include <unordered_map> #include "Fps.h" @@ -39,7 +42,7 @@ class RefreshRateOverlay { public: RefreshRateOverlay(SurfaceFlinger&, uint32_t lowFps, uint32_t highFps, bool showSpinner); - void setLayerStack(uint32_t stack); + void setLayerStack(ui::LayerStack); void setViewport(ui::Size); void changeRefreshRate(const Fps&); void onInvalidate(); @@ -47,20 +50,16 @@ public: private: class SevenSegmentDrawer { public: - static std::vector<sp<GraphicBuffer>> drawNumber(int number, const half4& color, - bool showSpinner); + static std::vector<sp<GraphicBuffer>> draw(int number, SkColor& color, + ui::Transform::RotationFlags, bool showSpinner); static uint32_t getHeight() { return BUFFER_HEIGHT; } static uint32_t getWidth() { return BUFFER_WIDTH; } private: - enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Buttom }; + enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Bottom }; - static void drawRect(const Rect& r, const half4& color, const sp<GraphicBuffer>& buffer, - uint8_t* pixels); - static void drawSegment(Segment segment, int left, const half4& color, - const sp<GraphicBuffer>& buffer, uint8_t* pixels); - static void drawDigit(int digit, int left, const half4& color, - const sp<GraphicBuffer>& buffer, uint8_t* pixels); + static void drawSegment(Segment segment, int left, SkColor& color, SkCanvas& canvas); + static void drawDigit(int digit, int left, SkColor& color, SkCanvas& canvas); static constexpr uint32_t DIGIT_HEIGHT = 100; static constexpr uint32_t DIGIT_WIDTH = 64; @@ -80,13 +79,15 @@ private: sp<IBinder> mIBinder; sp<IGraphicBufferProducer> mGbp; - std::unordered_map<int, std::vector<std::shared_ptr<renderengine::ExternalTexture>>> + std::unordered_map< + ui::Transform::RotationFlags, + std::unordered_map<int, std::vector<std::shared_ptr<renderengine::ExternalTexture>>>> mBufferCache; std::optional<int> mCurrentFps; int mFrame = 0; static constexpr float ALPHA = 0.8f; - const half3 LOW_FPS_COLOR = half3(1.0f, 0.0f, 0.0f); - const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f); + const SkColor LOW_FPS_COLOR = SK_ColorRED; + const SkColor HIGH_FPS_COLOR = SK_ColorGREEN; const bool mShowSpinner; diff --git a/services/surfaceflinger/RenderArea.cpp b/services/surfaceflinger/RenderArea.cpp index 9a6c8533ea..5fea521f18 100644 --- a/services/surfaceflinger/RenderArea.cpp +++ b/services/surfaceflinger/RenderArea.cpp @@ -14,10 +14,6 @@ * limitations under the License. */ -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - #include "RenderArea.h" namespace android { @@ -33,6 +29,3 @@ float RenderArea::getCaptureFillValue(CaptureFill captureFill) { } } // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index 81a669aa09..b00423e6b7 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -140,6 +140,8 @@ float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& laye return 0; } + constexpr float kScoreForFractionalPairs = .8f; + // Slightly prefer seamless switches. constexpr float kSeamedSwitchPenalty = 0.95f; const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty; @@ -156,19 +158,29 @@ float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& laye const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs(); if (layer.vote == LayerVoteType::ExplicitDefault) { // Find the actual rate the layer will render, assuming - // that layerPeriod is the minimal time to render a frame + // that layerPeriod is the minimal period to render a frame. + // For example if layerPeriod is 20ms and displayPeriod is 16ms, + // then the actualLayerPeriod will be 32ms, because it is the + // smallest multiple of the display period which is >= layerPeriod. auto actualLayerPeriod = displayPeriod; int multiplier = 1; while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) { multiplier++; actualLayerPeriod = displayPeriod * multiplier; } + + // Because of the threshold we used above it's possible that score is slightly + // above 1. return std::min(1.0f, static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod)); } if (layer.vote == LayerVoteType::ExplicitExactOrMultiple || layer.vote == LayerVoteType::Heuristic) { + if (isFractionalPairOrMultiple(refreshRate.getFps(), layer.desiredRefreshRate)) { + return kScoreForFractionalPairs * seamlessness; + } + // Calculate how many display vsyncs we need to present a single frame for this // layer const auto [displayFramesQuotient, displayFramesRemainder] = @@ -318,18 +330,28 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 || explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0; + const Policy* policy = getCurrentPolicyLocked(); + const auto& defaultMode = mRefreshRates.at(policy->defaultMode); + // If the default mode group is different from the group of current mode, + // this means a layer requesting a seamed mode switch just disappeared and + // we should switch back to the default group. + // However if a seamed layer is still present we anchor around the group + // of the current mode, in order to prevent unnecessary seamed mode switches + // (e.g. when pausing a video playback). + const auto anchorGroup = seamedFocusedLayers > 0 ? mCurrentRefreshRate->getModeGroup() + : defaultMode->getModeGroup(); + // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've // selected a refresh rate to see if we should apply touch boost. if (globalSignals.touch && !hasExplicitVoteLayers) { ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str()); setTouchConsidered(); - return getMaxRefreshRateByPolicyLocked(); + return getMaxRefreshRateByPolicyLocked(anchorGroup); } // If the primary range consists of a single refresh rate then we can only // move out the of range if layers explicitly request a different refresh // rate. - const Policy* policy = getCurrentPolicyLocked(); const bool primaryRangeIsSingleRate = policy->primaryRange.min.equalsWithMargin(policy->primaryRange.max); @@ -341,7 +363,9 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( } if (layers.empty() || noVoteLayers == layers.size()) { - return getMaxRefreshRateByPolicyLocked(); + const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup); + ALOGV("no layers with votes - choose %s", refreshRate.getName().c_str()); + return refreshRate; } // Only if all layers want Min we should return Min @@ -358,8 +382,6 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( scores.emplace_back(RefreshRateScore{refreshRate, 0.0f}); } - const auto& defaultMode = mRefreshRates.at(policy->defaultMode); - for (const auto& layer : layers) { ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(), layerVoteTypeString(layer.vote).c_str(), layer.weight, @@ -397,10 +419,7 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( // mode group otherwise. In second case, if the current mode group is different // from the default, this means a layer with seamlessness=SeamedAndSeamless has just // disappeared. - const bool isInPolicyForDefault = seamedFocusedLayers > 0 - ? scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup() - : scores[i].refreshRate->getModeGroup() == defaultMode->getModeGroup(); - + const bool isInPolicyForDefault = scores[i].refreshRate->getModeGroup() == anchorGroup; if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) { ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(), scores[i].refreshRate->toString().c_str(), @@ -421,7 +440,7 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( const auto layerScore = calculateLayerScoreLocked(layer, *scores[i].refreshRate, isSeamlessSwitch); - ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(), + ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(), scores[i].refreshRate->getName().c_str(), layerScore); scores[i].score += weight * layerScore; } @@ -439,9 +458,9 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( // range instead of picking a random score from the app range. if (std::all_of(scores.begin(), scores.end(), [](RefreshRateScore score) { return score.score == 0; })) { - ALOGV("layers not scored - choose %s", - getMaxRefreshRateByPolicyLocked().getName().c_str()); - return getMaxRefreshRateByPolicyLocked(); + const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup); + ALOGV("layers not scored - choose %s", refreshRate.getName().c_str()); + return refreshRate; } else { return *bestRefreshRate; } @@ -451,7 +470,7 @@ RefreshRate RefreshRateConfigs::getBestRefreshRateLocked( // interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit // vote we should not change it if we get a touch event. Only apply touch boost if it will // actually increase the refresh rate over the normal selection. - const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(); + const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup); const bool touchBoostForExplicitExact = [&] { if (mSupportsFrameRateOverride) { @@ -582,7 +601,7 @@ RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverr template <typename Iter> const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const { - constexpr auto EPSILON = 0.001f; + constexpr auto kEpsilon = 0.0001f; const RefreshRate* bestRefreshRate = begin->refreshRate; float max = begin->score; for (auto i = begin; i != end; ++i) { @@ -591,7 +610,7 @@ const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) ATRACE_INT(refreshRate->getName().c_str(), round<int>(score * 100)); - if (score > max * (1 + EPSILON)) { + if (score > max * (1 + kEpsilon)) { max = score; bestRefreshRate = refreshRate; } @@ -634,10 +653,10 @@ RefreshRate RefreshRateConfigs::getMaxRefreshRateByPolicy() const { return getMaxRefreshRateByPolicyLocked(); } -const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked() const { +const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int anchorGroup) const { for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); it++) { const auto& refreshRate = (**it); - if (mCurrentRefreshRate->getModeGroup() == refreshRate.getModeGroup()) { + if (anchorGroup == refreshRate.getModeGroup()) { return refreshRate; } } @@ -923,6 +942,17 @@ int RefreshRateConfigs::getFrameRateDivider(Fps displayFrameRate, Fps layerFrame return static_cast<int>(numPeriodsRounded); } +bool RefreshRateConfigs::isFractionalPairOrMultiple(Fps smaller, Fps bigger) { + if (smaller.getValue() > bigger.getValue()) { + return isFractionalPairOrMultiple(bigger, smaller); + } + + const auto multiplier = std::round(bigger.getValue() / smaller.getValue()); + constexpr float kCoef = 1000.f / 1001.f; + return bigger.equalsWithMargin(Fps(smaller.getValue() * multiplier / kCoef)) || + bigger.equalsWithMargin(Fps(smaller.getValue() * multiplier * kCoef)); +} + void RefreshRateConfigs::dump(std::string& result) const { std::lock_guard lock(mLock); base::StringAppendF(&result, "DesiredDisplayModeSpecs (DisplayManager): %s\n\n", diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index 4a9a1fd9fc..3713587393 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -335,6 +335,10 @@ public: // layer refresh rate. static int getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate); + // Returns if the provided frame rates have a ratio t*1000/1001 or t*1001/1000 + // for an integer t. + static bool isFractionalPairOrMultiple(Fps, Fps); + using UidToFrameRateOverride = std::map<uid_t, Fps>; // Returns the frame rate override for each uid. // @@ -384,7 +388,11 @@ private: // Returns the highest refresh rate according to the current policy. May change at runtime. Only // uses the primary range, not the app request range. - const RefreshRate& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock); + const RefreshRate& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock) { + return getMaxRefreshRateByPolicyLocked(mCurrentRefreshRate->getModeGroup()); + } + + const RefreshRate& getMaxRefreshRateByPolicyLocked(int anchorGroup) const REQUIRES(mLock); // Returns the current refresh rate, if allowed. Otherwise the default that is allowed by // the policy. diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 80b2504a2c..c64ccd10cb 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -677,7 +677,7 @@ void Scheduler::resetIdleTimer() { } } -void Scheduler::notifyTouchEvent() { +void Scheduler::onTouchHint() { if (mTouchTimer) { mTouchTimer->reset(); @@ -886,7 +886,7 @@ DisplayModePtr Scheduler::getPreferredDisplayMode() { void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) { if (timeline.refreshRequired) { - mSchedulerCallback.repaintEverythingForHWC(); + mSchedulerCallback.scheduleRefresh(FrameHint::kNone); } std::lock_guard<std::mutex> lock(mVsyncTimelineLock); @@ -899,21 +899,21 @@ void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimel } void Scheduler::onDisplayRefreshed(nsecs_t timestamp) { - bool callRepaint = false; - { + const bool refresh = [=] { std::lock_guard<std::mutex> lock(mVsyncTimelineLock); if (mLastVsyncPeriodChangeTimeline && mLastVsyncPeriodChangeTimeline->refreshRequired) { - if (mLastVsyncPeriodChangeTimeline->refreshTimeNanos < timestamp) { - mLastVsyncPeriodChangeTimeline->refreshRequired = false; - } else { - // We need to send another refresh as refreshTimeNanos is still in the future - callRepaint = true; + if (timestamp < mLastVsyncPeriodChangeTimeline->refreshTimeNanos) { + // We need to schedule another refresh as refreshTimeNanos is still in the future. + return true; } + + mLastVsyncPeriodChangeTimeline->refreshRequired = false; } - } + return false; + }(); - if (callRepaint) { - mSchedulerCallback.repaintEverythingForHWC(); + if (refresh) { + mSchedulerCallback.scheduleRefresh(FrameHint::kNone); } } diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 4b6905bc6d..420ba61a14 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -56,10 +56,13 @@ class TokenManager; } // namespace frametimeline struct ISchedulerCallback { + // Indicates frame activity, i.e. whether commit and/or composite is taking place. + enum class FrameHint { kNone, kActive }; + + virtual void scheduleRefresh(FrameHint) = 0; virtual void setVsyncEnabled(bool) = 0; virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&, scheduler::RefreshRateConfigEvent) = 0; - virtual void repaintEverythingForHWC() = 0; virtual void kernelTimerChanged(bool expired) = 0; virtual void triggerOnFrameRateOverridesChanged() = 0; @@ -136,8 +139,8 @@ public: bool isIdleTimerEnabled() const { return mIdleTimer.has_value(); } void resetIdleTimer(); - // Function that resets the touch timer. - void notifyTouchEvent(); + // Indicates that touch interaction is taking place. + void onTouchHint(); void setDisplayPowerState(bool normal); @@ -194,6 +197,8 @@ public: private: friend class TestableScheduler; + using FrameHint = ISchedulerCallback::FrameHint; + // In order to make sure that the features don't override themselves, we need a state machine // to keep track which feature requested the config change. enum class ContentDetectionState { Off, On }; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index bfe619c3e3..5a881a3dfe 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -109,6 +109,7 @@ #include "DisplayRenderArea.h" #include "EffectLayer.h" #include "Effects/Daltonizer.h" +#include "FlagManager.h" #include "FpsReporter.h" #include "FrameTimeline/FrameTimeline.h" #include "FrameTracer/FrameTracer.h" @@ -168,6 +169,7 @@ using namespace android::sysprop; using android::hardware::power::Boost; using base::StringAppendF; +using gui::DisplayInfo; using gui::IWindowInfosListener; using gui::WindowInfo; using ui::ColorMode; @@ -416,10 +418,7 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI property_get("ro.build.type", value, "user"); mIsUserBuild = strcmp(value, "user") == 0; - property_get("debug.sf.showupdates", value, "0"); - mDebugRegion = atoi(value); - - ALOGI_IF(mDebugRegion, "showupdates enabled"); + mDebugFlashDelay = base::GetUintProperty("debug.sf.showupdates"s, 0u); // DDMS debugging deprecated (b/120782499) property_get("debug.sf.ddms", value, "0"); @@ -619,17 +618,20 @@ void SurfaceFlinger::releaseVirtualDisplay(VirtualDisplayId displayId) { } std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const { - const auto display = getDefaultDisplayDeviceLocked(); - if (!display) { - return {}; - } - std::vector<PhysicalDisplayId> displayIds; displayIds.reserve(mPhysicalDisplayTokens.size()); - displayIds.push_back(display->getPhysicalId()); + const auto defaultDisplayId = [this]() REQUIRES(mStateLock) { + if (const auto display = getDefaultDisplayDeviceLocked()) { + return display->getPhysicalId(); + } + + // fallback to the internal display id if the active display is unknown + return getInternalDisplayIdLocked(); + }(); + displayIds.push_back(defaultDisplayId); for (const auto& [id, token] : mPhysicalDisplayTokens) { - if (id != display->getPhysicalId()) { + if (id != defaultDisplayId) { displayIds.push_back(id); } } @@ -679,6 +681,7 @@ void SurfaceFlinger::bootFinished() { const nsecs_t duration = now - mBootTime; ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) ); + mFlagManager = std::make_unique<android::FlagManager>(); mFrameTracer->initialize(); mFrameTimeline->onBootFinished(); @@ -796,10 +799,10 @@ void SurfaceFlinger::init() { // Process any initial hotplug and resulting display changes. processDisplayHotplugEventsLocked(); const auto display = getDefaultDisplayDeviceLocked(); - LOG_ALWAYS_FATAL_IF(!display, "Missing internal display after registering composer callback."); + LOG_ALWAYS_FATAL_IF(!display, "Missing primary display after registering composer callback."); const auto displayId = display->getPhysicalId(); LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(displayId), - "Internal display is disconnected."); + "Primary display is disconnected."); // initialize our drawing state mDrawingState = mCurrentState; @@ -968,6 +971,11 @@ status_t SurfaceFlinger::getDynamicDisplayInfo(const sp<IBinder>& displayToken, return NAME_NOT_FOUND; } + const auto displayId = PhysicalDisplayId::tryCast(display->getId()); + if (!displayId) { + return INVALID_OPERATION; + } + info->activeDisplayModeId = static_cast<int32_t>(display->getActiveMode()->getId().value()); const auto& supportedModes = display->getSupportedModes(); @@ -1027,18 +1035,18 @@ status_t SurfaceFlinger::getDynamicDisplayInfo(const sp<IBinder>& displayToken, } info->activeColorMode = display->getCompositionDisplay()->getState().colorMode; - const auto displayId = display->getPhysicalId(); - info->supportedColorModes = getDisplayColorModes(displayId); - + info->supportedColorModes = getDisplayColorModes(*display); info->hdrCapabilities = display->getHdrCapabilities(); + info->autoLowLatencyModeSupported = - getHwComposer().hasDisplayCapability(displayId, + getHwComposer().hasDisplayCapability(*displayId, hal::DisplayCapability::AUTO_LOW_LATENCY_MODE); std::vector<hal::ContentType> types; - getHwComposer().getSupportedContentTypes(displayId, &types); + getHwComposer().getSupportedContentTypes(*displayId, &types); info->gameContentTypeSupported = std::any_of(types.begin(), types.end(), [](auto type) { return type == hal::ContentType::GAME; }); + return NO_ERROR; } @@ -1065,8 +1073,8 @@ void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) { } if (display->setDesiredActiveMode(info)) { - // This will trigger HWC refresh without resetting the idle timer. - repaintEverythingForHWC(); + scheduleRefresh(FrameHint::kNone); + // Start receiving vsync samples now, so that we can detect a period // switch. mScheduler->resyncToHardwareVsync(true, info.mode->getVsyncPeriod()); @@ -1262,15 +1270,15 @@ void SurfaceFlinger::disableExpensiveRendering() { }).wait(); } -std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(PhysicalDisplayId displayId) { - auto modes = getHwComposer().getColorModes(displayId); - bool isInternalDisplay = displayId == getInternalDisplayIdLocked(); +std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(const DisplayDevice& display) { + auto modes = getHwComposer().getColorModes(display.getPhysicalId()); - // If it's built-in display and the configuration claims it's not wide color capable, + // If the display is internal and the configuration claims it's not wide color capable, // filter out all wide color modes. The typical reason why this happens is that the // hardware is not good enough to support GPU composition of wide color, and thus the // OEMs choose to disable this capability. - if (isInternalDisplay && !hasWideColorDisplay) { + if (display.getConnectionType() == ui::DisplayConnectionType::Internal && + !hasWideColorDisplay) { const auto newEnd = std::remove_if(modes.begin(), modes.end(), isWideColorMode); modes.erase(newEnd, modes.end()); } @@ -1294,34 +1302,41 @@ status_t SurfaceFlinger::getDisplayNativePrimaries(const sp<IBinder>& displayTok } status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) { - schedule([=]() MAIN_THREAD { - const auto displayId = getPhysicalDisplayIdLocked(displayToken); - if (!displayId) { - ALOGE("Invalid display token %p", displayToken.get()); - return; - } - const auto modes = getDisplayColorModes(*displayId); - bool exists = std::find(std::begin(modes), std::end(modes), mode) != std::end(modes); - if (mode < ColorMode::NATIVE || !exists) { - ALOGE("Attempt to set invalid active color mode %s (%d) for display token %p", - decodeColorMode(mode).c_str(), mode, displayToken.get()); - return; - } + if (!displayToken) { + return BAD_VALUE; + } + + auto future = schedule([=]() MAIN_THREAD -> status_t { const auto display = getDisplayDeviceLocked(displayToken); if (!display) { ALOGE("Attempt to set active color mode %s (%d) for invalid display token %p", decodeColorMode(mode).c_str(), mode, displayToken.get()); - } else if (display->isVirtual()) { + return NAME_NOT_FOUND; + } + + if (display->isVirtual()) { ALOGW("Attempt to set active color mode %s (%d) for virtual display", decodeColorMode(mode).c_str(), mode); - } else { - display->getCompositionDisplay()->setColorProfile( - compositionengine::Output::ColorProfile{mode, Dataspace::UNKNOWN, - RenderIntent::COLORIMETRIC, - Dataspace::UNKNOWN}); + return INVALID_OPERATION; } - }).wait(); + const auto modes = getDisplayColorModes(*display); + const bool exists = std::find(modes.begin(), modes.end(), mode) != modes.end(); + + if (mode < ColorMode::NATIVE || !exists) { + ALOGE("Attempt to set invalid active color mode %s (%d) for display token %p", + decodeColorMode(mode).c_str(), mode, displayToken.get()); + return BAD_VALUE; + } + + display->getCompositionDisplay()->setColorProfile( + {mode, Dataspace::UNKNOWN, RenderIntent::COLORIMETRIC, Dataspace::UNKNOWN}); + + return NO_ERROR; + }); + + // TODO(b/195698395): Propagate error. + future.wait(); return NO_ERROR; } @@ -1641,7 +1656,7 @@ status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) { Boost powerBoost = static_cast<Boost>(boostId); if (powerBoost == Boost::INTERACTION) { - mScheduler->notifyTouchEvent(); + mScheduler->onTouchHint(); } return NO_ERROR; @@ -1658,16 +1673,26 @@ sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( return mScheduler->createDisplayEventConnection(handle, eventRegistration); } -void SurfaceFlinger::signalTransaction() { - mScheduler->resetIdleTimer(); +void SurfaceFlinger::scheduleInvalidate(FrameHint hint) { + if (hint == FrameHint::kActive) { + mScheduler->resetIdleTimer(); + } mPowerAdvisor.notifyDisplayUpdateImminent(); mEventQueue->invalidate(); } +void SurfaceFlinger::scheduleRefresh(FrameHint hint) { + mForceRefresh = true; + scheduleInvalidate(hint); +} + +void SurfaceFlinger::scheduleRepaint() { + mGeometryDirty = true; + scheduleRefresh(FrameHint::kActive); +} + void SurfaceFlinger::signalLayerUpdate() { - mScheduler->resetIdleTimer(); - mPowerAdvisor.notifyDisplayUpdateImminent(); - mEventQueue->invalidate(); + scheduleInvalidate(FrameHint::kActive); } void SurfaceFlinger::signalRefresh() { @@ -1738,8 +1763,8 @@ void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate, void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId, hal::Connection connection) { - ALOGI("%s(%" PRIu64 ", %s)", __func__, hwcDisplayId, - connection == hal::Connection::CONNECTED ? "connected" : "disconnected"); + const bool connected = connection == hal::Connection::CONNECTED; + ALOGI("%s HAL display %" PRIu64, connected ? "Connecting" : "Disconnecting", hwcDisplayId); // Only lock if we're not on the main thread. This function is normally // called on a hwbinder thread, but for the primary display it's called on @@ -1770,7 +1795,7 @@ void SurfaceFlinger::onComposerHalSeamlessPossible(hal::HWDisplayId) { void SurfaceFlinger::onComposerHalRefresh(hal::HWDisplayId) { Mutex::Autolock lock(mStateLock); - repaintEverythingForHWC(); + scheduleRefresh(FrameHint::kNone); } void SurfaceFlinger::setVsyncEnabled(bool enabled) { @@ -1931,17 +1956,13 @@ void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncT } if (mRefreshRateOverlaySpinner) { - if (Mutex::Autolock lock(mStateLock); - const auto display = getDefaultDisplayDeviceLocked()) { - if (display) { - display->onInvalidate(); - } else { - ALOGW("%s: default display is null", __func__); - } + Mutex::Autolock lock(mStateLock); + if (const auto display = getDefaultDisplayDeviceLocked()) { + display->onInvalidate(); } } - bool refreshNeeded; + bool refreshNeeded = mForceRefresh.exchange(false); { mTracePostComposition = mTracing.flagIsSet(SurfaceTracing::TRACE_COMPOSITION) || mTracing.flagIsSet(SurfaceTracing::TRACE_SYNC) || @@ -1951,8 +1972,9 @@ void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncT mFrameTimeline->setSfWakeUp(vsyncId, frameStart, Fps::fromPeriodNsecs(stats.vsyncPeriod)); - refreshNeeded = handleMessageTransaction(); + refreshNeeded |= flushAndCommitTransactions(); refreshNeeded |= handleMessageInvalidate(); + if (tracePreComposition) { if (mVisibleRegionsDirty) { mTracing.notifyLocked("visibleRegionsDirty"); @@ -1974,7 +1996,6 @@ void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncT updateCursorAsync(); updateInputFlinger(); - refreshNeeded |= mRepaintEverything; if (refreshNeeded && CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) { // Signal a refresh if a transaction modified the window state, // a new buffer was latched, or if HWC has requested a full @@ -1996,25 +2017,23 @@ void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncT notifyRegionSamplingThread(); } -bool SurfaceFlinger::handleMessageTransaction() { +bool SurfaceFlinger::flushAndCommitTransactions() { ATRACE_CALL(); - if (getTransactionFlags(eTransactionFlushNeeded)) { + if (clearTransactionFlags(eTransactionFlushNeeded)) { flushTransactionQueues(); } - uint32_t transactionFlags = peekTransactionFlags(); - bool runHandleTransaction = - ((transactionFlags & (~eTransactionFlushNeeded)) != 0) || mForceTraversal; - if (runHandleTransaction) { - handleTransaction(eTransactionMask); + const bool shouldCommit = (getTransactionFlags() & ~eTransactionFlushNeeded) || mForceTraversal; + if (shouldCommit) { + commitTransactions(); } if (transactionFlushNeeded()) { setTransactionFlags(eTransactionFlushNeeded); } - return runHandleTransaction; + return shouldCommit; } void SurfaceFlinger::onMessageRefresh() { @@ -2038,7 +2057,6 @@ void SurfaceFlinger::onMessageRefresh() { refreshArgs.layersWithQueuedFrames.push_back(layerFE); } - refreshArgs.repaintEverything = mRepaintEverything.exchange(false); refreshArgs.outputColorSetting = useColorManagement ? mDisplayColorSetting : compositionengine::OutputColorSetting::kUnmanaged; @@ -2046,7 +2064,7 @@ void SurfaceFlinger::onMessageRefresh() { refreshArgs.forceOutputColorMode = mForceColorMode; refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty; - refreshArgs.updatingGeometryThisFrame = mGeometryInvalid || mVisibleRegionsDirty; + refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty; refreshArgs.blursAreExpensive = mBlursAreExpensive; refreshArgs.internalDisplayRotationFlags = DisplayDevice::getPrimaryDisplayRotationFlags(); @@ -2055,11 +2073,11 @@ void SurfaceFlinger::onMessageRefresh() { mDrawingState.colorMatrixChanged = false; } - refreshArgs.devOptForceClientComposition = mDebugDisableHWC || mDebugRegion; + refreshArgs.devOptForceClientComposition = mDebugDisableHWC; - if (mDebugRegion != 0) { - refreshArgs.devOptFlashDirtyRegionsDelay = - std::chrono::milliseconds(mDebugRegion > 1 ? mDebugRegion : 0); + if (mDebugFlashDelay != 0) { + refreshArgs.devOptForceClientComposition = true; + refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay); } const auto prevVsyncTime = mScheduler->getPreviousVsyncFrom(mExpectedPresentTime); @@ -2068,8 +2086,6 @@ void SurfaceFlinger::onMessageRefresh() { refreshArgs.previousPresentFence = mPreviousPresentFences[0].fenceTime; refreshArgs.nextInvalidateTime = mEventQueue->nextExpectedInvalidate(); - mGeometryInvalid = false; - // Store the present time just before calling to the composition engine so we could notify // the scheduler. const auto presentTime = systemTime(); @@ -2291,7 +2307,7 @@ void SurfaceFlinger::postComposition() { int32_t maxArea = 0; mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) { const auto layerFe = layer->getCompositionEngineLayerFE(); - if (layer->isVisible() && compositionDisplay->belongsInOutput(layerFe)) { + if (layer->isVisible() && compositionDisplay->includesLayer(layerFe)) { const Dataspace transfer = static_cast<Dataspace>(layer->getDataSpace() & Dataspace::TRANSFER_MASK); const bool isHdr = (transfer == Dataspace::TRANSFER_ST2084 || @@ -2448,29 +2464,26 @@ void SurfaceFlinger::postFrame() { } } -void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) { +void SurfaceFlinger::commitTransactions() { ATRACE_CALL(); - // here we keep a copy of the drawing state (that is the state that's - // going to be overwritten by handleTransactionLocked()) outside of - // mStateLock so that the side-effects of the State assignment - // don't happen with mStateLock held (which can cause deadlocks). + // Keep a copy of the drawing state (that is going to be overwritten + // by commitTransactionsLocked) outside of mStateLock so that the side + // effects of the State assignment don't happen with mStateLock held, + // which can cause deadlocks. State drawingState(mDrawingState); - Mutex::Autolock _l(mStateLock); + Mutex::Autolock lock(mStateLock); mDebugInTransaction = systemTime(); // Here we're guaranteed that some transaction flags are set - // so we can call handleTransactionLocked() unconditionally. - // We call getTransactionFlags(), which will also clear the flags, - // with mStateLock held to guarantee that mCurrentState won't change - // until the transaction is committed. + // so we can call commitTransactionsLocked unconditionally. + // We clear the flags with mStateLock held to guarantee that + // mCurrentState won't change until the transaction is committed. modulateVsync(&VsyncModulator::onTransactionCommit); - transactionFlags = getTransactionFlags(eTransactionMask); - handleTransactionLocked(transactionFlags); + commitTransactionsLocked(clearTransactionFlags(eTransactionMask)); mDebugInTransaction = 0; - // here the transaction has been committed } void SurfaceFlinger::loadDisplayModes(PhysicalDisplayId displayId, DisplayModes& outModes, @@ -2733,14 +2746,12 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, compositionengine::DisplayCreationArgsBuilder builder; if (const auto& physical = state.physical) { builder.setId(physical->id); - builder.setConnectionType(physical->type); } else { builder.setId(acquireVirtualDisplay(resolution, pixelFormat)); } builder.setPixels(resolution); builder.setIsSecure(state.isSecure); - builder.setLayerStackId(state.layerStack); builder.setPowerAdvisor(&mPowerAdvisor); builder.setName(state.displayName); auto compositionDisplay = getCompositionEngine().createDisplay(builder.build()); @@ -2846,7 +2857,7 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, setPowerModeInternal(display, hal::PowerMode::ON); // TODO(b/175678251) Call a listener instead. - if (currentState.physical->hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) { + if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) { updateInternalDisplayVsyncLocked(display); } } @@ -2926,8 +2937,8 @@ void SurfaceFlinger::processDisplayChangesLocked() { mDrawingState.displays = mCurrentState.displays; } -void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) { - // Commit display transactions +void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) { + // Commit display transactions. const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded; if (displayTransactionNeeded) { processDisplayChangesLocked(); @@ -2941,18 +2952,9 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) { mSomeChildrenChanged = false; } - // Update transform hint + // Update transform hint. if (transactionFlags & (eTransformHintUpdateNeeded | eDisplayTransactionNeeded)) { - // The transform hint might have changed for some layers - // (either because a display has changed, or because a layer - // as changed). - // - // Walk through all the layers in currentLayers, - // and update their transform hint. - // - // If a layer is visible only on a single display, then that - // display is used to calculate the hint, otherwise we use the - // default display. + // Layers and/or displays have changed, so update the transform hint for each layer. // // NOTE: we do this here, rather than when presenting the display so that // the hint is set before we acquire a buffer from the surface texture. @@ -2963,30 +2965,29 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) { // (soon to become the drawing state list). // sp<const DisplayDevice> hintDisplay; - uint32_t currentlayerStack = 0; - bool first = true; + ui::LayerStack layerStack; + mCurrentState.traverse([&](Layer* layer) REQUIRES(mStateLock) { // NOTE: we rely on the fact that layers are sorted by // layerStack first (so we don't have to traverse the list // of displays for every layer). - uint32_t layerStack = layer->getLayerStack(); - if (first || currentlayerStack != layerStack) { - currentlayerStack = layerStack; - // figure out if this layerstack is mirrored - // (more than one display) if so, pick the default display, - // if not, pick the only display it's on. + if (const auto filter = layer->getOutputFilter(); layerStack != filter.layerStack) { + layerStack = filter.layerStack; hintDisplay = nullptr; + + // Find the display that includes the layer. for (const auto& [token, display] : mDisplays) { - if (display->getCompositionDisplay() - ->belongsInOutput(layer->getLayerStack(), - layer->getPrimaryDisplayOnly())) { - if (hintDisplay) { - hintDisplay = nullptr; - break; - } else { - hintDisplay = display; - } + if (!display->getCompositionDisplay()->includesLayer(filter)) { + continue; } + + // Pick the primary display if another display mirrors the layer. + if (hintDisplay) { + hintDisplay = nullptr; + break; + } + + hintDisplay = display; } } @@ -3000,20 +3001,10 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) { hintDisplay = getDefaultDisplayDeviceLocked(); } - // could be null if there is no display available at all to get - // the transform hint from. - if (hintDisplay) { - layer->updateTransformHint(hintDisplay->getTransformHint()); - } - - first = false; + layer->updateTransformHint(hintDisplay->getTransformHint()); }); } - /* - * Perform our own transaction if needed - */ - if (mLayersAdded) { mLayersAdded = false; // Layers have been added. @@ -3035,7 +3026,9 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) { }); } - commitTransaction(); + doCommitTransactions(); + signalSynchronousTransactions(CountDownLatch::eSyncTransaction); + mAnimTransactionPending = false; } void SurfaceFlinger::updateInputFlinger() { @@ -3067,17 +3060,32 @@ bool enablePerWindowInputRotation() { void SurfaceFlinger::notifyWindowInfos() { std::vector<WindowInfo> windowInfos; + std::vector<DisplayInfo> displayInfos; + std::unordered_map<const DisplayDevice*, const ui::Transform> displayTransforms; + + if (enablePerWindowInputRotation()) { + for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) { + const auto& [info, transform] = display->getInputInfo(); + displayInfos.emplace_back(info); + displayTransforms.emplace(display.get(), transform); + } + } mDrawingState.traverseInReverseZOrder([&](Layer* layer) { if (!layer->needsInputInfo()) return; - sp<DisplayDevice> display = enablePerWindowInputRotation() - ? ON_MAIN_THREAD(getDisplayWithInputByLayer(layer)) - : nullptr; + + const DisplayDevice* display = nullptr; + if (enablePerWindowInputRotation()) { + display = ON_MAIN_THREAD(getDisplayWithInputByLayer(layer)).get(); + } + // When calculating the screen bounds we ignore the transparent region since it may // result in an unwanted offset. - windowInfos.push_back(layer->fillInputInfo(display)); + const auto it = displayTransforms.find(display); + windowInfos.push_back( + layer->fillInputInfo(it != displayTransforms.end() ? it->second : ui::Transform())); }); - mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, + mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, displayInfos, mInputWindowCommands.syncInputWindows); } @@ -3181,14 +3189,9 @@ void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config, mEventQueue->setDuration(config.sfWorkDuration); } -void SurfaceFlinger::commitTransaction() { +void SurfaceFlinger::doCommitTransactions() { ATRACE_CALL(); - commitTransactionLocked(); - signalSynchronousTransactions(CountDownLatch::eSyncTransaction); - mAnimTransactionPending = false; -} -void SurfaceFlinger::commitTransactionLocked() { if (!mLayersPendingRemoval.isEmpty()) { // Notify removed layers now that they can't be drawn from for (const auto& l : mLayersPendingRemoval) { @@ -3232,11 +3235,10 @@ void SurfaceFlinger::commitTransactionLocked() { void SurfaceFlinger::commitOffscreenLayers() { for (Layer* offscreenLayer : mOffscreenLayers) { offscreenLayer->traverse(LayerVector::StateSet::Drawing, [](Layer* layer) { - uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded); - if (!trFlags) return; - - layer->doTransaction(0); - layer->commitChildList(); + if (layer->clearTransactionFlags(eTransactionNeeded)) { + layer->doTransaction(0); + layer->commitChildList(); + } }); } } @@ -3244,7 +3246,7 @@ void SurfaceFlinger::commitOffscreenLayers() { void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) { for (const auto& [token, displayDevice] : ON_MAIN_THREAD(mDisplays)) { auto display = displayDevice->getCompositionDisplay(); - if (display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) { + if (display->includesLayer(layer->getOutputFilter())) { display->editState().dirtyRegion.orSelf(dirty); } } @@ -3272,14 +3274,14 @@ bool SurfaceFlinger::handlePageFlip() { // Display is now waiting on Layer 1's frame, which is behind layer 0's // second frame. But layer 0's second frame could be waiting on display. mDrawingState.traverse([&](Layer* layer) { - uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded); - if (trFlags || mForceTransactionDisplayChange) { - const uint32_t flags = layer->doTransaction(0); - if (flags & Layer::eVisibleRegion) - mVisibleRegionsDirty = true; - } + if (layer->clearTransactionFlags(eTransactionNeeded) || mForceTransactionDisplayChange) { + const uint32_t flags = layer->doTransaction(0); + if (flags & Layer::eVisibleRegion) { + mVisibleRegionsDirty = true; + } + } - if (layer->hasReadyFrame()) { + if (layer->hasReadyFrame()) { frameQueued = true; if (layer->shouldPresentNow(expectedPresentTime)) { mLayersWithQueuedFrames.emplace(layer); @@ -3287,7 +3289,7 @@ bool SurfaceFlinger::handlePageFlip() { ATRACE_NAME("!layer->shouldPresentNow()"); layer->useEmptyDamage(); } - } else { + } else { layer->useEmptyDamage(); } }); @@ -3340,10 +3342,6 @@ bool SurfaceFlinger::handlePageFlip() { return !mLayersWithQueuedFrames.empty() && newDataLatched; } -void SurfaceFlinger::invalidateHwcGeometry() { - mGeometryInvalid = true; -} - status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle, const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc, const sp<IBinder>& parentHandle, @@ -3390,23 +3388,23 @@ void SurfaceFlinger::removeGraphicBufferProducerAsync(const wp<IBinder>& binder) })); } -uint32_t SurfaceFlinger::peekTransactionFlags() { +uint32_t SurfaceFlinger::getTransactionFlags() const { return mTransactionFlags; } -uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) { - return mTransactionFlags.fetch_and(~flags) & flags; +uint32_t SurfaceFlinger::clearTransactionFlags(uint32_t mask) { + return mTransactionFlags.fetch_and(~mask) & mask; } -uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) { - return setTransactionFlags(flags, TransactionSchedule::Late); +uint32_t SurfaceFlinger::setTransactionFlags(uint32_t mask) { + return setTransactionFlags(mask, TransactionSchedule::Late); } -uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, TransactionSchedule schedule, - const sp<IBinder>& token) { - uint32_t old = mTransactionFlags.fetch_or(flags); - modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, token); - if ((old & flags) == 0) signalTransaction(); +uint32_t SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule schedule, + const sp<IBinder>& applyToken) { + const uint32_t old = mTransactionFlags.fetch_or(mask); + modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, applyToken); + if ((old & mask) == 0) scheduleInvalidate(FrameHint::kActive); return old; } @@ -4465,7 +4463,7 @@ void SurfaceFlinger::onInitializeDisplays() { d.what = DisplayState::eDisplayProjectionChanged | DisplayState::eLayerStackChanged; d.token = token; - d.layerStack = 0; + d.layerStack = ui::DEFAULT_LAYER_STACK; d.orientation = ui::ROTATION_0; d.orientedDisplaySpaceRect.makeInvalid(); d.layerStackSpaceRect.makeInvalid(); @@ -4496,23 +4494,22 @@ void SurfaceFlinger::initializeDisplays() { } sp<DisplayDevice> SurfaceFlinger::getDisplayWithInputByLayer(Layer* layer) const { - sp<DisplayDevice> display; - for (const auto& pair : mDisplays) { - const auto& displayDevice = pair.second; - if (!displayDevice->receivesInput() || - !displayDevice->getCompositionDisplay() - ->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) { + const auto filter = layer->getOutputFilter(); + sp<DisplayDevice> inputDisplay; + + for (const auto& [_, display] : mDisplays) { + if (!display->receivesInput() || !display->getCompositionDisplay()->includesLayer(filter)) { continue; } // Don't return immediately so that we can log duplicates. - if (display) { - ALOGE("Multiple display devices claim to accept input for the same layerstack: %d", - layer->getLayerStack()); + if (inputDisplay) { + ALOGE("Multiple displays claim to accept input for the same layer stack: %u", + filter.layerStack.id); continue; } - display = displayDevice; + inputDisplay = display; } - return display; + return inputDisplay; } void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) { @@ -4557,7 +4554,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: mVisibleRegionsDirty = true; mHasPoweredOff = true; - repaintEverything(); + scheduleRefresh(FrameHint::kActive); } else if (mode == hal::PowerMode::OFF) { // Turn off the display if (SurfaceFlinger::setSchedFifo(false) != NO_ERROR) { @@ -4950,7 +4947,7 @@ void SurfaceFlinger::dumpDisplayProto(LayersTraceProto& layersTraceProto) const DisplayProto* displayProto = layersTraceProto.add_displays(); displayProto->set_id(display->getId().value); displayProto->set_name(display->getDisplayName()); - displayProto->set_layer_stack(display->getLayerStack()); + displayProto->set_layer_stack(display->getLayerStack().id); LayerProtoHelper::writeSizeToProto(display->getWidth(), display->getHeight(), [&]() { return displayProto->mutable_size(); }); LayerProtoHelper::writeToProto(display->getLayerStackSpaceRect(), [&]() { @@ -5170,7 +5167,7 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co colorizer.bold(result); result.append("h/w composer state:\n"); colorizer.reset(result); - bool hwcDisabled = mDebugDisableHWC || mDebugRegion; + const bool hwcDisabled = mDebugDisableHWC || mDebugFlashDelay; StringAppendF(&result, " h/w composer %s\n", hwcDisabled ? "disabled" : "enabled"); getHwComposer().dump(result); @@ -5180,6 +5177,11 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get()); alloc.dump(result); + /* + * Dump flag/property manager state + */ + mFlagManager->dump(result); + result.append(mTimeStats->miniDump()); result.append("\n"); } @@ -5379,9 +5381,8 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { - status_t credentialCheck = CheckTransactCodeCredentials(code); - if (credentialCheck != OK) { - return credentialCheck; + if (const status_t error = CheckTransactCodeCredentials(code); error != OK) { + return error; } status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags); @@ -5398,47 +5399,43 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r } int n; switch (code) { - case 1000: // SHOW_CPU, NOT SUPPORTED ANYMORE - case 1001: // SHOW_FPS, NOT SUPPORTED ANYMORE - return NO_ERROR; - case 1002: // SHOW_UPDATES - n = data.readInt32(); - mDebugRegion = n ? n : (mDebugRegion ? 0 : 1); - invalidateHwcGeometry(); - repaintEverything(); + case 1000: // Unused. + case 1001: + return NAME_NOT_FOUND; + case 1002: // Toggle flashing on surface damage. + if (const int delay = data.readInt32(); delay > 0) { + mDebugFlashDelay = delay; + } else { + mDebugFlashDelay = mDebugFlashDelay ? 0 : 1; + } + scheduleRepaint(); return NO_ERROR; - case 1004:{ // repaint everything - repaintEverything(); + case 1004: // Force refresh ahead of next VSYNC. + scheduleRefresh(FrameHint::kActive); return NO_ERROR; - } - case 1005:{ // force transaction - Mutex::Autolock _l(mStateLock); - setTransactionFlags( - eTransactionNeeded| - eDisplayTransactionNeeded| - eTraversalNeeded); + case 1005: { // Force commit ahead of next VSYNC. + Mutex::Autolock lock(mStateLock); + setTransactionFlags(eTransactionNeeded | eDisplayTransactionNeeded | + eTraversalNeeded); return NO_ERROR; } - case 1006:{ // send empty update + case 1006: // Force refresh immediately. signalRefresh(); return NO_ERROR; - } - case 1008: // toggle use of hw composer - n = data.readInt32(); - mDebugDisableHWC = n != 0; - invalidateHwcGeometry(); - repaintEverything(); + case 1007: // Unused. + return NAME_NOT_FOUND; + case 1008: // Toggle forced GPU composition. + mDebugDisableHWC = data.readInt32() != 0; + scheduleRepaint(); return NO_ERROR; - case 1009: // toggle use of transform hint - n = data.readInt32(); - mDebugDisableTransformHint = n != 0; - invalidateHwcGeometry(); - repaintEverything(); + case 1009: // Toggle use of transform hint. + mDebugDisableTransformHint = data.readInt32() != 0; + scheduleRepaint(); return NO_ERROR; - case 1010: // interrogate. + case 1010: // Interrogate. reply->writeInt32(0); reply->writeInt32(0); - reply->writeInt32(mDebugRegion); + reply->writeInt32(mDebugFlashDelay); reply->writeInt32(0); reply->writeInt32(mDebugDisableHWC); return NO_ERROR; @@ -5493,12 +5490,15 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r mClientColorMatrix = mat4(); } + // TODO(b/193487656): Restore once HWASan bug is fixed. +#if 0 // Check that supplied matrix's last row is {0,0,0,1} so we can avoid // the division by w in the fragment shader float4 lastRow(transpose(mClientColorMatrix)[3]); if (any(greaterThan(abs(lastRow - float4{0, 0, 0, 1}), float4{1e-4f}))) { ALOGE("The color transform's last row must be (0, 0, 0, 1)"); } +#endif updateColorMatrixLocked(); return NO_ERROR; @@ -5552,8 +5552,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r if (data.readInt32(&colorMode) == NO_ERROR) { mForceColorMode = static_cast<ColorMode>(colorMode); } - invalidateHwcGeometry(); - repaintEverything(); + scheduleRepaint(); return NO_ERROR; } // Deprecate, use 1030 to check whether the device is color managed. @@ -5684,37 +5683,25 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r } case 1035: { const int modeId = data.readInt32(); - mDebugDisplayModeSetByBackdoor = false; - const auto displayId = [&]() -> std::optional<PhysicalDisplayId> { - uint64_t inputDisplayId = 0; - if (data.readUint64(&inputDisplayId) == NO_ERROR) { - const auto token = getPhysicalDisplayToken( - static_cast<PhysicalDisplayId>(inputDisplayId)); - if (!token) { - ALOGE("No display with id: %" PRIu64, inputDisplayId); - return std::nullopt; - } + const auto display = [&]() -> sp<IBinder> { + uint64_t value; + if (data.readUint64(&value) != NO_ERROR) { + return getDefaultDisplayDevice()->getDisplayToken().promote(); + } - return std::make_optional<PhysicalDisplayId>(inputDisplayId); + if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(value)) { + return getPhysicalDisplayToken(*id); } - return getDefaultDisplayDevice()->getPhysicalId(); + ALOGE("Invalid physical display ID"); + return nullptr; }(); - if (!displayId) { - ALOGE("No display found"); - return NO_ERROR; - } - - status_t result = setActiveMode(getPhysicalDisplayToken(*displayId), modeId); - if (result != NO_ERROR) { - return result; - } - - mDebugDisplayModeSetByBackdoor = true; - - return NO_ERROR; + mDebugDisplayModeSetByBackdoor = false; + const status_t result = setActiveMode(display, modeId); + mDebugDisplayModeSetByBackdoor = result == NO_ERROR; + return result; } case 1036: { if (data.readInt32() > 0) { @@ -5731,12 +5718,10 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r // Inject a hotplug connected event for the primary display. This will deallocate and // reallocate the display state including framebuffers. case 1037: { - std::optional<hal::HWDisplayId> hwcId; - { - Mutex::Autolock lock(mStateLock); - hwcId = getHwComposer().getInternalHwcDisplayId(); - } - onComposerHalHotplug(*hwcId, hal::Connection::CONNECTED); + const hal::HWDisplayId hwcId = + (Mutex::Autolock(mStateLock), getHwComposer().getPrimaryHwcDisplayId()); + + onComposerHalHotplug(hwcId, hal::Connection::CONNECTED); return NO_ERROR; } // Modify the max number of display frames stored within FrameTimeline @@ -5777,14 +5762,11 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r std::optional<PhysicalDisplayId> inputId = std::nullopt; if (uint64_t inputDisplayId; data.readUint64(&inputDisplayId) == NO_ERROR) { - const auto token = getPhysicalDisplayToken( - static_cast<PhysicalDisplayId>(inputDisplayId)); - if (!token) { + inputId = DisplayId::fromValue<PhysicalDisplayId>(inputDisplayId); + if (!inputId || getPhysicalDisplayToken(*inputId)) { ALOGE("No display with id: %" PRIu64, inputDisplayId); return NAME_NOT_FOUND; } - - inputId = std::make_optional<PhysicalDisplayId>(inputDisplayId); } { Mutex::Autolock lock(mStateLock); @@ -5801,8 +5783,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r if (error != OK) { return error; } - invalidateHwcGeometry(); - repaintEverything(); + scheduleRepaint(); return NO_ERROR; } } @@ -5810,17 +5791,6 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r return err; } -void SurfaceFlinger::repaintEverything() { - mRepaintEverything = true; - signalTransaction(); -} - -void SurfaceFlinger::repaintEverythingForHWC() { - mRepaintEverything = true; - mPowerAdvisor.notifyDisplayUpdateImminent(); - mEventQueue->invalidate(); -} - void SurfaceFlinger::kernelTimerChanged(bool expired) { static bool updateOverlay = property_get_bool("debug.sf.kernel_idle_timer_update_overlay", true); @@ -6042,7 +6012,7 @@ status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, captureListener); } -status_t SurfaceFlinger::captureDisplay(uint64_t displayIdOrLayerStack, +status_t SurfaceFlinger::captureDisplay(DisplayId displayId, const sp<IScreenCaptureListener>& captureListener) { ui::LayerStack layerStack; wp<const DisplayDevice> displayWeak; @@ -6050,21 +6020,14 @@ status_t SurfaceFlinger::captureDisplay(uint64_t displayIdOrLayerStack, ui::Dataspace dataspace; { Mutex::Autolock lock(mStateLock); - auto display = getDisplayDeviceLocked(PhysicalDisplayId{displayIdOrLayerStack}); - - // Fall back to first display whose layer stack matches. - if (!display) { - const auto layerStack = static_cast<ui::LayerStack>(displayIdOrLayerStack); - display = findDisplay(WithLayerStack(layerStack)); - } + const auto display = getDisplayDeviceLocked(displayId); if (!display) { return NAME_NOT_FOUND; } - layerStack = display->getLayerStack(); displayWeak = display; - + layerStack = display->getLayerStack(); size = display->getLayerStackSpaceRect().getSize(); dataspace = @@ -6149,7 +6112,11 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, } } - const auto display = findDisplay(WithLayerStack(parent->getLayerStack())); + const auto display = + findDisplay([layerStack = parent->getLayerStack()](const auto& display) { + return display.getLayerStack() == layerStack; + }); + if (!display) { return NAME_NOT_FOUND; } @@ -6364,7 +6331,6 @@ status_t SurfaceFlinger::renderScreenImplLocked( const auto display = renderArea.getDisplayDevice(); std::vector<Layer*> renderedLayers; - Region clearRegion = Region::INVALID_REGION; bool disableBlurs = false; traverseLayers([&](Layer* layer) { disableBlurs |= layer->getDrawingState().sidebandStream != nullptr; @@ -6376,7 +6342,6 @@ status_t SurfaceFlinger::renderScreenImplLocked( renderArea.needsFiltering(), renderArea.isSecure(), useProtected, - clearRegion, layerStackSpaceRect, clientCompositionDisplay.outputDataspace, true, /* realContentIsVisible */ @@ -6414,16 +6379,17 @@ status_t SurfaceFlinger::renderScreenImplLocked( clientCompositionLayerPointers.begin(), std::pointer_traits<renderengine::LayerSettings*>::pointer_to); - clientCompositionDisplay.clearRegion = clearRegion; // Use an empty fence for the buffer fence, since we just created the buffer so // there is no need for synchronization with the GPU. base::unique_fd bufferFence; - base::unique_fd drawFence; getRenderEngine().useProtectedContext(useProtected); const constexpr bool kUseFramebufferCache = false; - getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer, - kUseFramebufferCache, std::move(bufferFence), &drawFence); + auto [status, drawFence] = + getRenderEngine() + .drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer, + kUseFramebufferCache, std::move(bufferFence)) + .get(); if (drawFence >= 0) { sp<Fence> releaseFence = new Fence(dup(drawFence)); @@ -6436,7 +6402,7 @@ status_t SurfaceFlinger::renderScreenImplLocked( // Always switch back to unprotected context. getRenderEngine().useProtectedContext(false); - return NO_ERROR; + return status; } void SurfaceFlinger::windowInfosReported() { @@ -6463,12 +6429,12 @@ void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack, const // We loop through the first level of layers without traversing, // as we need to determine which layers belong to the requested display. for (const auto& layer : mDrawingState.layersSortedByZ) { - if (!layer->belongsToDisplay(layerStack)) { + if (layer->getLayerStack() != layerStack) { continue; } // relative layers are traversed in Layer::traverseInZOrder layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) { - if (layer->getPrimaryDisplayOnly()) { + if (layer->isInternalDisplayOverlay()) { return; } if (!layer->isVisible()) { @@ -6827,33 +6793,29 @@ int SurfaceFlinger::calculateMaxAcquiredBufferCount(Fps refreshRate, } status_t SurfaceFlinger::getMaxAcquiredBufferCount(int* buffers) const { - const auto maxSupportedRefreshRate = [&] { - const auto display = getDefaultDisplayDevice(); - if (display) { - return display->refreshRateConfigs().getSupportedRefreshRateRange().max; + Fps maxRefreshRate(60.f); + + if (!getHwComposer().isHeadless()) { + if (const auto display = getDefaultDisplayDevice()) { + maxRefreshRate = display->refreshRateConfigs().getSupportedRefreshRateRange().max; } - ALOGW("%s: default display is null", __func__); - return Fps(60); - }(); - *buffers = getMaxAcquiredBufferCountForRefreshRate(maxSupportedRefreshRate); + } + + *buffers = getMaxAcquiredBufferCountForRefreshRate(maxRefreshRate); return NO_ERROR; } -int SurfaceFlinger::getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const { - const auto refreshRate = [&] { - const auto frameRateOverride = mScheduler->getFrameRateOverride(uid); - if (frameRateOverride.has_value()) { - return frameRateOverride.value(); - } +uint32_t SurfaceFlinger::getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const { + Fps refreshRate(60.f); - const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()); - if (display) { - return display->refreshRateConfigs().getCurrentRefreshRate().getFps(); + if (const auto frameRateOverride = mScheduler->getFrameRateOverride(uid)) { + refreshRate = *frameRateOverride; + } else if (!getHwComposer().isHeadless()) { + if (const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked())) { + refreshRate = display->refreshRateConfigs().getCurrentRefreshRate().getFps(); } + } - ALOGW("%s: default display is null", __func__); - return Fps(60); - }(); return getMaxAcquiredBufferCountForRefreshRate(refreshRate); } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index bcfe626ecc..9dedfa7b71 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -89,6 +89,7 @@ namespace android { class Client; class EventThread; +class FlagManager; class FpsReporter; class TunnelModeEnabledReporter; class HdrLayerInfoReporter; @@ -285,8 +286,12 @@ public: template <typename F, typename T = std::invoke_result_t<F>> [[nodiscard]] std::future<T> schedule(F&&); - // force full composition on all displays - void repaintEverything(); + // Schedule commit of transactions on the main thread ahead of the next VSYNC. + void scheduleInvalidate(FrameHint); + // As above, but also force refresh regardless if transactions were committed. + void scheduleRefresh(FrameHint) override; + // As above, but also force dirty geometry to repaint. + void scheduleRepaint(); surfaceflinger::Factory& getFactory() { return mFactory; } @@ -348,7 +353,6 @@ protected: uint32_t permissions, std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) REQUIRES(mStateLock); - virtual void commitTransactionLocked(); virtual void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState&) REQUIRES(mStateLock); @@ -628,12 +632,10 @@ private: sp<IDisplayEventConnection> createDisplayEventConnection( ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp, ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) override; - status_t captureDisplay(const DisplayCaptureArgs& args, - const sp<IScreenCaptureListener>& captureListener) override; - status_t captureDisplay(uint64_t displayOrLayerStack, - const sp<IScreenCaptureListener>& captureListener) override; - status_t captureLayers(const LayerCaptureArgs& args, - const sp<IScreenCaptureListener>& captureListener) override; + + status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override; + status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) override; + status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override; status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override; status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*) @@ -746,8 +748,6 @@ private: void setVsyncEnabled(bool) override; // Initiates a refresh rate change to be applied on invalidate. void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override; - // Forces full composition on all displays without resetting the scheduler idle timer. - void repaintEverythingForHWC() override; // Called when kernel idle timer has expired. Used to update the refresh rate overlay. void kernelTimerChanged(bool expired) override; // Called when the frame rate override list changed to trigger an event. @@ -766,8 +766,6 @@ private: * Message handling */ // Can only be called from the main thread or with mStateLock held - void signalTransaction(); - // Can only be called from the main thread or with mStateLock held void signalLayerUpdate(); void signalRefresh(); @@ -799,8 +797,12 @@ private: // incoming transactions void onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime); - // Returns whether the transaction actually modified any state - bool handleMessageTransaction(); + // Returns whether transactions were committed. + bool flushAndCommitTransactions() EXCLUDES(mStateLock); + + void commitTransactions() EXCLUDES(mStateLock); + void commitTransactionsLocked(uint32_t transactionFlags) REQUIRES(mStateLock); + void doCommitTransactions() REQUIRES(mStateLock); // Handle the REFRESH message queue event, sending the current frame down to RenderEngine and // the Composer HAL for presentation @@ -809,9 +811,6 @@ private: // Returns whether a new buffer has been latched (see handlePageFlip()) bool handleMessageInvalidate(); - void handleTransaction(uint32_t transactionFlags); - void handleTransactionLocked(uint32_t transactionFlags) REQUIRES(mStateLock); - void updateInputFlinger(); void notifyWindowInfos(); void commitInputWindowCommands() REQUIRES(mStateLock); @@ -843,18 +842,23 @@ private: void flushTransactionQueues(); // Returns true if there is at least one transaction that needs to be flushed bool transactionFlushNeeded(); - uint32_t getTransactionFlags(uint32_t flags); - uint32_t peekTransactionFlags(); - // Can only be called from the main thread or with mStateLock held - uint32_t setTransactionFlags(uint32_t flags); + + uint32_t getTransactionFlags() const; + + // Sets the masked bits, and returns the old flags. + uint32_t setTransactionFlags(uint32_t mask); + + // Clears and returns the masked bits. + uint32_t clearTransactionFlags(uint32_t mask); + // Indicate SF should call doTraversal on layers, but don't trigger a wakeup! We use this cases // where there are still pending transactions but we know they won't be ready until a frame // arrives from a different layer. So we need to ensure we performTransaction from invalidate // but there is no need to try and wake up immediately to do it. Rather we rely on // onFrameAvailable or another layer update to wake us up. void setTraversalNeeded(); - uint32_t setTransactionFlags(uint32_t flags, TransactionSchedule, const sp<IBinder>& = {}); - void commitTransaction() REQUIRES(mStateLock); + uint32_t setTransactionFlags(uint32_t mask, TransactionSchedule, + const sp<IBinder>& applyToken = {}); void commitOffscreenLayers(); bool transactionIsReadyToBeApplied( const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime, @@ -937,7 +941,7 @@ private: return width > mMaxRenderTargetSize || height > mMaxRenderTargetSize; } - int getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const; + uint32_t getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const; /* * Display and layer stack management @@ -1028,8 +1032,6 @@ private: /* * Compositing */ - void invalidateHwcGeometry(); - void postComposition(); void getCompositorTiming(CompositorTiming* compositorTiming); void updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime, @@ -1113,15 +1115,19 @@ private: return {}; } - // TODO(b/74619554): Remove special cases for primary display. + // TODO(b/182939859): SF conflates the primary (a.k.a. default) display with the first display + // connected at boot, which is typically internal. (Theoretically, it must be internal because + // SF does not support disconnecting it, though in practice HWC may circumvent this limitation.) + // + // SF inherits getInternalDisplayToken and getInternalDisplayId from ISurfaceComposer, so these + // locked counterparts are named consistently. Once SF supports headless mode and can designate + // any display as primary, the "internal" misnomer will be phased out. sp<IBinder> getInternalDisplayTokenLocked() const REQUIRES(mStateLock) { - const auto displayId = getInternalDisplayIdLocked(); - return displayId ? getPhysicalDisplayTokenLocked(*displayId) : nullptr; + return getPhysicalDisplayTokenLocked(getInternalDisplayIdLocked()); } - std::optional<PhysicalDisplayId> getInternalDisplayIdLocked() const REQUIRES(mStateLock) { - const auto hwcDisplayId = getHwComposer().getInternalHwcDisplayId(); - return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt; + PhysicalDisplayId getInternalDisplayIdLocked() const REQUIRES(mStateLock) { + return getHwComposer().getPrimaryDisplayId(); } // Toggles use of HAL/GPU virtual displays. @@ -1202,8 +1208,7 @@ private: /* * Misc */ - std::vector<ui::ColorMode> getDisplayColorModes(PhysicalDisplayId displayId) - REQUIRES(mStateLock); + std::vector<ui::ColorMode> getDisplayColorModes(const DisplayDevice&) REQUIRES(mStateLock); static int calculateMaxAcquiredBufferCount(Fps refreshRate, std::chrono::nanoseconds presentLatency); @@ -1245,7 +1250,8 @@ private: bool mLayersRemoved = false; bool mLayersAdded = false; - std::atomic<bool> mRepaintEverything = false; + std::atomic_bool mForceRefresh = false; + std::atomic_bool mGeometryDirty = false; // constant members (no synchronization needed for access) const nsecs_t mBootTime = systemTime(); @@ -1272,7 +1278,6 @@ private: bool mSomeDataspaceChanged = false; bool mForceTransactionDisplayChange = false; - bool mGeometryInvalid = false; bool mAnimCompositionPending = false; // Tracks layers that have pending frames which are candidates for being @@ -1307,13 +1312,13 @@ private: std::optional<DisplayIdGenerator<HalVirtualDisplayId>> hal; } mVirtualDisplayIdGenerators; - // don't use a lock for these, we don't care - int mDebugRegion = 0; - bool mDebugDisableHWC = false; - bool mDebugDisableTransformHint = false; + std::atomic_uint mDebugFlashDelay = 0; + std::atomic_bool mDebugDisableHWC = false; + std::atomic_bool mDebugDisableTransformHint = false; + std::atomic<nsecs_t> mDebugInTransaction = 0; + std::atomic_bool mForceFullDamage = false; + bool mLayerCachingEnabled = false; - volatile nsecs_t mDebugInTransaction = 0; - bool mForceFullDamage = false; bool mPropagateBackpressureClientComposition = false; sp<SurfaceInterceptor> mInterceptor; @@ -1510,6 +1515,8 @@ private: wp<IBinder> mActiveDisplayToken GUARDED_BY(mStateLock); const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker; + + std::unique_ptr<FlagManager> mFlagManager; }; } // namespace android diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp index 9be3abefab..0782fef8ea 100644 --- a/services/surfaceflinger/SurfaceInterceptor.cpp +++ b/services/surfaceflinger/SurfaceInterceptor.cpp @@ -323,11 +323,10 @@ void SurfaceInterceptor::addFlagsLocked(Transaction* transaction, int32_t layerI } void SurfaceInterceptor::addLayerStackLocked(Transaction* transaction, int32_t layerId, - uint32_t layerStack) -{ + ui::LayerStack layerStack) { SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); LayerStackChange* layerStackChange(change->mutable_layer_stack()); - layerStackChange->set_layer_stack(layerStack); + layerStackChange->set_layer_stack(layerStack.id); } void SurfaceInterceptor::addCropLocked(Transaction* transaction, int32_t layerId, @@ -568,12 +567,11 @@ void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32 } } -void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction, - int32_t sequenceId, uint32_t layerStack) -{ +void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId, + ui::LayerStack layerStack) { DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId)); LayerStackChange* layerStackChange(dispChange->mutable_layer_stack()); - layerStackChange->set_layer_stack(layerStack); + layerStackChange->set_layer_stack(layerStack.id); } void SurfaceInterceptor::addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId, diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h index 7b331b9f5f..970c3e5c27 100644 --- a/services/surfaceflinger/SurfaceInterceptor.h +++ b/services/surfaceflinger/SurfaceInterceptor.h @@ -160,7 +160,7 @@ private: void addTransparentRegionLocked(Transaction* transaction, int32_t layerId, const Region& transRegion); void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags, uint8_t mask); - void addLayerStackLocked(Transaction* transaction, int32_t layerId, uint32_t layerStack); + void addLayerStackLocked(Transaction* transaction, int32_t layerId, ui::LayerStack); void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect); void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius); void addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId, @@ -183,8 +183,7 @@ private: DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t sequenceId); void addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId, const sp<const IGraphicBufferProducer>& surface); - void addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId, - uint32_t layerStack); + void addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId, ui::LayerStack); void addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId, uint32_t flags); void addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId, uint32_t w, uint32_t h); diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp index dc2aa58c9a..b93d127ab8 100644 --- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp +++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp @@ -21,6 +21,7 @@ namespace android { +using gui::DisplayInfo; using gui::IWindowInfosListener; using gui::WindowInfo; @@ -67,6 +68,7 @@ void WindowInfosListenerInvoker::binderDied(const wp<IBinder>& who) { } void WindowInfosListenerInvoker::windowInfosChanged(const std::vector<WindowInfo>& windowInfos, + const std::vector<DisplayInfo>& displayInfos, bool shouldSync) { std::unordered_set<sp<IWindowInfosListener>, ISurfaceComposer::SpHash<IWindowInfosListener>> windowInfosListeners; @@ -81,7 +83,7 @@ void WindowInfosListenerInvoker::windowInfosChanged(const std::vector<WindowInfo mCallbacksPending = windowInfosListeners.size(); for (const auto& listener : windowInfosListeners) { - listener->onWindowInfosChanged(windowInfos, + listener->onWindowInfosChanged(windowInfos, displayInfos, shouldSync ? mWindowInfosReportedListener : nullptr); } } diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h index 5e5796fe2d..ecd797a631 100644 --- a/services/surfaceflinger/WindowInfosListenerInvoker.h +++ b/services/surfaceflinger/WindowInfosListenerInvoker.h @@ -33,7 +33,8 @@ public: void addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener); void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener); - void windowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos, bool shouldSync); + void windowInfosChanged(const std::vector<gui::WindowInfo>&, + const std::vector<gui::DisplayInfo>&, bool shouldSync); protected: void binderDied(const wp<IBinder>& who) override; diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp index fa3f0e7239..d33bc1080c 100644 --- a/services/surfaceflinger/tests/Credentials_test.cpp +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -93,7 +93,7 @@ protected: ASSERT_TRUE(mBGSurfaceControl->isValid()); Transaction t; - t.setDisplayLayerStack(mDisplay, 0); + t.setDisplayLayerStack(mDisplay, ui::DEFAULT_LAYER_STACK); ASSERT_EQ(NO_ERROR, t.setLayer(mBGSurfaceControl, INT_MAX - 3).show(mBGSurfaceControl).apply()); } diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp index 93656f3fd2..9fa0452915 100644 --- a/services/surfaceflinger/tests/EffectLayer_test.cpp +++ b/services/surfaceflinger/tests/EffectLayer_test.cpp @@ -33,7 +33,7 @@ protected: mParentLayer = createColorLayer("Parent layer", Color::RED); asTransaction([&](Transaction& t) { - t.setDisplayLayerStack(display, 0); + t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer); t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque); }); diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp index 9fa3d4c417..8d7e175cec 100644 --- a/services/surfaceflinger/tests/IPC_test.cpp +++ b/services/surfaceflinger/tests/IPC_test.cpp @@ -159,7 +159,7 @@ public: {0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height)}, Color::RED); - transaction->setLayerStack(mSurfaceControl, 0) + transaction->setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK) .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max()) .setBuffer(mSurfaceControl, gb) .setAcquireFence(mSurfaceControl, fence) @@ -232,7 +232,7 @@ public: mDisplayHeight = mode.resolution.getHeight(); Transaction setupTransaction; - setupTransaction.setDisplayLayerStack(mPrimaryDisplay, 0); + setupTransaction.setDisplayLayerStack(mPrimaryDisplay, ui::DEFAULT_LAYER_STACK); setupTransaction.apply(); } @@ -310,7 +310,7 @@ TEST_F(IPCTest, MergeBasic) { Color::RED); Transaction transaction; - transaction.setLayerStack(sc, 0) + transaction.setLayerStack(sc, ui::DEFAULT_LAYER_STACK) .setLayer(sc, std::numeric_limits<int32_t>::max() - 1) .setBuffer(sc, gb) .setAcquireFence(sc, fence) diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h index 0bc8fe7aa0..6bd7920a62 100644 --- a/services/surfaceflinger/tests/LayerTransactionTest.h +++ b/services/surfaceflinger/tests/LayerTransactionTest.h @@ -266,7 +266,7 @@ protected: sp<IBinder> mDisplay; uint32_t mDisplayWidth; uint32_t mDisplayHeight; - uint32_t mDisplayLayerStack; + ui::LayerStack mDisplayLayerStack = ui::DEFAULT_LAYER_STACK; Rect mDisplayRect = Rect::INVALID_RECT; // leave room for ~256 layers @@ -294,8 +294,6 @@ private: // vsyncs. mBufferPostDelay = static_cast<int32_t>(1e6 / mode.refreshRate) * 3; - mDisplayLayerStack = 0; - mBlackBgSurface = createSurface(mClient, "BaseSurface", 0 /* buffer width */, 0 /* buffer height */, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect); diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp index 43d957cf7a..407625313c 100644 --- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp @@ -643,7 +643,8 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, SetLayerStackBasic) { ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); - Transaction().setLayerStack(layer, mDisplayLayerStack + 1).apply(); + const auto layerStack = ui::LayerStack::fromValue(mDisplayLayerStack.id + 1); + Transaction().setLayerStack(layer, layerStack).apply(); { SCOPED_TRACE("non-existing layer stack"); getScreenCapture()->expectColor(mDisplayRect, Color::BLACK); diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp index ee4d367f3d..e1a7ecc03b 100644 --- a/services/surfaceflinger/tests/LayerUpdate_test.cpp +++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp @@ -63,7 +63,7 @@ protected: TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31); asTransaction([&](Transaction& t) { - t.setDisplayLayerStack(display, 0); + t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl); diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp index d02786504e..3ec6da9ff4 100644 --- a/services/surfaceflinger/tests/MirrorLayer_test.cpp +++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp @@ -36,7 +36,7 @@ protected: mParentLayer = createColorLayer("Parent layer", Color::RED); mChildLayer = createColorLayer("Child layer", Color::GREEN, mParentLayer.get()); asTransaction([&](Transaction& t) { - t.setDisplayLayerStack(display, 0); + t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer); t.setCrop(mChildLayer, Rect(0, 0, 400, 400)).show(mChildLayer); t.setPosition(mChildLayer, 50, 50); diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp index 08de01cdfb..1ed6c65afb 100644 --- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp +++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp @@ -52,7 +52,7 @@ protected: mColorLayer = 0; } - void createDisplay(const ui::Size& layerStackSize, uint32_t layerStack) { + void createDisplay(const ui::Size& layerStackSize, ui::LayerStack layerStack) { mVirtualDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/); asTransaction([&](Transaction& t) { @@ -63,7 +63,7 @@ protected: }); } - void createColorLayer(uint32_t layerStack) { + void createColorLayer(ui::LayerStack layerStack) { mColorLayer = createSurface(mClient, "ColorLayer", 0 /* buffer width */, 0 /* buffer height */, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect); @@ -90,8 +90,9 @@ protected: }; TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) { - createDisplay(mMainDisplayState.layerStackSpaceRect, 1 /* layerStack */); - createColorLayer(1 /* layerStack */); + constexpr ui::LayerStack kLayerStack{1u}; + createDisplay(mMainDisplayState.layerStackSpaceRect, kLayerStack); + createColorLayer(kLayerStack); asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); }); @@ -113,8 +114,8 @@ TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) { // Assumption here is that the new mirrored display has the same layer stack rect as the // primary display that it is mirroring. - createDisplay(mMainDisplayState.layerStackSpaceRect, 0 /* layerStack */); - createColorLayer(0 /* layerStack */); + createDisplay(mMainDisplayState.layerStackSpaceRect, ui::DEFAULT_LAYER_STACK); + createColorLayer(ui::DEFAULT_LAYER_STACK); asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); }); diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp index fde6e6eff8..50a4092ddb 100644 --- a/services/surfaceflinger/tests/RelativeZ_test.cpp +++ b/services/surfaceflinger/tests/RelativeZ_test.cpp @@ -43,7 +43,7 @@ protected: mForegroundLayer = createColorLayer("Foreground layer", Color::GREEN); asTransaction([&](Transaction& t) { - t.setDisplayLayerStack(display, 0); + t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); t.setLayer(mBackgroundLayer, INT32_MAX - 2).show(mBackgroundLayer); t.setLayer(mForegroundLayer, INT32_MAX - 1).show(mForegroundLayer); }); diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp index ab2064efd0..95301b3a37 100644 --- a/services/surfaceflinger/tests/ScreenCapture_test.cpp +++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp @@ -53,7 +53,7 @@ protected: TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); asTransaction([&](Transaction& t) { - t.setDisplayLayerStack(display, 0); + t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl); @@ -563,7 +563,7 @@ TEST_F(ScreenCaptureTest, CaptureSecureLayer) { Transaction() .show(redLayer) .show(secureLayer) - .setLayerStack(redLayer, 0) + .setLayerStack(redLayer, ui::DEFAULT_LAYER_STACK) .setLayer(redLayer, INT32_MAX) .apply(); @@ -655,7 +655,7 @@ TEST_F(ScreenCaptureTest, CaptureDisplayPrimaryDisplayOnly) { Transaction() .show(layer) .hide(mFGSurfaceControl) - .setLayerStack(layer, 0) + .setLayerStack(layer, ui::DEFAULT_LAYER_STACK) .setLayer(layer, INT32_MAX) .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255}) .setCrop(layer, bounds) @@ -702,7 +702,7 @@ TEST_F(ScreenCaptureTest, CaptureDisplayChildPrimaryDisplayOnly) { .show(layer) .show(childLayer) .hide(mFGSurfaceControl) - .setLayerStack(layer, 0) + .setLayerStack(layer, ui::DEFAULT_LAYER_STACK) .setLayer(layer, INT32_MAX) .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255}) .setColor(childLayer, {childColor.r / 255, childColor.g / 255, childColor.b / 255}) diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp index a42405989f..ee16f40b6d 100644 --- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp +++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp @@ -283,7 +283,7 @@ void SurfaceInterceptorTest::setupBackgroundSurface() { ASSERT_TRUE(mFGSurfaceControl->isValid()); Transaction t; - t.setDisplayLayerStack(display, 0); + t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); ASSERT_EQ(NO_ERROR, t.setLayer(mBGSurfaceControl, INT_MAX - 3) .show(mBGSurfaceControl) @@ -380,7 +380,7 @@ void SurfaceInterceptorTest::transparentRegionHintUpdate(Transaction& t) { } void SurfaceInterceptorTest::layerStackUpdate(Transaction& t) { - t.setLayerStack(mBGSurfaceControl, STACK_UPDATE); + t.setLayerStack(mBGSurfaceControl, ui::LayerStack::fromValue(STACK_UPDATE)); } void SurfaceInterceptorTest::hiddenFlagUpdate(Transaction& t) { diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h index 89f608645d..60cffb19df 100644 --- a/services/surfaceflinger/tests/TransactionTestHarnesses.h +++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h @@ -65,7 +65,7 @@ public: SurfaceComposerClient::Transaction t; t.setDisplaySurface(vDisplay, producer); - t.setDisplayLayerStack(vDisplay, 0); + t.setDisplayLayerStack(vDisplay, ui::DEFAULT_LAYER_STACK); t.setDisplayProjection(vDisplay, displayState.orientation, Rect(displayState.layerStackSpaceRect), Rect(resolution)); t.apply(); diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp index 89228d55e5..0069111e09 100644 --- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp +++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp @@ -22,6 +22,7 @@ namespace android { using Transaction = SurfaceComposerClient::Transaction; +using gui::DisplayInfo; using gui::WindowInfo; class WindowInfosListenerTest : public ::testing::Test { @@ -40,7 +41,8 @@ protected: struct SyncWindowInfosListener : public gui::WindowInfosListener { public: - void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos) override { + void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos, + const std::vector<DisplayInfo>&) override { windowInfosPromise.set_value(windowInfos); } @@ -84,7 +86,7 @@ TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) { ISurfaceComposerClient::eFXSurfaceBufferState); Transaction() - .setLayerStack(surfaceControl, 0) + .setLayerStack(surfaceControl, ui::DEFAULT_LAYER_STACK) .show(surfaceControl) .setLayer(surfaceControl, INT32_MAX - 1) .setInputWindowInfo(surfaceControl, windowInfo) @@ -112,7 +114,7 @@ TEST_F(WindowInfosListenerTest, WindowInfoChanged) { ISurfaceComposerClient::eFXSurfaceBufferState); Transaction() - .setLayerStack(surfaceControl, 0) + .setLayerStack(surfaceControl, ui::DEFAULT_LAYER_STACK) .show(surfaceControl) .setLayer(surfaceControl, INT32_MAX - 1) .setInputWindowInfo(surfaceControl, windowInfo) diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp index 162711d6f5..b3b4ec15cd 100644 --- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp +++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp @@ -279,7 +279,7 @@ protected: } bool waitForHotplugEvent(Display displayId, bool connected) { - return waitForHotplugEvent(PhysicalDisplayId(displayId), connected); + return waitForHotplugEvent(physicalIdFromHwcDisplayId(displayId), connected); } bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) { @@ -305,7 +305,7 @@ protected: } bool waitForModeChangedEvent(Display display, int32_t modeId) { - PhysicalDisplayId displayId(display); + PhysicalDisplayId displayId = physicalIdFromHwcDisplayId(display); int waitCount = 20; while (waitCount--) { while (!mReceivedDisplayEvents.empty()) { @@ -363,7 +363,7 @@ protected: { TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, 0); + ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); } @@ -426,7 +426,7 @@ protected: { TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, 0); + ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); } @@ -479,7 +479,7 @@ protected: { TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, 0); + ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); } @@ -534,7 +534,7 @@ protected: { TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, 0); + ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); } @@ -586,7 +586,7 @@ protected: { TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, 0); + ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); } @@ -651,7 +651,7 @@ protected: { TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, 0); + ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); } @@ -703,7 +703,7 @@ protected: { TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, 0); + ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); } @@ -750,7 +750,7 @@ protected: { TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, 0); + ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); } @@ -797,7 +797,7 @@ protected: { TransactionScope ts(*mFakeComposerClient); - ts.setDisplayLayerStack(display, 0); + ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl); } @@ -1195,7 +1195,7 @@ protected: fillSurfaceRGBA8(mFGSurfaceControl, RED); Transaction t; - t.setDisplayLayerStack(display, 0); + t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); t.setLayer(mBGSurfaceControl, INT32_MAX - 2); t.show(mBGSurfaceControl); @@ -1342,7 +1342,7 @@ protected: ALOGD("TransactionTest::SetLayerStack"); { TransactionScope ts(*sFakeComposer); - ts.setLayerStack(mFGSurfaceControl, 1); + ts.setLayerStack(mFGSurfaceControl, ui::LayerStack{1}); } // Foreground layer should have disappeared. diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 9e704c32fc..078b0d4ef3 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -23,7 +23,10 @@ package { cc_test { name: "libsurfaceflinger_unittest", - defaults: ["surfaceflinger_defaults"], + defaults: [ + "skia_renderengine_deps", + "surfaceflinger_defaults", + ], test_suites: ["device-tests"], sanitize: { // Using the address sanitizer not only helps uncover issues in the code @@ -54,6 +57,7 @@ cc_test { "DisplayDevice_InitiateModeChange.cpp", "DisplayDevice_SetProjectionTest.cpp", "EventThreadTest.cpp", + "FlagManagerTest.cpp", "FpsReporterTest.cpp", "FpsTest.cpp", "FramebufferSurfaceTest.cpp", @@ -67,10 +71,10 @@ cc_test { "MessageQueueTest.cpp", "SurfaceFlinger_CreateDisplayTest.cpp", "SurfaceFlinger_DestroyDisplayTest.cpp", + "SurfaceFlinger_DisplayTransactionCommitTest.cpp", "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp", - "SurfaceFlinger_HandleTransactionLockedTest.cpp", - "SurfaceFlinger_NotifyPowerBoostTest.cpp", "SurfaceFlinger_HotplugTest.cpp", + "SurfaceFlinger_NotifyPowerBoostTest.cpp", "SurfaceFlinger_OnInitializeDisplaysTest.cpp", "SurfaceFlinger_SetDisplayStateTest.cpp", "SurfaceFlinger_SetPowerModeInternalTest.cpp", @@ -161,6 +165,7 @@ cc_test { "libsync", "libui", "libutils", + "server_configurable_flags", ], header_libs: [ "android.hardware.graphics.composer@2.1-command-buffer", diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp index 6a7ec9b553..6f85498670 100644 --- a/services/surfaceflinger/tests/unittests/CachingTest.cpp +++ b/services/surfaceflinger/tests/unittests/CachingTest.cpp @@ -14,11 +14,6 @@ * limitations under the License. */ -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" -#pragma clang diagnostic ignored "-Wextra" - #undef LOG_TAG #define LOG_TAG "CachingTest" @@ -42,7 +37,7 @@ TEST_F(SlotGenerationTest, getHwcCacheSlot_Invalid) { sp<IBinder> binder = new BBinder(); // test getting invalid client_cache_id client_cache_t id; - uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id); + int slot = mHwcSlotGenerator->getHwcCacheSlot(id); EXPECT_EQ(BufferQueue::INVALID_BUFFER_SLOT, slot); } @@ -51,7 +46,7 @@ TEST_F(SlotGenerationTest, getHwcCacheSlot_Basic) { client_cache_t id; id.token = binder; id.id = 0; - uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id); + int slot = mHwcSlotGenerator->getHwcCacheSlot(id); EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot); client_cache_t idB; @@ -72,31 +67,28 @@ TEST_F(SlotGenerationTest, getHwcCacheSlot_Reuse) { std::vector<client_cache_t> ids; uint32_t cacheId = 0; // fill up cache - for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { client_cache_t id; id.token = binder; id.id = cacheId; ids.push_back(id); - uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id); + int slot = mHwcSlotGenerator->getHwcCacheSlot(id); EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot); cacheId++; } - for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { - uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(ids[i]); + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + int slot = mHwcSlotGenerator->getHwcCacheSlot(ids[static_cast<uint32_t>(i)]); EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot); } - for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { client_cache_t id; id.token = binder; id.id = cacheId; - uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id); + int slot = mHwcSlotGenerator->getHwcCacheSlot(id); EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot); cacheId++; } } } // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion -Wextra" diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 52a36a2719..5135ff952f 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -77,12 +77,12 @@ constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DIS constexpr hal::HWLayerId HWC_LAYER = 5000; constexpr Transform DEFAULT_TRANSFORM = static_cast<Transform>(0); -constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(42); +constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); constexpr int DEFAULT_DISPLAY_WIDTH = 1920; constexpr int DEFAULT_DISPLAY_HEIGHT = 1024; constexpr int DEFAULT_TEXTURE_ID = 6000; -constexpr int DEFAULT_LAYER_STACK = 7000; +constexpr ui::LayerStack LAYER_STACK{7000u}; constexpr int DEFAULT_DISPLAY_MAX_LUMINANCE = 500; @@ -157,7 +157,7 @@ public: // pain) // mFlinger.mutableVisibleRegionsDirty() = true; - mFlinger.mutableGeometryInvalid() = true; + mFlinger.mutableGeometryDirty() = true; } template <typename Case> @@ -255,6 +255,14 @@ void CompositionTest::captureScreenComposition() { LayerCase::cleanup(this); } +template <class T> +std::future<T> futureOf(T obj) { + std::promise<T> resultPromise; + std::future<T> resultFuture = resultPromise.get_future(); + resultPromise.set_value(std::move(obj)); + return resultFuture; +} + /* ------------------------------------------------------------------------ * Variants for each display configuration which can be tested */ @@ -287,10 +295,8 @@ struct BaseDisplayVariant { auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder() .setId(DEFAULT_DISPLAY_ID) - .setConnectionType(ui::DisplayConnectionType::Internal) .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT}) .setIsSecure(Derived::IS_SECURE) - .setLayerStackId(DEFAULT_LAYER_STACK) .setPowerAdvisor(&test->mPowerAdvisor) .setName(std::string("Injected display for ") + test_info->test_case_name() + "." + test_info->name()) @@ -309,7 +315,7 @@ struct BaseDisplayVariant { .setPowerMode(Derived::INIT_POWER_MODE) .inject(); Mock::VerifyAndClear(test->mNativeWindow); - test->mDisplay->setLayerStack(DEFAULT_LAYER_STACK); + test->mDisplay->setLayerStack(LAYER_STACK); } template <typename Case> @@ -339,16 +345,18 @@ struct BaseDisplayVariant { template <typename Case> static void setupCommonScreensCaptureCallExpectations(CompositionTest* test) { EXPECT_CALL(*test->mRenderEngine, drawLayers) - .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings, - const std::vector<const renderengine::LayerSettings*>&, - const std::shared_ptr<renderengine::ExternalTexture>&, - const bool, base::unique_fd&&, base::unique_fd*) -> status_t { + .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings, + const std::vector<const renderengine::LayerSettings*>&, + const std::shared_ptr<renderengine::ExternalTexture>&, + const bool, base::unique_fd &&) + -> std::future<renderengine::RenderEngineResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.clip); - return NO_ERROR; + return futureOf<renderengine::RenderEngineResult>( + {NO_ERROR, base::unique_fd()}); }); } @@ -388,17 +396,19 @@ struct BaseDisplayVariant { .WillOnce(DoAll(SetArgPointee<0>(test->mNativeWindowBuffer), SetArgPointee<1>(-1), Return(0))); EXPECT_CALL(*test->mRenderEngine, drawLayers) - .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings, - const std::vector<const renderengine::LayerSettings*>&, - const std::shared_ptr<renderengine::ExternalTexture>&, - const bool, base::unique_fd&&, base::unique_fd*) -> status_t { + .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings, + const std::vector<const renderengine::LayerSettings*>&, + const std::shared_ptr<renderengine::ExternalTexture>&, + const bool, base::unique_fd &&) + -> std::future<renderengine::RenderEngineResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.clip); EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace); - return NO_ERROR; + return futureOf<renderengine::RenderEngineResult>( + {NO_ERROR, base::unique_fd()}); }); } @@ -622,10 +632,10 @@ struct BaseLayerProperties { static void setupREBufferCompositionCommonCallExpectations(CompositionTest* test) { EXPECT_CALL(*test->mRenderEngine, drawLayers) - .WillOnce([](const renderengine::DisplaySettings& displaySettings, - const std::vector<const renderengine::LayerSettings*>& layerSettings, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&, base::unique_fd*) -> status_t { + .WillOnce([&](const renderengine::DisplaySettings& displaySettings, + const std::vector<const renderengine::LayerSettings*>& layerSettings, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -633,11 +643,14 @@ struct BaseLayerProperties { displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so gtet the back layer. + std::future<renderengine::RenderEngineResult> resultFuture = + futureOf<renderengine::RenderEngineResult>( + {NO_ERROR, base::unique_fd()}); if (layerSettings.empty()) { ADD_FAILURE() << "layerSettings was not expected to be empty in " "setupREBufferCompositionCommonCallExpectations " "verification lambda"; - return NO_ERROR; + return resultFuture; } const renderengine::LayerSettings* layer = layerSettings.back(); EXPECT_THAT(layer->source.buffer.buffer, Not(IsNull())); @@ -649,7 +662,7 @@ struct BaseLayerProperties { EXPECT_EQ(0.0, layer->geometry.roundedCornersRadius); EXPECT_EQ(ui::Dataspace::UNKNOWN, layer->sourceDataspace); EXPECT_EQ(LayerProperties::COLOR[3], layer->alpha); - return NO_ERROR; + return resultFuture; }); } @@ -671,10 +684,10 @@ struct BaseLayerProperties { static void setupREColorCompositionCallExpectations(CompositionTest* test) { EXPECT_CALL(*test->mRenderEngine, drawLayers) - .WillOnce([](const renderengine::DisplaySettings& displaySettings, - const std::vector<const renderengine::LayerSettings*>& layerSettings, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&, base::unique_fd*) -> status_t { + .WillOnce([&](const renderengine::DisplaySettings& displaySettings, + const std::vector<const renderengine::LayerSettings*>& layerSettings, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -682,11 +695,14 @@ struct BaseLayerProperties { displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so get the back layer. + std::future<renderengine::RenderEngineResult> resultFuture = + futureOf<renderengine::RenderEngineResult>( + {NO_ERROR, base::unique_fd()}); if (layerSettings.empty()) { ADD_FAILURE() << "layerSettings was not expected to be empty in " "setupREColorCompositionCallExpectations verification lambda"; - return NO_ERROR; + return resultFuture; } const renderengine::LayerSettings* layer = layerSettings.back(); EXPECT_THAT(layer->source.buffer.buffer, IsNull()); @@ -696,7 +712,7 @@ struct BaseLayerProperties { EXPECT_EQ(0.0, layer->geometry.roundedCornersRadius); EXPECT_EQ(ui::Dataspace::UNKNOWN, layer->sourceDataspace); EXPECT_EQ(LayerProperties::COLOR[3], layer->alpha); - return NO_ERROR; + return resultFuture; }); } @@ -748,10 +764,10 @@ struct CommonSecureLayerProperties : public BaseLayerProperties<LayerProperties> static void setupInsecureREBufferCompositionCommonCallExpectations(CompositionTest* test) { EXPECT_CALL(*test->mRenderEngine, drawLayers) - .WillOnce([](const renderengine::DisplaySettings& displaySettings, - const std::vector<const renderengine::LayerSettings*>& layerSettings, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, - base::unique_fd&&, base::unique_fd*) -> status_t { + .WillOnce([&](const renderengine::DisplaySettings& displaySettings, + const std::vector<const renderengine::LayerSettings*>& layerSettings, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -759,11 +775,14 @@ struct CommonSecureLayerProperties : public BaseLayerProperties<LayerProperties> displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so get the back layer. + std::future<renderengine::RenderEngineResult> resultFuture = + futureOf<renderengine::RenderEngineResult>( + {NO_ERROR, base::unique_fd()}); if (layerSettings.empty()) { ADD_FAILURE() << "layerSettings was not expected to be empty in " "setupInsecureREBufferCompositionCommonCallExpectations " "verification lambda"; - return NO_ERROR; + return resultFuture; } const renderengine::LayerSettings* layer = layerSettings.back(); EXPECT_THAT(layer->source.buffer.buffer, IsNull()); @@ -771,7 +790,7 @@ struct CommonSecureLayerProperties : public BaseLayerProperties<LayerProperties> EXPECT_EQ(0.0, layer->geometry.roundedCornersRadius); EXPECT_EQ(ui::Dataspace::UNKNOWN, layer->sourceDataspace); EXPECT_EQ(1.0f, layer->alpha); - return NO_ERROR; + return resultFuture; }); } @@ -834,7 +853,7 @@ struct BaseLayerVariant { template <typename L> static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) { auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer); - layerDrawingState.layerStack = DEFAULT_LAYER_STACK; + layerDrawingState.layerStack = LAYER_STACK; layerDrawingState.width = 100; layerDrawingState.height = 100; layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1], diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp index 9fe30f8f72..3d24ecbf71 100644 --- a/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp @@ -84,10 +84,10 @@ public: EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width, mHardwareDisplaySize.height), compositionState.transform); - EXPECT_EQ(ui::ROTATION_0, compositionState.displaySpace.orientation); - EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content); - EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content); - EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content); + EXPECT_EQ(ui::ROTATION_0, compositionState.displaySpace.getOrientation()); + EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.getContent()); + EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.getContent()); + EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.getContent()); EXPECT_EQ(false, compositionState.needsFiltering); } @@ -96,13 +96,14 @@ public: EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width, mHardwareDisplaySize.height), compositionState.transform); - EXPECT_EQ(ui::ROTATION_90, compositionState.displaySpace.orientation); - EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content); + EXPECT_EQ(ui::ROTATION_90, compositionState.displaySpace.getOrientation()); + EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.getContent()); // For 90, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display // size width and height swapped EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), - compositionState.orientedDisplaySpace.content); - EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content); + compositionState.orientedDisplaySpace.getContent()); + EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), + compositionState.layerStackSpace.getContent()); EXPECT_EQ(false, compositionState.needsFiltering); } @@ -111,9 +112,9 @@ public: EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width, mHardwareDisplaySize.height), compositionState.transform); - EXPECT_EQ(ui::ROTATION_180, compositionState.displaySpace.orientation); - EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content); - EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content); + EXPECT_EQ(ui::ROTATION_180, compositionState.displaySpace.getOrientation()); + EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.getContent()); + EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.getContent()); EXPECT_EQ(false, compositionState.needsFiltering); } @@ -122,13 +123,14 @@ public: EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width, mHardwareDisplaySize.height), compositionState.transform); - EXPECT_EQ(ui::ROTATION_270, compositionState.displaySpace.orientation); - EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content); + EXPECT_EQ(ui::ROTATION_270, compositionState.displaySpace.getOrientation()); + EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.getContent()); // For 270, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display // size width and height swapped EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), - compositionState.orientedDisplaySpace.content); - EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content); + compositionState.orientedDisplaySpace.getContent()); + EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), + compositionState.layerStackSpace.getContent()); EXPECT_EQ(false, compositionState.needsFiltering); } diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index cc24323c98..5a21e7be5b 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -122,7 +122,7 @@ void DisplayTransactionTest::injectFakeNativeWindowSurfaceFactory() { sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay( std::function<void(FakeDisplayDeviceInjector&)> injectExtra) { - constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(777); + constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(255u); constexpr int DEFAULT_DISPLAY_WIDTH = 1080; constexpr int DEFAULT_DISPLAY_HEIGHT = 1920; constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0; @@ -139,20 +139,18 @@ sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay( EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)); EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber()); - constexpr auto kConnectionType = ui::DisplayConnectionType::Internal; - constexpr bool kIsPrimary = true; - auto compositionDisplay = compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(), compositionengine::DisplayCreationArgsBuilder() .setId(DEFAULT_DISPLAY_ID) - .setConnectionType(kConnectionType) .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT}) .setPowerAdvisor(&mPowerAdvisor) .build()); - auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay, kConnectionType, + constexpr bool kIsPrimary = true; + auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay, + ui::DisplayConnectionType::Internal, DEFAULT_DISPLAY_HWC_DISPLAY_ID, kIsPrimary); injector.setNativeWindow(mNativeWindow); diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index de058a4f68..0f1cc6765d 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -267,11 +267,6 @@ struct DisplayVariant { .setPixels({WIDTH, HEIGHT}) .setPowerAdvisor(&test->mPowerAdvisor); - const auto connectionType = CONNECTION_TYPE::value; - if (connectionType) { - ceDisplayArgs.setConnectionType(*connectionType); - } - auto compositionDisplay = compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(), ceDisplayArgs.build()); @@ -279,7 +274,7 @@ struct DisplayVariant { auto injector = TestableSurfaceFlinger::FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay, - connectionType, + CONNECTION_TYPE::value, HWC_DISPLAY_ID_OPT::value, static_cast<bool>(PRIMARY)); @@ -388,7 +383,6 @@ struct HwcDisplayVariant { auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder() .setId(DisplayVariant::DISPLAY_ID::get()) - .setConnectionType(PhysicalDisplay::CONNECTION_TYPE) .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT}) .setIsSecure(static_cast<bool>(DisplayVariant::SECURE)) .setPowerAdvisor(&test->mPowerAdvisor) diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index 4ff7592b71..28d0222829 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -41,10 +41,13 @@ namespace android { namespace { -constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID(111); -constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID(222); -constexpr PhysicalDisplayId DISPLAY_ID_64BIT(0xabcd12349876fedcULL); +constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID = PhysicalDisplayId::fromPort(111u); +constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID = PhysicalDisplayId::fromPort(222u); +constexpr PhysicalDisplayId DISPLAY_ID_64BIT = + PhysicalDisplayId::fromEdid(0xffu, 0xffffu, 0xffff'ffffu); + constexpr std::chrono::duration VSYNC_PERIOD(16ms); + class MockVSyncSource : public VSyncSource { public: const char* getName() const override { return "test"; } diff --git a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp new file mode 100644 index 0000000000..0905cd14b1 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp @@ -0,0 +1,143 @@ +/* + * Copyright 2021 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 <cstdint> +#undef LOG_TAG +#define LOG_TAG "FlagManagerTest" + +#include "FlagManager.h" + +#include <android-base/properties.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <log/log.h> +#include <server_configurable_flags/get_flags.h> +#include <optional> + +namespace android { + +using testing::Return; + +class MockFlagManager : public FlagManager { +public: + MockFlagManager() = default; + ~MockFlagManager() = default; + + MOCK_METHOD(std::string, getServerConfigurableFlag, (const std::string& experimentFlagName), + (const, override)); +}; + +class FlagManagerTest : public testing::Test { +public: + FlagManagerTest(); + ~FlagManagerTest() override; + std::unique_ptr<MockFlagManager> mFlagManager; + + template <typename T> + T getValue(const std::string& experimentFlagName, std::optional<T> systemPropertyOpt, + T defaultValue); +}; + +FlagManagerTest::FlagManagerTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + mFlagManager = std::make_unique<MockFlagManager>(); +} + +FlagManagerTest::~FlagManagerTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); +} + +template <typename T> +T FlagManagerTest::getValue(const std::string& experimentFlagName, + std::optional<T> systemPropertyOpt, T defaultValue) { + return mFlagManager->getValue(experimentFlagName, systemPropertyOpt, defaultValue); +} + +namespace { +TEST_F(FlagManagerTest, getValue_bool_default) { + EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("")); + const bool defaultValue = false; + std::optional<bool> systemPropertyValue = std::nullopt; + const bool result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue); + ASSERT_EQ(result, defaultValue); +} + +TEST_F(FlagManagerTest, getValue_bool_sysprop) { + const bool defaultValue = false; + std::optional<bool> systemPropertyValue = std::make_optional(true); + const bool result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue); + ASSERT_EQ(result, true); +} + +TEST_F(FlagManagerTest, getValue_bool_experiment) { + EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("1")); + const bool defaultValue = false; + std::optional<bool> systemPropertyValue = std::nullopt; + const bool result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue); + ASSERT_EQ(result, true); +} + +TEST_F(FlagManagerTest, getValue_int32_default) { + EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("")); + int32_t defaultValue = 30; + std::optional<int32_t> systemPropertyValue = std::nullopt; + int32_t result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue); + ASSERT_EQ(result, defaultValue); +} + +TEST_F(FlagManagerTest, getValue_int32_sysprop) { + int32_t defaultValue = 30; + std::optional<int32_t> systemPropertyValue = std::make_optional(10); + int32_t result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue); + ASSERT_EQ(result, 10); +} + +TEST_F(FlagManagerTest, getValue_int32_experiment) { + EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("50")); + std::int32_t defaultValue = 30; + std::optional<std::int32_t> systemPropertyValue = std::nullopt; + std::int32_t result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue); + ASSERT_EQ(result, 50); +} + +TEST_F(FlagManagerTest, getValue_int64_default) { + EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("")); + int64_t defaultValue = 30; + std::optional<int64_t> systemPropertyValue = std::nullopt; + int64_t result = getValue("flag_name", systemPropertyValue, defaultValue); + ASSERT_EQ(result, defaultValue); +} + +TEST_F(FlagManagerTest, getValue_int64_sysprop) { + int64_t defaultValue = 30; + std::optional<int64_t> systemPropertyValue = std::make_optional(10); + int64_t result = getValue("flag_name", systemPropertyValue, defaultValue); + ASSERT_EQ(result, 10); +} + +TEST_F(FlagManagerTest, getValue_int64_experiment) { + EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("50")); + int64_t defaultValue = 30; + std::optional<int64_t> systemPropertyValue = std::nullopt; + int64_t result = getValue("flag_name", systemPropertyValue, defaultValue); + ASSERT_EQ(result, 50); +} +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index 02ec7fc493..bb21ad63db 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -118,13 +118,15 @@ protected: std::shared_ptr<RefreshRateConfigs> mConfigs = std::make_shared< RefreshRateConfigs>(DisplayModes{DisplayMode::Builder(0) .setId(DisplayModeId(0)) - .setPhysicalDisplayId(PhysicalDisplayId(0)) + .setPhysicalDisplayId( + PhysicalDisplayId::fromPort(0)) .setVsyncPeriod(int32_t(LO_FPS_PERIOD)) .setGroup(0) .build(), DisplayMode::Builder(1) .setId(DisplayModeId(1)) - .setPhysicalDisplayId(PhysicalDisplayId(0)) + .setPhysicalDisplayId( + PhysicalDisplayId::fromPort(0)) .setVsyncPeriod(int32_t(HI_FPS_PERIOD)) .setGroup(0) .build()}, diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp index 691676420c..597e5e71a2 100644 --- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp +++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp @@ -72,7 +72,8 @@ TEST_F(OneShotTimerTest, startStopTest) { mIdleTimer->stop(); } -TEST_F(OneShotTimerTest, resetTest) { +// TODO(b/186417847) This test is flaky. Reenable once fixed. +TEST_F(OneShotTimerTest, DISABLED_resetTest) { fake::FakeClock* clock = new fake::FakeClock(); mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms, mResetTimerCallback.getInvocable(), @@ -94,7 +95,8 @@ TEST_F(OneShotTimerTest, resetTest) { EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value()); } -TEST_F(OneShotTimerTest, resetBackToBackTest) { +// TODO(b/186417847) This test is flaky. Reenable once fixed. +TEST_F(OneShotTimerTest, DISABLED_resetBackToBackTest) { fake::FakeClock* clock = new fake::FakeClock(); mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms, mResetTimerCallback.getInvocable(), @@ -144,7 +146,8 @@ TEST_F(OneShotTimerTest, startNotCalledTest) { EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value()); } -TEST_F(OneShotTimerTest, idleTimerIdlesTest) { +// TODO(b/186417847) This test is flaky. Reenable once fixed. +TEST_F(OneShotTimerTest, DISABLED_idleTimerIdlesTest) { fake::FakeClock* clock = new fake::FakeClock(); mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms, mResetTimerCallback.getInvocable(), @@ -169,7 +172,8 @@ TEST_F(OneShotTimerTest, idleTimerIdlesTest) { EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value()); } -TEST_F(OneShotTimerTest, timeoutCallbackExecutionTest) { +// TODO(b/186417847) This test is flaky. Reenable once fixed. +TEST_F(OneShotTimerTest, DISABLED_timeoutCallbackExecutionTest) { fake::FakeClock* clock = new fake::FakeClock(); mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms, mResetTimerCallback.getInvocable(), diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp index c1dba2bd61..618c10d6ad 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -98,9 +98,15 @@ protected: static inline const DisplayModeId HWC_CONFIG_ID_30 = DisplayModeId(4); static inline const DisplayModeId HWC_CONFIG_ID_25 = DisplayModeId(5); static inline const DisplayModeId HWC_CONFIG_ID_50 = DisplayModeId(6); + static inline const DisplayModeId HWC_CONFIG_ID_24 = DisplayModeId(7); + static inline const DisplayModeId HWC_CONFIG_ID_24_FRAC = DisplayModeId(8); + static inline const DisplayModeId HWC_CONFIG_ID_30_FRAC = DisplayModeId(9); + static inline const DisplayModeId HWC_CONFIG_ID_60_FRAC = DisplayModeId(10); // Test configs DisplayModePtr mConfig60 = createDisplayMode(HWC_CONFIG_ID_60, 0, Fps(60.0f).getPeriodNsecs()); + DisplayModePtr mConfig60Frac = + createDisplayMode(HWC_CONFIG_ID_60_FRAC, 0, Fps(59.94f).getPeriodNsecs()); DisplayModePtr mConfig90 = createDisplayMode(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs()); DisplayModePtr mConfig90DifferentGroup = createDisplayMode(HWC_CONFIG_ID_90, 1, Fps(90.0f).getPeriodNsecs()); @@ -116,9 +122,15 @@ protected: DisplayModePtr mConfig30 = createDisplayMode(HWC_CONFIG_ID_30, 0, Fps(30.0f).getPeriodNsecs()); DisplayModePtr mConfig30DifferentGroup = createDisplayMode(HWC_CONFIG_ID_30, 1, Fps(30.0f).getPeriodNsecs()); + DisplayModePtr mConfig30Frac = + createDisplayMode(HWC_CONFIG_ID_30_FRAC, 0, Fps(29.97f).getPeriodNsecs()); + DisplayModePtr mConfig25 = createDisplayMode(HWC_CONFIG_ID_25, 0, Fps(25.0f).getPeriodNsecs()); DisplayModePtr mConfig25DifferentGroup = createDisplayMode(HWC_CONFIG_ID_25, 1, Fps(25.0f).getPeriodNsecs()); DisplayModePtr mConfig50 = createDisplayMode(HWC_CONFIG_ID_50, 0, Fps(50.0f).getPeriodNsecs()); + DisplayModePtr mConfig24 = createDisplayMode(HWC_CONFIG_ID_24, 0, Fps(24.0f).getPeriodNsecs()); + DisplayModePtr mConfig24Frac = + createDisplayMode(HWC_CONFIG_ID_24_FRAC, 0, Fps(23.976f).getPeriodNsecs()); // Test device configurations // The positions of the configs in the arrays below MUST match their IDs. For example, @@ -145,6 +157,11 @@ protected: mConfig50}; DisplayModes m60_120Device = {mConfig60, mConfig120}; + // This is a typical TV configuration. + DisplayModes m24_25_30_50_60WithFracDevice = {mConfig24, mConfig24Frac, mConfig25, + mConfig30, mConfig30Frac, mConfig50, + mConfig60, mConfig60Frac}; + // Expected RefreshRate objects RefreshRate mExpected60Config = {mConfig60, RefreshRate::ConstructorTag(0)}; RefreshRate mExpectedAlmost60Config = {createDisplayMode(HWC_CONFIG_ID_60, 0, 16666665), @@ -181,7 +198,7 @@ DisplayModePtr RefreshRateConfigsTest::createDisplayMode(DisplayModeId modeId, i int64_t vsyncPeriod, ui::Size resolution) { return DisplayMode::Builder(hal::HWConfigId(modeId.value())) .setId(modeId) - .setPhysicalDisplayId(PhysicalDisplayId(0)) + .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0)) .setVsyncPeriod(int32_t(vsyncPeriod)) .setGroup(group) .setHeight(resolution.height) @@ -332,10 +349,19 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_noLayers) { EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})); - ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}), - 0); + ASSERT_EQ(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}), + NO_ERROR); EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})); + + // We select max even when this will cause a non-seamless switch. + refreshRateConfigs = std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, + /*currentConfigId=*/HWC_CONFIG_ID_60); + ASSERT_EQ(refreshRateConfigs->setDisplayManagerPolicy( + {HWC_CONFIG_ID_90, /*allowGroupSwitching*/ true, {Fps(0), Fps(90)}}), + NO_ERROR); + EXPECT_EQ(mExpected90DifferentGroupConfig, + refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) { @@ -1230,7 +1256,109 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitDefault) { const auto& refreshRate = refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}); EXPECT_TRUE(refreshRate.getFps().equalsWithMargin(Fps(test.second))) - << "Expecting " << test.first << "fps => " << test.second << "Hz"; + << "Expecting " << test.first << "fps => " << test.second << "Hz" + << " but it was " << refreshRate.getFps(); + } +} + +TEST_F(RefreshRateConfigsTest, + getBestRefreshRate_ExplicitExactOrMultiple_WithFractionalRefreshRates) { + auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}}; + auto& lr = layers[0]; + + // Test that 23.976 will choose 24 if 23.976 is not supported + { + android::DisplayModes modes = {mConfig24, mConfig25, mConfig30, + mConfig30Frac, mConfig60, mConfig60Frac}; + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60); + + lr.vote = LayerVoteType::ExplicitExactOrMultiple; + lr.desiredRefreshRate = Fps(23.976f); + lr.name = "ExplicitExactOrMultiple 23.976 fps"; + EXPECT_EQ(HWC_CONFIG_ID_24, + refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) + .getModeId()); + } + + // Test that 24 will choose 23.976 if 24 is not supported + { + android::DisplayModes modes = {mConfig24Frac, mConfig25, mConfig30, + mConfig30Frac, mConfig60, mConfig60Frac}; + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60); + lr.desiredRefreshRate = Fps(24.f); + lr.name = "ExplicitExactOrMultiple 24 fps"; + EXPECT_EQ(HWC_CONFIG_ID_24_FRAC, + refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) + .getModeId()); + } + + // Test that 29.97 will prefer 59.94 over 60 and 30 + { + android::DisplayModes modes = {mConfig24, mConfig24Frac, mConfig25, + mConfig30, mConfig60, mConfig60Frac}; + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60); + lr.desiredRefreshRate = Fps(29.97f); + lr.name = "ExplicitExactOrMultiple 29.97f fps"; + EXPECT_EQ(HWC_CONFIG_ID_60_FRAC, + refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) + .getModeId()); + } +} + +TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact_WithFractionalRefreshRates) { + auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}}; + auto& lr = layers[0]; + + // Test that voting for supported refresh rate will select this refresh rate + { + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(m24_25_30_50_60WithFracDevice, + /*currentConfigId=*/HWC_CONFIG_ID_60); + + for (auto desiredRefreshRate : {23.976f, 24.f, 25.f, 29.97f, 30.f, 50.f, 59.94f, 60.f}) { + lr.vote = LayerVoteType::ExplicitExact; + lr.desiredRefreshRate = Fps(desiredRefreshRate); + std::stringstream ss; + ss << "ExplicitExact " << desiredRefreshRate << " fps"; + lr.name = ss.str(); + + auto selecteRefreshRate = + refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}); + + EXPECT_TRUE(selecteRefreshRate.getFps().equalsWithMargin(lr.desiredRefreshRate)) + << "Expecting " << lr.desiredRefreshRate << " but it was " + << selecteRefreshRate.getFps(); + } + } + + // Test that 23.976 will choose 24 if 23.976 is not supported + { + android::DisplayModes modes = {mConfig24, mConfig25, mConfig30, + mConfig30Frac, mConfig60, mConfig60Frac}; + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60); + lr.vote = LayerVoteType::ExplicitExact; + lr.desiredRefreshRate = Fps(23.976f); + lr.name = "ExplicitExact 23.976 fps"; + EXPECT_EQ(HWC_CONFIG_ID_24, + refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) + .getModeId()); + } + + // Test that 24 will choose 23.976 if 24 is not supported + { + android::DisplayModes modes = {mConfig24Frac, mConfig25, mConfig30, + mConfig30Frac, mConfig60, mConfig60Frac}; + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60); + lr.desiredRefreshRate = Fps(24.f); + lr.name = "ExplicitExact 24 fps"; + EXPECT_EQ(HWC_CONFIG_ID_24_FRAC, + refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) + .getModeId()); } } @@ -2028,6 +2156,29 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactTouchBoost) { refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false})); } +TEST_F(RefreshRateConfigsTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) { + RefreshRateConfigs::Config config = {.enableFrameRateOverride = true}; + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(m24_25_30_50_60WithFracDevice, + /*currentConfigId=*/HWC_CONFIG_ID_60, config); + + auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 0.5f}, + LayerRequirement{.weight = 0.5f}}; + auto& explicitDefaultLayer = layers[0]; + auto& explicitExactOrMultipleLayer = layers[1]; + + explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple; + explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple"; + explicitExactOrMultipleLayer.desiredRefreshRate = Fps(60); + + explicitDefaultLayer.vote = LayerVoteType::ExplicitDefault; + explicitDefaultLayer.name = "ExplicitDefault"; + explicitDefaultLayer.desiredRefreshRate = Fps(59.94f); + + EXPECT_EQ(mExpected60Config, + refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})); +} + TEST_F(RefreshRateConfigsTest, testComparisonOperator) { EXPECT_TRUE(mExpected60Config < mExpected90Config); EXPECT_FALSE(mExpected60Config < mExpected60Config); @@ -2123,6 +2274,33 @@ TEST_F(RefreshRateConfigsTest, getFrameRateDivider) { EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(60.f), Fps(59.94f))); } +TEST_F(RefreshRateConfigsTest, isFractionalPairOrMultiple) { + EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(23.976f), Fps(24.f))); + EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(24.f), Fps(23.976f))); + + EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(29.97f), Fps(30.f))); + EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(30.f), Fps(29.97f))); + + EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(59.94f), Fps(60.f))); + EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(60.f), Fps(59.94f))); + + EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(29.97f), Fps(60.f))); + EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(60.f), Fps(29.97f))); + + EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(59.94f), Fps(30.f))); + EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(30.f), Fps(59.94f))); + + const std::vector<float> refreshRates = {23.976f, 24.f, 25.f, 29.97f, 30.f, 50.f, 59.94f, 60.f}; + for (auto refreshRate : refreshRates) { + EXPECT_FALSE( + RefreshRateConfigs::isFractionalPairOrMultiple(Fps(refreshRate), Fps(refreshRate))); + } + + EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(24.f), Fps(25.f))); + EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(23.978f), Fps(25.f))); + EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(Fps(29.97f), Fps(59.94f))); +} + TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) { auto refreshRateConfigs = std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/ diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp index 12b155bd2c..0a8759de66 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp @@ -81,7 +81,7 @@ DisplayModePtr RefreshRateStatsTest::createDisplayMode(DisplayModeId modeId, int int64_t vsyncPeriod) { return DisplayMode::Builder(static_cast<hal::HWConfigId>(modeId.value())) .setId(modeId) - .setPhysicalDisplayId(PhysicalDisplayId(0)) + .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0)) .setVsyncPeriod(static_cast<int32_t>(vsyncPeriod)) .setGroup(group) .build(); diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 5713c2f64a..e2b3993211 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -34,7 +34,7 @@ using testing::Return; namespace android { namespace { -constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID(999); +constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = PhysicalDisplayId::fromPort(255u); class SchedulerTest : public testing::Test { protected: @@ -53,13 +53,13 @@ protected: const DisplayModePtr mode60 = DisplayMode::Builder(0) .setId(DisplayModeId(0)) - .setPhysicalDisplayId(PhysicalDisplayId(0)) + .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0)) .setVsyncPeriod(Fps(60.f).getPeriodNsecs()) .setGroup(0) .build(); const DisplayModePtr mode120 = DisplayMode::Builder(1) .setId(DisplayModeId(1)) - .setPhysicalDisplayId(PhysicalDisplayId(0)) + .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0)) .setVsyncPeriod(Fps(120.f).getPeriodNsecs()) .setGroup(0) .build(); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp index b713334f99..6959ee35c3 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp @@ -22,8 +22,7 @@ namespace android { namespace { -class HandleTransactionLockedTest : public DisplayTransactionTest { -public: +struct DisplayTransactionCommitTest : DisplayTransactionTest { template <typename Case> void setupCommonPreconditions(); @@ -55,7 +54,7 @@ public: }; template <typename Case> -void HandleTransactionLockedTest::setupCommonPreconditions() { +void DisplayTransactionCommitTest::setupCommonPreconditions() { // Wide color displays support is configured appropriately Case::WideColorSupport::injectConfigChange(this); @@ -68,7 +67,7 @@ void HandleTransactionLockedTest::setupCommonPreconditions() { } template <typename Case, bool connected> -void HandleTransactionLockedTest::expectHotplugReceived(mock::EventThread* eventThread) { +void DisplayTransactionCommitTest::expectHotplugReceived(mock::EventThread* eventThread) { const auto convert = [](auto physicalDisplayId) { return std::make_optional(DisplayId{physicalDisplayId}); }; @@ -79,7 +78,7 @@ void HandleTransactionLockedTest::expectHotplugReceived(mock::EventThread* event } template <typename Case> -void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessing() { +void DisplayTransactionCommitTest::setupCommonCallExpectationsForConnectProcessing() { Case::Display::setupHwcHotplugCallExpectations(this); Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this); @@ -97,7 +96,7 @@ void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessin } template <typename Case> -void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() { +void DisplayTransactionCommitTest::setupCommonCallExpectationsForDisconnectProcessing() { EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1); expectHotplugReceived<Case, false>(mEventThread); @@ -105,7 +104,7 @@ void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProces } template <typename Case> -void HandleTransactionLockedTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) { +void DisplayTransactionCommitTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) { // The display device should have been set up in the list of displays. ASSERT_TRUE(hasDisplayDevice(displayToken)); const auto& device = getDisplayDevice(displayToken); @@ -137,7 +136,7 @@ void HandleTransactionLockedTest::verifyDisplayIsConnected(const sp<IBinder>& di } template <typename Case> -void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() { +void DisplayTransactionCommitTest::verifyPhysicalDisplayIsConnected() { // HWComposer should have an entry for the display EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID)); @@ -150,14 +149,14 @@ void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() { verifyDisplayIsConnected<Case>(displayToken); } -void HandleTransactionLockedTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) { +void DisplayTransactionCommitTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) { EXPECT_FALSE(hasDisplayDevice(displayToken)); EXPECT_FALSE(hasCurrentDisplayState(displayToken)); EXPECT_FALSE(hasDrawingDisplayState(displayToken)); } template <typename Case> -void HandleTransactionLockedTest::processesHotplugConnectCommon() { +void DisplayTransactionCommitTest::processesHotplugConnectCommon() { // -------------------------------------------------------------------- // Preconditions @@ -174,7 +173,7 @@ void HandleTransactionLockedTest::processesHotplugConnectCommon() { // -------------------------------------------------------------------- // Invocation - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); // -------------------------------------------------------------------- // Postconditions @@ -191,7 +190,7 @@ void HandleTransactionLockedTest::processesHotplugConnectCommon() { } template <typename Case> -void HandleTransactionLockedTest::ignoresHotplugConnectCommon() { +void DisplayTransactionCommitTest::ignoresHotplugConnectCommon() { // -------------------------------------------------------------------- // Preconditions @@ -203,7 +202,7 @@ void HandleTransactionLockedTest::ignoresHotplugConnectCommon() { // -------------------------------------------------------------------- // Invocation - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); // -------------------------------------------------------------------- // Postconditions @@ -213,7 +212,7 @@ void HandleTransactionLockedTest::ignoresHotplugConnectCommon() { } template <typename Case> -void HandleTransactionLockedTest::processesHotplugDisconnectCommon() { +void DisplayTransactionCommitTest::processesHotplugDisconnectCommon() { // -------------------------------------------------------------------- // Preconditions @@ -238,7 +237,7 @@ void HandleTransactionLockedTest::processesHotplugDisconnectCommon() { // -------------------------------------------------------------------- // Invocation - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); // -------------------------------------------------------------------- // Postconditions @@ -255,18 +254,18 @@ void HandleTransactionLockedTest::processesHotplugDisconnectCommon() { verifyDisplayIsNotConnected(existing.token()); } -TEST_F(HandleTransactionLockedTest, processesHotplugConnectPrimaryDisplay) { +TEST_F(DisplayTransactionCommitTest, processesHotplugConnectPrimaryDisplay) { processesHotplugConnectCommon<SimplePrimaryDisplayCase>(); } -TEST_F(HandleTransactionLockedTest, processesHotplugConnectExternalDisplay) { +TEST_F(DisplayTransactionCommitTest, processesHotplugConnectExternalDisplay) { // Inject a primary display. PrimaryDisplayVariant::injectHwcDisplay(this); processesHotplugConnectCommon<SimpleExternalDisplayCase>(); } -TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) { +TEST_F(DisplayTransactionCommitTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) { // Inject both a primary and external display. PrimaryDisplayVariant::injectHwcDisplay(this); ExternalDisplayVariant::injectHwcDisplay(this); @@ -281,108 +280,119 @@ TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfPrimaryAndExternalAlr ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>(); } -TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) { - processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>(); +TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectPrimaryDisplay) { + EXPECT_EXIT(processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>(), + testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected."); } -TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) { +TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectExternalDisplay) { processesHotplugDisconnectCommon<SimpleExternalDisplayCase>(); } -TEST_F(HandleTransactionLockedTest, processesHotplugConnectThenDisconnectPrimary) { - using Case = SimplePrimaryDisplayCase; +TEST_F(DisplayTransactionCommitTest, processesHotplugConnectThenDisconnectPrimary) { + EXPECT_EXIT( + [this] { + using Case = SimplePrimaryDisplayCase; - // -------------------------------------------------------------------- - // Preconditions + // -------------------------------------------------------------------- + // Preconditions - setupCommonPreconditions<Case>(); + setupCommonPreconditions<Case>(); - // A hotplug connect event is enqueued for a display - Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED); - // A hotplug disconnect event is also enqueued for the same display - Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED); + // A hotplug connect event is enqueued for a display + Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED); + // A hotplug disconnect event is also enqueued for the same display + Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED); - // -------------------------------------------------------------------- - // Call Expectations + // -------------------------------------------------------------------- + // Call Expectations - setupCommonCallExpectationsForConnectProcessing<Case>(); - setupCommonCallExpectationsForDisconnectProcessing<Case>(); + setupCommonCallExpectationsForConnectProcessing<Case>(); + setupCommonCallExpectationsForDisconnectProcessing<Case>(); - EXPECT_CALL(*mComposer, - setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE)) - .WillOnce(Return(Error::NONE)); - EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*mComposer, + setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, + IComposerClient::Vsync::DISABLE)) + .WillOnce(Return(Error::NONE)); + EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR)); - // -------------------------------------------------------------------- - // Invocation + // -------------------------------------------------------------------- + // Invocation - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); - // -------------------------------------------------------------------- - // Postconditions + // -------------------------------------------------------------------- + // Postconditions - // HWComposer should not have an entry for the display - EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID)); + // HWComposer should not have an entry for the display + EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID)); - // SF should not have a display token. - const auto displayId = Case::Display::DISPLAY_ID::get(); - ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId)); - ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0); + // SF should not have a display token. + const auto displayId = Case::Display::DISPLAY_ID::get(); + ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId)); + ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0); + }(), + testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected."); } -TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) { - using Case = SimplePrimaryDisplayCase; +TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectThenConnectPrimary) { + EXPECT_EXIT( + [this] { + using Case = SimplePrimaryDisplayCase; - // -------------------------------------------------------------------- - // Preconditions + // -------------------------------------------------------------------- + // Preconditions - setupCommonPreconditions<Case>(); + setupCommonPreconditions<Case>(); - // The display is already completely set up. - Case::Display::injectHwcDisplay(this); - auto existing = Case::Display::makeFakeExistingDisplayInjector(this); - existing.inject(); + // The display is already completely set up. + Case::Display::injectHwcDisplay(this); + auto existing = Case::Display::makeFakeExistingDisplayInjector(this); + existing.inject(); - // A hotplug disconnect event is enqueued for a display - Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED); - // A hotplug connect event is also enqueued for the same display - Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED); + // A hotplug disconnect event is enqueued for a display + Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED); + // A hotplug connect event is also enqueued for the same display + Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED); - // -------------------------------------------------------------------- - // Call Expectations + // -------------------------------------------------------------------- + // Call Expectations - setupCommonCallExpectationsForConnectProcessing<Case>(); - setupCommonCallExpectationsForDisconnectProcessing<Case>(); + setupCommonCallExpectationsForConnectProcessing<Case>(); + setupCommonCallExpectationsForDisconnectProcessing<Case>(); - // -------------------------------------------------------------------- - // Invocation + // -------------------------------------------------------------------- + // Invocation - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); - // -------------------------------------------------------------------- - // Postconditions + // -------------------------------------------------------------------- + // Postconditions - // The existing token should have been removed - verifyDisplayIsNotConnected(existing.token()); - const auto displayId = Case::Display::DISPLAY_ID::get(); - ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId)); - ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1); - EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[displayId]); + // The existing token should have been removed + verifyDisplayIsNotConnected(existing.token()); + const auto displayId = Case::Display::DISPLAY_ID::get(); + ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId)); + ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1); + EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[displayId]); - // A new display should be connected in its place + // A new display should be connected in its place - verifyPhysicalDisplayIsConnected<Case>(); + verifyPhysicalDisplayIsConnected<Case>(); - // -------------------------------------------------------------------- - // Cleanup conditions + // -------------------------------------------------------------------- + // Cleanup conditions - EXPECT_CALL(*mComposer, - setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE)) - .WillOnce(Return(Error::NONE)); - EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*mComposer, + setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, + IComposerClient::Vsync::DISABLE)) + .WillOnce(Return(Error::NONE)); + EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR)); + }(), + testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected."); } -TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) { +TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayAdded) { using Case = HwcVirtualDisplayCase; // -------------------------------------------------------------------- @@ -433,7 +443,7 @@ TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) { // -------------------------------------------------------------------- // Invocation - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); // -------------------------------------------------------------------- // Postconditions @@ -453,7 +463,7 @@ TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) { mFlinger.mutableDrawingState().displays.removeItem(displayToken); } -TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) { +TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayAddedWithNoSurface) { using Case = HwcVirtualDisplayCase; // -------------------------------------------------------------------- @@ -479,7 +489,7 @@ TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) { // -------------------------------------------------------------------- // Invocation - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); // -------------------------------------------------------------------- // Postconditions @@ -493,7 +503,7 @@ TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) { EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual()); } -TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) { +TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayRemoval) { using Case = HwcVirtualDisplayCase; // -------------------------------------------------------------------- @@ -511,7 +521,7 @@ TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) { // -------------------------------------------------------------------- // Invocation - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); // -------------------------------------------------------------------- // Postconditions @@ -520,11 +530,11 @@ TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) { verifyDisplayIsNotConnected(existing.token()); } -TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackChanges) { +TEST_F(DisplayTransactionCommitTest, processesDisplayLayerStackChanges) { using Case = NonHwcVirtualDisplayCase; - constexpr uint32_t oldLayerStack = 0u; - constexpr uint32_t newLayerStack = 123u; + constexpr ui::LayerStack oldLayerStack = ui::DEFAULT_LAYER_STACK; + constexpr ui::LayerStack newLayerStack{123u}; // -------------------------------------------------------------------- // Preconditions @@ -540,7 +550,7 @@ TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackChanges) { // -------------------------------------------------------------------- // Invocation - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); // -------------------------------------------------------------------- // Postconditions @@ -548,7 +558,7 @@ TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackChanges) { EXPECT_EQ(newLayerStack, display.mutableDisplayDevice()->getLayerStack()); } -TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) { +TEST_F(DisplayTransactionCommitTest, processesDisplayTransformChanges) { using Case = NonHwcVirtualDisplayCase; constexpr ui::Rotation oldTransform = ui::ROTATION_0; @@ -568,7 +578,7 @@ TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) { // -------------------------------------------------------------------- // Invocation - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); // -------------------------------------------------------------------- // Postconditions @@ -576,7 +586,7 @@ TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) { EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation()); } -TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackRectChanges) { +TEST_F(DisplayTransactionCommitTest, processesDisplayLayerStackRectChanges) { using Case = NonHwcVirtualDisplayCase; const Rect oldLayerStackRect(0, 0, 0, 0); @@ -596,7 +606,7 @@ TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackRectChanges) { // -------------------------------------------------------------------- // Invocation - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); // -------------------------------------------------------------------- // Postconditions @@ -604,7 +614,7 @@ TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackRectChanges) { EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect()); } -TEST_F(HandleTransactionLockedTest, processesDisplayRectChanges) { +TEST_F(DisplayTransactionCommitTest, processesDisplayRectChanges) { using Case = NonHwcVirtualDisplayCase; const Rect oldDisplayRect(0, 0); @@ -624,7 +634,7 @@ TEST_F(HandleTransactionLockedTest, processesDisplayRectChanges) { // -------------------------------------------------------------------- // Invocation - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); // -------------------------------------------------------------------- // Postconditions @@ -632,7 +642,7 @@ TEST_F(HandleTransactionLockedTest, processesDisplayRectChanges) { EXPECT_EQ(newDisplayRect, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect()); } -TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) { +TEST_F(DisplayTransactionCommitTest, processesDisplayWidthChanges) { using Case = NonHwcVirtualDisplayCase; constexpr int oldWidth = 0; @@ -674,10 +684,10 @@ TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) { // -------------------------------------------------------------------- // Invocation - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); } -TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) { +TEST_F(DisplayTransactionCommitTest, processesDisplayHeightChanges) { using Case = NonHwcVirtualDisplayCase; constexpr int oldWidth = 0; @@ -719,10 +729,10 @@ TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) { // -------------------------------------------------------------------- // Invocation - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); } -TEST_F(HandleTransactionLockedTest, processesDisplaySizeDisplayRectAndLayerStackRectChanges) { +TEST_F(DisplayTransactionCommitTest, processesDisplaySizeDisplayRectAndLayerStackRectChanges) { using Case = NonHwcVirtualDisplayCase; constexpr uint32_t kOldWidth = 567; @@ -769,7 +779,7 @@ TEST_F(HandleTransactionLockedTest, processesDisplaySizeDisplayRectAndLayerStack // -------------------------------------------------------------------- // Invocation - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded); EXPECT_EQ(display.mutableDisplayDevice()->getBounds(), kNewSize); EXPECT_EQ(display.mutableDisplayDevice()->getWidth(), kNewWidth); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp index ef8b1493ae..bafa910270 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp @@ -63,15 +63,11 @@ TEST_F(OnInitializeDisplaysTest, onInitializeDisplaysSetsUpPrimaryDisplay) { // The primary display should have a current state ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token())); const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token()); - // The layer stack state should be set to zero - EXPECT_EQ(0u, primaryDisplayState.layerStack); - // The orientation state should be set to zero - EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation); - // The orientedDisplaySpaceRect state should be set to INVALID + // The primary display state should be reset + EXPECT_EQ(ui::DEFAULT_LAYER_STACK, primaryDisplayState.layerStack); + EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation); EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.orientedDisplaySpaceRect); - - // The layerStackSpaceRect state should be set to INVALID EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.layerStackSpaceRect); // The width and height should both be zero @@ -99,4 +95,4 @@ TEST_F(OnInitializeDisplaysTest, onInitializeDisplaysSetsUpPrimaryDisplay) { } } // namespace -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp index fc40818ad1..7d9e22b23e 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp @@ -25,6 +25,8 @@ namespace android { namespace { +constexpr ui::LayerStack LAYER_STACK{456u}; + class SetDisplayStateLockedTest : public DisplayTransactionTest {}; TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingWithUnknownDisplay) { @@ -38,7 +40,7 @@ TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingWithUnknownDis DisplayState state; state.what = DisplayState::eLayerStackChanged; state.token = displayToken; - state.layerStack = 456; + state.layerStack = LAYER_STACK; // -------------------------------------------------------------------- // Invocation @@ -167,13 +169,13 @@ TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfLayerStackDi display.inject(); // The display has a layer stack set - display.mutableCurrentDisplayState().layerStack = 456u; + display.mutableCurrentDisplayState().layerStack = LAYER_STACK; // The incoming request sets the same layer stack DisplayState state; state.what = DisplayState::eLayerStackChanged; state.token = display.token(); - state.layerStack = 456u; + state.layerStack = LAYER_STACK; // -------------------------------------------------------------------- // Invocation @@ -187,7 +189,7 @@ TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfLayerStackDi EXPECT_EQ(0u, flags); // The current display state is unchanged - EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack); + EXPECT_EQ(LAYER_STACK, display.getCurrentDisplayState().layerStack); } TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfLayerStackChanged) { @@ -201,13 +203,13 @@ TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfLayerStac display.inject(); // The display has a layer stack set - display.mutableCurrentDisplayState().layerStack = 654u; + display.mutableCurrentDisplayState().layerStack = ui::LayerStack{LAYER_STACK.id + 1}; // The incoming request sets a different layer stack DisplayState state; state.what = DisplayState::eLayerStackChanged; state.token = display.token(); - state.layerStack = 456u; + state.layerStack = LAYER_STACK; // -------------------------------------------------------------------- // Invocation @@ -221,7 +223,7 @@ TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfLayerStac EXPECT_EQ(eDisplayTransactionNeeded, flags); // The desired display state has been set to the new value. - EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack); + EXPECT_EQ(LAYER_STACK, display.getCurrentDisplayState().layerStack); } TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfFlagsNotChanged) { diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp index e32c4bf145..7ead0af26d 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp @@ -292,7 +292,9 @@ TEST_F(SetupNewDisplayDeviceInternalTest, createSimplePrimaryDisplay) { } TEST_F(SetupNewDisplayDeviceInternalTest, createSimpleExternalDisplay) { - setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>(); + // External displays must be secondary, as the primary display cannot be disconnected. + EXPECT_EXIT(setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>(), + testing::KilledBySignal(SIGABRT), "Missing primary display"); } TEST_F(SetupNewDisplayDeviceInternalTest, createNonHwcVirtualDisplay) { diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 40c881e902..d8352ed9d4 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -209,7 +209,7 @@ public: ISchedulerCallback* callback = nullptr, bool hasMultipleModes = false) { DisplayModes modes{DisplayMode::Builder(0) .setId(DisplayModeId(0)) - .setPhysicalDisplayId(PhysicalDisplayId(0)) + .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0)) .setVsyncPeriod(16'666'667) .setGroup(0) .build()}; @@ -217,7 +217,7 @@ public: if (hasMultipleModes) { modes.emplace_back(DisplayMode::Builder(1) .setId(DisplayModeId(1)) - .setPhysicalDisplayId(PhysicalDisplayId(0)) + .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0)) .setVsyncPeriod(11'111'111) .setGroup(0) .build()); @@ -316,9 +316,9 @@ public: dispSurface, producer); } - auto handleTransactionLocked(uint32_t transactionFlags) { - Mutex::Autolock _l(mFlinger->mStateLock); - return mFlinger->handleTransactionLocked(transactionFlags); + auto commitTransactionsLocked(uint32_t transactionFlags) { + Mutex::Autolock lock(mFlinger->mStateLock); + return mFlinger->commitTransactionsLocked(transactionFlags); } void onComposerHalHotplug(hal::HWDisplayId hwcDisplayId, hal::Connection connection) { @@ -326,7 +326,7 @@ public: } auto setDisplayStateLocked(const DisplayState& s) { - Mutex::Autolock _l(mFlinger->mStateLock); + Mutex::Autolock lock(mFlinger->mStateLock); return mFlinger->setDisplayStateLocked(s); } @@ -426,7 +426,7 @@ public: auto& mutableDisplays() { return mFlinger->mDisplays; } auto& mutableDrawingState() { return mFlinger->mDrawingState; } auto& mutableEventQueue() { return mFlinger->mEventQueue; } - auto& mutableGeometryInvalid() { return mFlinger->mGeometryInvalid; } + auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; } auto& mutableInterceptor() { return mFlinger->mInterceptor; } auto& mutableMainThreadId() { return mFlinger->mMainThreadId; } auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; } @@ -439,8 +439,7 @@ public: auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; } auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; } - auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; } - auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; } + auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; } auto& mutableUseFrameRateApi() { return mFlinger->useFrameRateApi; } auto fromHandle(const sp<IBinder>& handle) { @@ -600,13 +599,12 @@ public: LOG_ALWAYS_FATAL_IF(!physicalId); flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, *physicalId); if (mIsPrimary) { - flinger->mutableInternalHwcDisplayId() = mHwcDisplayId; + flinger->mutablePrimaryHwcDisplayId() = mHwcDisplayId; } else { - // If there is an external HWC display there should always be an internal ID + // If there is an external HWC display, there should always be a primary ID // as well. Set it to some arbitrary value. - auto& internalId = flinger->mutableInternalHwcDisplayId(); - if (!internalId) internalId = mHwcDisplayId - 1; - flinger->mutableExternalHwcDisplayId() = mHwcDisplayId; + auto& primaryId = flinger->mutablePrimaryHwcDisplayId(); + if (!primaryId) primaryId = mHwcDisplayId - 1; } } } @@ -645,7 +643,7 @@ public: DisplayModePtr activeMode = DisplayMode::Builder(FakeHwcDisplayInjector::DEFAULT_ACTIVE_CONFIG) .setId(mActiveModeId) - .setPhysicalDisplayId(PhysicalDisplayId(0)) + .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0)) .setWidth(FakeHwcDisplayInjector::DEFAULT_WIDTH) .setHeight(FakeHwcDisplayInjector::DEFAULT_HEIGHT) .setVsyncPeriod(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD) @@ -764,9 +762,9 @@ public: }; private: + void scheduleRefresh(FrameHint) override {} void setVsyncEnabled(bool) override {} void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override {} - void repaintEverythingForHWC() override {} void kernelTimerChanged(bool) override {} void triggerOnFrameRateOverridesChanged() {} diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp index 7de187207e..20d41e6072 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp @@ -18,6 +18,7 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" +#undef LOG_TAG #define LOG_TAG "MockComposer" #include "mock/DisplayHardware/MockComposer.h" diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h index ab19886755..291559f2f9 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h +++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h @@ -23,20 +23,20 @@ namespace android::mock { struct SchedulerCallback final : ISchedulerCallback { + MOCK_METHOD(void, scheduleRefresh, (FrameHint), (override)); MOCK_METHOD1(setVsyncEnabled, void(bool)); MOCK_METHOD2(changeRefreshRate, void(const scheduler::RefreshRateConfigs::RefreshRate&, scheduler::RefreshRateConfigEvent)); - MOCK_METHOD0(repaintEverythingForHWC, void()); MOCK_METHOD1(kernelTimerChanged, void(bool)); MOCK_METHOD0(triggerOnFrameRateOverridesChanged, void()); }; struct NoOpSchedulerCallback final : ISchedulerCallback { + void scheduleRefresh(FrameHint) override {} void setVsyncEnabled(bool) override {} void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&, scheduler::RefreshRateConfigEvent) override {} - void repaintEverythingForHWC() override {} void kernelTimerChanged(bool) override {} void triggerOnFrameRateOverridesChanged() {} }; diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp index 26052fba63..33401d24d6 100644 --- a/vulkan/libvulkan/api_gen.cpp +++ b/vulkan/libvulkan/api_gen.cpp @@ -560,6 +560,7 @@ VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pNa } static const char* const known_non_device_names[] = { + "vkAcquireDrmDisplayEXT", "vkCreateAndroidSurfaceKHR", "vkCreateDebugReportCallbackEXT", "vkCreateDebugUtilsMessengerEXT", @@ -581,6 +582,7 @@ VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pNa "vkEnumeratePhysicalDevices", "vkGetDisplayModeProperties2KHR", "vkGetDisplayPlaneCapabilities2KHR", + "vkGetDrmDisplayEXT", "vkGetInstanceProcAddr", "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT", "vkGetPhysicalDeviceDisplayPlaneProperties2KHR", @@ -624,6 +626,8 @@ VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pNa "vkGetPhysicalDeviceSurfacePresentModesKHR", "vkGetPhysicalDeviceSurfaceSupportKHR", "vkGetPhysicalDeviceToolPropertiesEXT", + "vkGetPhysicalDeviceVideoCapabilitiesKHR", + "vkGetPhysicalDeviceVideoFormatPropertiesKHR", "vkSubmitDebugUtilsMessageEXT", }; // clang-format on diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index d7fdab5586..cf774fd9b8 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -979,6 +979,8 @@ VkResult EnumerateInstanceExtensionProperties( void QueryPresentationProperties( VkPhysicalDevice physicalDevice, VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties) { + ATRACE_CALL(); + // Request the android-specific presentation properties via GPDP2 VkPhysicalDeviceProperties2 properties = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, @@ -994,7 +996,17 @@ void QueryPresentationProperties( presentation_properties->pNext = nullptr; presentation_properties->sharedImage = VK_FALSE; - GetPhysicalDeviceProperties2(physicalDevice, &properties); + const auto& driver = GetData(physicalDevice).driver; + + if (driver.GetPhysicalDeviceProperties2) { + // >= 1.1 driver, supports core GPDP2 entrypoint. + driver.GetPhysicalDeviceProperties2(physicalDevice, &properties); + } else if (driver.GetPhysicalDeviceProperties2KHR) { + // Old driver, but may support presentation properties + // if we have the GPDP2 extension. Otherwise, no presentation + // properties supported. + driver.GetPhysicalDeviceProperties2KHR(physicalDevice, &properties); + } } VkResult EnumerateDeviceExtensionProperties( diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py index 72fd4fbc9c..4176509447 100644 --- a/vulkan/scripts/generator_common.py +++ b/vulkan/scripts/generator_common.py @@ -33,6 +33,7 @@ _BLOCKED_EXTENSIONS = [ 'VK_EXT_metal_surface', 'VK_FUCHSIA_imagepipe_surface', 'VK_GGP_stream_descriptor_surface', + 'VK_HUAWEI_subpass_shading', 'VK_KHR_display', 'VK_KHR_display_swapchain', 'VK_KHR_external_fence_win32', @@ -47,11 +48,13 @@ _BLOCKED_EXTENSIONS = [ 'VK_MVK_ios_surface', 'VK_MVK_macos_surface', 'VK_NN_vi_surface', + 'VK_NV_acquire_winrt_display', 'VK_NV_cooperative_matrix', 'VK_NV_coverage_reduction_mode', 'VK_NV_external_memory_win32', 'VK_NV_win32_keyed_mutex', 'VK_NVX_image_view_handle', + 'VK_QNX_screen_surface', ] # Extensions having functions exported by the loader. |