diff options
295 files changed, 9844 insertions, 5893 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/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/choreographer.h b/include/android/choreographer.h index b743f491e4..0389e573a2 100644 --- a/include/android/choreographer.h +++ b/include/android/choreographer.h @@ -39,6 +39,12 @@ struct AChoreographer; */ typedef struct AChoreographer AChoreographer; +struct AChoreographerFrameCallbackData; +/** + * Opaque type that provides access to an AChoreographerFrameCallbackData object. + */ +typedef struct AChoreographerFrameCallbackData AChoreographerFrameCallbackData; + /** * Prototype of the function that is called when a new frame is being rendered. * It's passed the time that the frame is being rendered as nanoseconds in the @@ -60,6 +66,14 @@ typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data); typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data); /** + * Prototype of the function that is called when a new frame is being rendered. + * It's passed the frame data that should not outlive the callback, as well as the data pointer + * provided by the application that registered a callback. + */ +typedef void (*AChoreographer_extendedFrameCallback)( + const AChoreographerFrameCallbackData* callbackData, void* data); + +/** * Prototype of the function that is called when the display refresh rate * changes. It's passed the new vsync period in nanoseconds, as well as the data * pointer provided by the application that registered a callback. @@ -111,6 +125,14 @@ void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, uint32_t delayMillis) __INTRODUCED_IN(29); /** + * Posts a callback to run on the next frame. The data pointer provided will + * be passed to the callback function when it's called. + */ +void AChoreographer_postExtendedFrameCallback(AChoreographer* choreographer, + AChoreographer_extendedFrameCallback callback, void* data) + __INTRODUCED_IN(33); + +/** * Registers a callback to be run when the display refresh rate changes. The * data pointer provided will be passed to the callback function when it's * called. The same callback may be registered multiple times, provided that a @@ -160,6 +182,42 @@ void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer, AChoreographer_refreshRateCallback, void* data) __INTRODUCED_IN(30); +/** + * The time in nanoseconds when the frame started being rendered. + */ +int64_t AChoreographerFrameCallbackData_getFrameTimeNanos( + const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33); + +/** + * The number of possible frame timelines. + */ +size_t AChoreographerFrameCallbackData_getFrameTimelinesLength( + const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33); + +/** + * Get index of the platform-preferred FrameTimeline. + */ +size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex( + const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33); + +/** + * The vsync ID token used to map Choreographer data. + */ +int64_t AChoreographerFrameCallbackData_getFrameTimelineVsyncId( + const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33); + +/** + * The time in nanoseconds which the frame at given index is expected to be presented. + */ +int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime( + const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33); + +/** + * The time in nanoseconds which the frame at given index needs to be ready by. + */ +int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline( + const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33); + __END_DECLS #endif // ANDROID_CHOREOGRAPHER_H 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/initializer_list.h b/include/ftl/initializer_list.h index 769c09ff11..2102c253f1 100644 --- a/include/ftl/initializer_list.h +++ b/include/ftl/initializer_list.h @@ -16,6 +16,7 @@ #pragma once +#include <functional> #include <tuple> #include <utility> @@ -65,18 +66,18 @@ struct InitializerList<T, std::index_sequence<Sizes...>, Types...> { std::tuple<Types...> tuple; }; -template <typename K, typename V> +template <typename K, typename V, typename KeyEqual = std::equal_to<K>> struct KeyValue {}; // Shorthand for key-value pairs that assigns the first argument to the key, and the rest to the // value. The specialization is on KeyValue rather than std::pair, so that ftl::init::list works // with the latter. -template <typename K, typename V, std::size_t... Sizes, typename... Types> -struct InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...> { +template <typename K, typename V, typename E, std::size_t... Sizes, typename... Types> +struct InitializerList<KeyValue<K, V, E>, std::index_sequence<Sizes...>, Types...> { // Accumulate the three arguments to std::pair's piecewise constructor. template <typename... Args> [[nodiscard]] constexpr auto operator()(K&& k, Args&&... args) && -> InitializerList< - KeyValue<K, V>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t, + KeyValue<K, V, E>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t, std::tuple<K&&>, std::tuple<Args&&...>> { return {std::tuple_cat( std::move(tuple), @@ -94,9 +95,9 @@ template <typename T, typename... Args> return InitializerList<T>{}(std::forward<Args>(args)...); } -template <typename K, typename V, typename... Args> +template <typename K, typename V, typename E = std::equal_to<K>, typename... Args> [[nodiscard]] constexpr auto map(Args&&... args) { - return list<KeyValue<K, V>>(std::forward<Args>(args)...); + return list<KeyValue<K, V, E>>(std::forward<Args>(args)...); } template <typename K, typename V> diff --git a/include/ftl/small_map.h b/include/ftl/small_map.h index 84c15ebdca..2effaa437c 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,18 +47,21 @@ 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()); // -template <typename K, typename V, std::size_t N> +// assert(map == SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc"))); +// +template <typename K, typename V, std::size_t N, typename KeyEqual = std::equal_to<K>> class SmallMap final { using Map = SmallVector<std::pair<const K, V>, N>; @@ -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,14 +146,14 @@ 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) { + if (KeyEqual{}(k, key)) { if constexpr (std::is_void_v<R>) { f(v); return true; @@ -165,28 +167,119 @@ 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()); } + + // Removes all mappings. + // + // All iterators are invalidated. + // + void clear() { map_.clear(); } + private: + iterator find(const key_type& key, iterator first) { + return std::find_if(first, end(), + [&key](const auto& pair) { return KeyEqual{}(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_; }; // Deduction guide for in-place constructor. -template <typename K, typename V, std::size_t... Sizes, typename... Types> -SmallMap(InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...>&&) - -> SmallMap<K, V, sizeof...(Sizes)>; +template <typename K, typename V, typename E, std::size_t... Sizes, typename... Types> +SmallMap(InitializerList<KeyValue<K, V, E>, std::index_sequence<Sizes...>, Types...>&&) + -> SmallMap<K, V, sizeof...(Sizes), E>; // Returns whether the key-value pairs of two maps are equal. -template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M> -bool operator==(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) { +template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M, typename E> +bool operator==(const SmallMap<K, V, N, E>& lhs, const SmallMap<Q, W, M, E>& rhs) { if (lhs.size() != rhs.size()) return false; 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; } } @@ -195,8 +288,8 @@ bool operator==(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) { } // TODO: Remove in C++20. -template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M> -inline bool operator!=(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) { +template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M, typename E> +inline bool operator!=(const SmallMap<K, V, N, E>& lhs, const SmallMap<Q, W, M, E>& rhs) { return !(lhs == rhs); } diff --git a/include/ftl/small_vector.h b/include/ftl/small_vector.h index cb0ae359eb..65a953670d 100644 --- a/include/ftl/small_vector.h +++ b/include/ftl/small_vector.h @@ -151,8 +151,6 @@ class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> { DISPATCH(reference, back, noexcept) DISPATCH(const_reference, back, const) -#undef DISPATCH - reference operator[](size_type i) { return dynamic() ? std::get<Dynamic>(vector_)[i] : std::get<Static>(vector_)[i]; } @@ -214,13 +212,15 @@ class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> { // // The last() and end() iterators are invalidated. // - void pop_back() { - if (dynamic()) { - std::get<Dynamic>(vector_).pop_back(); - } else { - std::get<Static>(vector_).pop_back(); - } - } + DISPATCH(void, pop_back, noexcept) + + // Removes all elements. + // + // All iterators are invalidated. + // + DISPATCH(void, clear, noexcept) + +#undef DISPATCH // Erases an element, but does not preserve order. Rather than shifting subsequent elements, // this moves the last element to the slot of the erased element. @@ -345,10 +345,11 @@ class SmallVector<T, 0> final : ArrayTraits<T>, return true; } + using Impl::clear; 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/static_vector.h b/include/ftl/static_vector.h index 96a1ae853d..cd7b92a758 100644 --- a/include/ftl/static_vector.h +++ b/include/ftl/static_vector.h @@ -189,8 +189,7 @@ class StaticVector final : ArrayTraits<T>, } StaticVector& operator=(StaticVector&& other) { - std::destroy(begin(), end()); - size_ = 0; + clear(); swap<true>(other); return *this; } @@ -280,6 +279,15 @@ class StaticVector final : ArrayTraits<T>, // void pop_back() { unstable_erase(last()); } + // Removes all elements. + // + // All iterators are invalidated. + // + void clear() { + std::destroy(begin(), end()); + size_ = 0; + } + // Erases an element, but does not preserve order. Rather than shifting subsequent elements, // this moves the last element to the slot of the erased element. // 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..5015e68121 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; } @@ -577,9 +579,7 @@ public: void setCursorPosition(float x, float y); - uint32_t getDisplayOrientation() const { return mDisplayOrientation; } - - int2 getDisplaySize() const { return {mDisplayWidth, mDisplayHeight}; } + ui::Transform getRawTransform() const { return mRawTransform; } static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); } @@ -755,8 +755,8 @@ public: int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, const ui::Transform& transform, float xPrecision, float yPrecision, float rawXCursorPosition, - float rawYCursorPosition, uint32_t displayOrientation, int32_t displayWidth, - int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, + float rawYCursorPosition, const ui::Transform& rawTransform, nsecs_t downTime, + nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); void copyFrom(const MotionEvent* other, bool keepHistory); @@ -801,6 +801,8 @@ public: static std::string actionToString(int32_t action); + static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy); + protected: int32_t mAction; int32_t mActionButton; @@ -814,9 +816,7 @@ protected: float mYPrecision; float mRawXCursorPosition; float mRawYCursorPosition; - uint32_t mDisplayOrientation; - int32_t mDisplayWidth; - int32_t mDisplayHeight; + ui::Transform mRawTransform; nsecs_t mDownTime; Vector<PointerProperties> mPointerProperties; std::vector<nsecs_t> mSampleEventTimes; @@ -888,6 +888,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 +971,7 @@ public: virtual FocusEvent* createFocusEvent() = 0; virtual CaptureEvent* createCaptureEvent() = 0; virtual DragEvent* createDragEvent() = 0; + virtual TouchModeEvent* createTouchModeEvent() = 0; }; /* @@ -968,6 +988,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 +996,7 @@ private: FocusEvent mFocusEvent; CaptureEvent mCaptureEvent; DragEvent mDragEvent; + TouchModeEvent mTouchModeEvent; }; /* @@ -990,6 +1012,7 @@ public: virtual FocusEvent* createFocusEvent() override; virtual CaptureEvent* createCaptureEvent() override; virtual DragEvent* createDragEvent() override; + virtual TouchModeEvent* createTouchModeEvent() override; void recycle(InputEvent* event); @@ -1001,6 +1024,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..d655b28278 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 { @@ -111,7 +114,7 @@ struct InputMessage { struct Motion { int32_t eventId; - uint32_t empty1; + uint32_t pointerCount; nsecs_t eventTime __attribute__((aligned(8))); int32_t deviceId; int32_t source; @@ -126,20 +129,22 @@ struct InputMessage { uint8_t empty2[3]; // 3 bytes to fill gap created by classification int32_t edgeFlags; nsecs_t downTime __attribute__((aligned(8))); - float dsdx; - float dtdx; - float dtdy; - float dsdy; - float tx; - float ty; + float dsdx; // Begin window transform + float dtdx; // + float dtdy; // + float dsdy; // + float tx; // + float ty; // End window transform float xPrecision; float yPrecision; float xCursorPosition; float yCursorPosition; - uint32_t displayOrientation; - int32_t displayWidth; - int32_t displayHeight; - uint32_t pointerCount; + float dsdxRaw; // Begin raw transform + float dtdxRaw; // + float dtdyRaw; // + float dsdyRaw; // + float txRaw; // + float tyRaw; // End raw transform /** * The "pointers" field must be the last field of the struct InputMessage. * When we send the struct InputMessage across the socket, we are not @@ -206,6 +211,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; @@ -355,9 +369,8 @@ public: int32_t metaState, int32_t buttonState, MotionClassification classification, const ui::Transform& transform, float xPrecision, float yPrecision, float xCursorPosition, - float yCursorPosition, uint32_t displayOrientation, - int32_t displayWidth, int32_t displayHeight, nsecs_t downTime, - nsecs_t eventTime, uint32_t pointerCount, + float yCursorPosition, const ui::Transform& rawTransform, + nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); @@ -388,6 +401,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 +680,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..03e1f2a022 --- /dev/null +++ b/libs/battery/MultiStateCounter.h @@ -0,0 +1,271 @@ +/* + * 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); + + /** + * Updates the value for the current state and returns the delta from the previously + * set value. + */ + const T& updateValue(const T& value, time_t timestamp); + + void addValue(const T& value); + + 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> +const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) { + T* returnValue = &emptyValue; + + // 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)) { + returnValue = &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; + return *returnValue; +} + +template <class T> +void MultiStateCounter<T>::addValue(const T& value) { + if (!isEnabled) { + return; + } + + add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */); +} + +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"; + } + if (!isEnabled) { + str << " disabled"; + } + 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..848fd10d15 --- /dev/null +++ b/libs/battery/MultiStateCounterTest.cpp @@ -0,0 +1,227 @@ +/* + * 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); + double delta = 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)); + EXPECT_DOUBLE_EQ(3.14, delta); +} + +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); + double delta = 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)); + + // 11.0-8.0 + EXPECT_DOUBLE_EQ(3.0, delta); +} + +TEST_F(MultiStateCounterTest, addValue) { + DoubleMultiStateCounter testCounter(1, 0); + testCounter.updateValue(0, 0); + testCounter.setState(0, 0); + testCounter.updateValue(6.0, 2000); + + testCounter.addValue(8.0); + + EXPECT_DOUBLE_EQ(14.0, testCounter.getCount(0)); + + testCounter.setEnabled(false, 3000); + testCounter.addValue(888.0); + + EXPECT_DOUBLE_EQ(14.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/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index 43533c5277..782328df02 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h @@ -289,7 +289,7 @@ bool AIBinder_isAlive(const AIBinder* binder) __INTRODUCED_IN(29); /** * Built-in transaction for all binder objects. This sends a transaction that will immediately * return. Usually this is used to make sure that a binder is alive, as a placeholder call, or as a - * sanity check. + * consistency check. * * Available since API level 29. * 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..ee650e5627 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,290 @@ 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()); + } +} + +TEST(SmallMap, Clear) { + SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3'); + + map.clear(); + + EXPECT_TRUE(map.empty()); + EXPECT_FALSE(map.dynamic()); + + map = ftl::init::map(1, '1')(2, '2')(3, '3'); + map.try_emplace(4, '4'); + + map.clear(); + + EXPECT_TRUE(map.empty()); + EXPECT_TRUE(map.dynamic()); +} + +TEST(SmallMap, KeyEqual) { + struct KeyEqual { + bool operator()(int lhs, int rhs) const { return lhs % 10 == rhs % 10; } + }; + + SmallMap<int, char, 1, KeyEqual> map; + + EXPECT_TRUE(map.try_emplace(3, '3').second); + EXPECT_FALSE(map.try_emplace(13, '3').second); + + EXPECT_TRUE(map.try_emplace(22, '2').second); + EXPECT_TRUE(map.contains(42)); + + EXPECT_TRUE(map.try_emplace(111, '1').second); + EXPECT_EQ(map.get(321), '1'); + + map.erase(123); + EXPECT_EQ(map, SmallMap(ftl::init::map<int, char, KeyEqual>(1, '1')(2, '2'))); +} + } // namespace android::test diff --git a/libs/ftl/small_vector_test.cpp b/libs/ftl/small_vector_test.cpp index 3a03e696d1..42374969f1 100644 --- a/libs/ftl/small_vector_test.cpp +++ b/libs/ftl/small_vector_test.cpp @@ -460,4 +460,34 @@ TEST(SmallVector, Destroy) { EXPECT_EQ(0, dead); } +TEST(SmallVector, Clear) { + int live = 0; + int dead = 0; + + SmallVector<DestroyCounts, 2> counts; + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + + counts.clear(); + + EXPECT_TRUE(counts.empty()); + EXPECT_FALSE(counts.dynamic()); + + EXPECT_EQ(2, live); + EXPECT_EQ(0, dead); + + live = 0; + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + + counts.clear(); + + EXPECT_TRUE(counts.empty()); + EXPECT_TRUE(counts.dynamic()); + + EXPECT_EQ(3, live); + EXPECT_EQ(2, dead); +} + } // namespace android::test diff --git a/libs/ftl/static_vector_test.cpp b/libs/ftl/static_vector_test.cpp index cbe8dff527..2de3ad273e 100644 --- a/libs/ftl/static_vector_test.cpp +++ b/libs/ftl/static_vector_test.cpp @@ -396,4 +396,19 @@ TEST(StaticVector, Destroy) { EXPECT_EQ(0, dead); } +TEST(StaticVector, Clear) { + int live = 0; + int dead = 0; + + StaticVector<DestroyCounts, 5> counts; + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + + counts.clear(); + + EXPECT_TRUE(counts.empty()); + EXPECT_EQ(2, live); + EXPECT_EQ(0, dead); +} + } // 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/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp index cda9e19c1e..6afd1729de 100644 --- a/libs/gralloc/types/Android.bp +++ b/libs/gralloc/types/Android.bp @@ -33,7 +33,7 @@ cc_library { target: { darwin: { enabled: false, - } + }, }, vendor_available: true, @@ -48,18 +48,18 @@ cc_library { min_sdk_version: "29", srcs: [ - "Gralloc4.cpp" + "Gralloc4.cpp", ], shared_libs: [ - "android.hardware.graphics.common-V2-ndk", + "android.hardware.graphics.common-V3-ndk", "android.hardware.graphics.mapper@4.0", "libhidlbase", "liblog", ], export_shared_lib_headers: [ - "android.hardware.graphics.common-V2-ndk", + "android.hardware.graphics.common-V3-ndk", "android.hardware.graphics.mapper@4.0", "libhidlbase", ], diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 8c250a4d0b..8c359c7756 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -55,7 +55,7 @@ cc_library_headers { filegroup { name: "guiconstants_aidl", srcs: [ - "android/**/DropInputMode.aidl", + "android/gui/DropInputMode.aidl", "android/**/TouchOcclusionMode.aidl", ], } @@ -66,11 +66,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", ], @@ -91,7 +93,7 @@ cc_library_static { ], aidl: { - export_aidl_headers: true + export_aidl_headers: true, }, include_dirs: [ @@ -136,8 +138,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 e8e664bd76..2b17616eda 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -468,12 +468,12 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); - t->setBuffer(mSurfaceControl, buffer, releaseCallbackId, releaseBufferCallback); + sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE; + t->setBuffer(mSurfaceControl, buffer, fence, bufferItem.mFrameNumber, releaseCallbackId, + releaseBufferCallback); t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace)); t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata); t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage); - t->setAcquireFence(mSurfaceControl, - bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE); t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this)); mSurfaceControlsWithPendingCallback.push(mSurfaceControl); @@ -486,7 +486,6 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { if (!bufferItem.mIsAutoTimestamp) { t->setDesiredPresentTime(bufferItem.mTimestamp); } - t->setFrameNumber(mSurfaceControl, bufferItem.mFrameNumber); if (!mNextFrameTimelineInfoQueue.empty()) { t->setFrameTimelineInfo(mNextFrameTimelineInfoQueue.front()); @@ -508,21 +507,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(); } @@ -724,6 +709,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: @@ -866,4 +877,9 @@ uint32_t BLASTBufferQueue::getLastTransformHint() const { } } +uint64_t BLASTBufferQueue::getLastAcquiredFrameNum() { + std::unique_lock _lock{mMutex}; + return mLastAcquiredFrameNumber; +} + } // namespace android diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp index e1b1efc0ed..6f1a7aed9c 100644 --- a/libs/gui/DisplayEventDispatcher.cpp +++ b/libs/gui/DisplayEventDispatcher.cpp @@ -153,6 +153,7 @@ bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, outVsyncEventData->id = ev.vsync.vsyncId; outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp; outVsyncEventData->frameInterval = ev.vsync.frameInterval; + outVsyncEventData->expectedPresentTime = ev.vsync.expectedVSyncTimestamp; break; case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected); 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 1726761785..3c8289fe2a 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; } } @@ -299,8 +304,11 @@ public: &reply); uint64_t rawId; SAFE_PARCEL(reply.readUint64, &rawId); - *displayId = PhysicalDisplayId(rawId); - return NO_ERROR; + if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(rawId)) { + *displayId = *id; + return NO_ERROR; + } + return NAME_NOT_FOUND; } sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override { @@ -1355,12 +1363,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); @@ -1427,9 +1438,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 54735fa133..f848e4ffde 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), @@ -51,7 +50,6 @@ layer_state_t::layer_state_t() transform(0), transformToDisplayInverse(false), crop(Rect::INVALID_RECT), - orientedDisplaySpaceRect(Rect::INVALID_RECT), dataspace(ui::Dataspace::UNKNOWN), surfaceDamageRegion(), api(-1), @@ -65,12 +63,10 @@ layer_state_t::layer_state_t() frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT), changeFrameRateStrategy(ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS), fixedTransformHint(ui::Transform::ROT_INVALID), - frameNumber(0), autoRefresh(false), isTrustedOverlay(false), bufferCrop(Rect::INVALID_RECT), destinationFrame(Rect::INVALID_RECT), - releaseBufferListener(nullptr), dropInputMode(gui::DropInputMode::NONE) { matrix.dsdx = matrix.dtdy = 1.0f; matrix.dsdy = matrix.dtdx = 0.0f; @@ -87,7 +83,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); @@ -103,21 +99,6 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.write, transparentRegion); SAFE_PARCEL(output.writeUint32, transform); SAFE_PARCEL(output.writeBool, transformToDisplayInverse); - SAFE_PARCEL(output.write, orientedDisplaySpaceRect); - - if (buffer) { - SAFE_PARCEL(output.writeBool, true); - SAFE_PARCEL(output.write, *buffer); - } else { - SAFE_PARCEL(output.writeBool, false); - } - - if (acquireFence) { - SAFE_PARCEL(output.writeBool, true); - SAFE_PARCEL(output.write, *acquireFence); - } else { - SAFE_PARCEL(output.writeBool, false); - } SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace)); SAFE_PARCEL(output.write, hdrMetadata); @@ -134,8 +115,6 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.write, colorTransform.asArray(), 16 * sizeof(float)); SAFE_PARCEL(output.writeFloat, cornerRadius); SAFE_PARCEL(output.writeUint32, backgroundBlurRadius); - SAFE_PARCEL(output.writeStrongBinder, cachedBuffer.token.promote()); - SAFE_PARCEL(output.writeUint64, cachedBuffer.id); SAFE_PARCEL(output.writeParcelable, metadata); SAFE_PARCEL(output.writeFloat, bgColorAlpha); SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(bgColorDataspace)); @@ -152,9 +131,7 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeByte, frameRateCompatibility); SAFE_PARCEL(output.writeByte, changeFrameRateStrategy); SAFE_PARCEL(output.writeUint32, fixedTransformHint); - SAFE_PARCEL(output.writeUint64, frameNumber); SAFE_PARCEL(output.writeBool, autoRefresh); - SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(releaseBufferListener)); SAFE_PARCEL(output.writeUint32, blurRegions.size()); for (auto region : blurRegions) { @@ -175,8 +152,8 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.write, destinationFrame); SAFE_PARCEL(output.writeBool, isTrustedOverlay); - SAFE_PARCEL(output.writeStrongBinder, releaseBufferEndpoint); SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dropInputMode)); + SAFE_PARCEL(bufferData.write, output); return NO_ERROR; } @@ -190,7 +167,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); @@ -216,20 +193,6 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.read, transparentRegion); SAFE_PARCEL(input.readUint32, &transform); SAFE_PARCEL(input.readBool, &transformToDisplayInverse); - SAFE_PARCEL(input.read, orientedDisplaySpaceRect); - - bool tmpBool = false; - SAFE_PARCEL(input.readBool, &tmpBool); - if (tmpBool) { - buffer = new GraphicBuffer(); - SAFE_PARCEL(input.read, *buffer); - } - - SAFE_PARCEL(input.readBool, &tmpBool); - if (tmpBool) { - acquireFence = new Fence(); - SAFE_PARCEL(input.read, *acquireFence); - } uint32_t tmpUint32 = 0; SAFE_PARCEL(input.readUint32, &tmpUint32); @@ -238,6 +201,8 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.read, hdrMetadata); SAFE_PARCEL(input.read, surfaceDamageRegion); SAFE_PARCEL(input.readInt32, &api); + + bool tmpBool = false; SAFE_PARCEL(input.readBool, &tmpBool); if (tmpBool) { sidebandStream = NativeHandle::create(input.readNativeHandle(), true); @@ -246,10 +211,6 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.read, &colorTransform, 16 * sizeof(float)); SAFE_PARCEL(input.readFloat, &cornerRadius); SAFE_PARCEL(input.readUint32, &backgroundBlurRadius); - sp<IBinder> tmpBinder; - SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder); - cachedBuffer.token = tmpBinder; - SAFE_PARCEL(input.readUint64, &cachedBuffer.id); SAFE_PARCEL(input.readParcelable, &metadata); SAFE_PARCEL(input.readFloat, &bgColorAlpha); @@ -274,15 +235,8 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readByte, &changeFrameRateStrategy); SAFE_PARCEL(input.readUint32, &tmpUint32); fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32); - SAFE_PARCEL(input.readUint64, &frameNumber); SAFE_PARCEL(input.readBool, &autoRefresh); - tmpBinder = nullptr; - SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder); - if (tmpBinder) { - releaseBufferListener = checked_interface_cast<ITransactionCompletedListener>(tmpBinder); - } - uint32_t numRegions = 0; SAFE_PARCEL(input.readUint32, &numRegions); blurRegions.clear(); @@ -306,11 +260,10 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.read, destinationFrame); SAFE_PARCEL(input.readBool, &isTrustedOverlay); - SAFE_PARCEL(input.readNullableStrongBinder, &releaseBufferEndpoint); - uint32_t mode; SAFE_PARCEL(input.readUint32, &mode); dropInputMode = static_cast<gui::DropInputMode>(mode); + SAFE_PARCEL(bufferData.read, input); return NO_ERROR; } @@ -322,21 +275,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); @@ -352,8 +298,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); @@ -468,12 +414,7 @@ void layer_state_t::merge(const layer_state_t& other) { } if (other.what & eBufferChanged) { what |= eBufferChanged; - buffer = other.buffer; - releaseBufferEndpoint = other.releaseBufferEndpoint; - } - if (other.what & eAcquireFenceChanged) { - what |= eAcquireFenceChanged; - acquireFence = other.acquireFence; + bufferData = other.bufferData; } if (other.what & eDataspaceChanged) { what |= eDataspaceChanged; @@ -506,10 +447,6 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eInputInfoChanged; windowInfoHandle = new WindowInfoHandle(*other.windowInfoHandle); } - if (other.what & eCachedBufferChanged) { - what |= eCachedBufferChanged; - cachedBuffer = other.cachedBuffer; - } if (other.what & eBackgroundColorChanged) { what |= eBackgroundColorChanged; color = other.color; @@ -538,10 +475,6 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eFixedTransformHintChanged; fixedTransformHint = other.fixedTransformHint; } - if (other.what & eFrameNumberChanged) { - what |= eFrameNumberChanged; - frameNumber = other.frameNumber; - } if (other.what & eAutoRefreshChanged) { what |= eAutoRefreshChanged; autoRefresh = other.autoRefresh; @@ -550,13 +483,6 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eTrustedOverlayChanged; isTrustedOverlay = other.isTrustedOverlay; } - if (other.what & eReleaseBufferListenerChanged) { - if (releaseBufferListener) { - ALOGW("Overriding releaseBufferListener"); - } - what |= eReleaseBufferListenerChanged; - releaseBufferListener = other.releaseBufferListener; - } if (other.what & eStretchChanged) { what |= eStretchChanged; stretchEffect = other.stretchEffect; @@ -569,23 +495,26 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eDestinationFrameChanged; destinationFrame = other.destinationFrame; } + if (other.what & eProducerDisconnect) { + what |= eProducerDisconnect; + } if (other.what & eDropInputModeChanged) { what |= eDropInputModeChanged; dropInputMode = other.dropInputMode; } 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); } } bool layer_state_t::hasBufferChanges() const { - return (what & layer_state_t::eBufferChanged) || (what & layer_state_t::eCachedBufferChanged); + return what & layer_state_t::eBufferChanged; } bool layer_state_t::hasValidBuffer() const { - return buffer || cachedBuffer.isValid(); + return bufferData.buffer || bufferData.cachedBuffer.isValid(); } status_t layer_state_t::matrix22_t::write(Parcel& output) const { @@ -746,4 +675,66 @@ status_t LayerCaptureArgs::read(const Parcel& input) { return NO_ERROR; } +status_t BufferData::write(Parcel& output) const { + SAFE_PARCEL(output.writeInt32, flags.get()); + + if (buffer) { + SAFE_PARCEL(output.writeBool, true); + SAFE_PARCEL(output.write, *buffer); + } else { + SAFE_PARCEL(output.writeBool, false); + } + + if (acquireFence) { + SAFE_PARCEL(output.writeBool, true); + SAFE_PARCEL(output.write, *acquireFence); + } else { + SAFE_PARCEL(output.writeBool, false); + } + + SAFE_PARCEL(output.writeUint64, frameNumber); + SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(releaseBufferListener)); + SAFE_PARCEL(output.writeStrongBinder, releaseBufferEndpoint); + + SAFE_PARCEL(output.writeStrongBinder, cachedBuffer.token.promote()); + SAFE_PARCEL(output.writeUint64, cachedBuffer.id); + + return NO_ERROR; +} + +status_t BufferData::read(const Parcel& input) { + int32_t tmpInt32; + SAFE_PARCEL(input.readInt32, &tmpInt32); + flags = Flags<BufferDataChange>(tmpInt32); + + bool tmpBool = false; + SAFE_PARCEL(input.readBool, &tmpBool); + if (tmpBool) { + buffer = new GraphicBuffer(); + SAFE_PARCEL(input.read, *buffer); + } + + SAFE_PARCEL(input.readBool, &tmpBool); + if (tmpBool) { + acquireFence = new Fence(); + SAFE_PARCEL(input.read, *acquireFence); + } + + SAFE_PARCEL(input.readUint64, &frameNumber); + + sp<IBinder> tmpBinder = nullptr; + SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder); + if (tmpBinder) { + releaseBufferListener = checked_interface_cast<ITransactionCompletedListener>(tmpBinder); + } + SAFE_PARCEL(input.readNullableStrongBinder, &releaseBufferEndpoint); + + tmpBinder = nullptr; + SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder); + cachedBuffer.token = tmpBinder; + SAFE_PARCEL(input.readUint64, &cachedBuffer.id); + + return NO_ERROR; +} + }; // namespace android diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 922bceb191..aca59b6d3b 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -805,7 +805,7 @@ void SurfaceComposerClient::Transaction::cacheBuffers() { layer_state_t* s = &(mComposerStates[handle].state); if (!(s->what & layer_state_t::eBufferChanged)) { continue; - } else if (s->what & layer_state_t::eCachedBufferChanged) { + } else if (s->bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged)) { // If eBufferChanged and eCachedBufferChanged are both trued then that means // we already cached the buffer in a previous call to cacheBuffers, perhaps // from writeToParcel on a Transaction that was merged in to this one. @@ -814,23 +814,22 @@ void SurfaceComposerClient::Transaction::cacheBuffers() { // Don't try to cache a null buffer. Sending null buffers is cheap so we shouldn't waste // time trying to cache them. - if (!s->buffer) { + if (!s->bufferData.buffer) { continue; } uint64_t cacheId = 0; - status_t ret = BufferCache::getInstance().getCacheId(s->buffer, &cacheId); + status_t ret = BufferCache::getInstance().getCacheId(s->bufferData.buffer, &cacheId); if (ret == NO_ERROR) { // Cache-hit. Strip the buffer and send only the id. - s->what &= ~static_cast<uint64_t>(layer_state_t::eBufferChanged); - s->buffer = nullptr; + s->bufferData.buffer = nullptr; } else { // Cache-miss. Include the buffer and send the new cacheId. - cacheId = BufferCache::getInstance().cache(s->buffer); + cacheId = BufferCache::getInstance().cache(s->bufferData.buffer); } - s->what |= layer_state_t::eCachedBufferChanged; - s->cachedBuffer.token = BufferCache::getInstance().getToken(); - s->cachedBuffer.id = cacheId; + s->bufferData.flags |= BufferData::BufferDataChange::cachedBufferChanged; + s->bufferData.cachedBuffer.token = BufferCache::getInstance().getToken(); + s->bufferData.cachedBuffer.id = cacheId; // If we have more buffers than the size of the cache, we should stop caching so we don't // evict other buffers in this transaction @@ -1112,7 +1111,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; @@ -1289,22 +1288,33 @@ SurfaceComposerClient::Transaction::setTransformToDisplayInverse(const sp<Surfac } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer( - const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer, const ReleaseCallbackId& id, - ReleaseBufferCallback callback) { + const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer, + const std::optional<sp<Fence>>& fence, const std::optional<uint64_t>& frameNumber, + const ReleaseCallbackId& id, ReleaseBufferCallback callback) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } removeReleaseBufferCallback(s); - s->what |= layer_state_t::eBufferChanged; - s->buffer = buffer; - s->releaseBufferEndpoint = IInterface::asBinder(TransactionCompletedListener::getIInstance()); + BufferData bufferData; + bufferData.buffer = buffer; + if (frameNumber) { + bufferData.frameNumber = *frameNumber; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + } + if (fence) { + bufferData.acquireFence = *fence; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + } + bufferData.releaseBufferEndpoint = + IInterface::asBinder(TransactionCompletedListener::getIInstance()); if (mIsAutoTimestamp) { mDesiredPresentTime = systemTime(); } - setReleaseBufferCallback(s, id, callback); - + setReleaseBufferCallback(&bufferData, id, callback); + s->what |= layer_state_t::eBufferChanged; + s->bufferData = bufferData; registerSurfaceControlForCallback(sc); mContainsBuffer = true; @@ -1312,51 +1322,33 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe } void SurfaceComposerClient::Transaction::removeReleaseBufferCallback(layer_state_t* s) { - if (!s->releaseBufferListener) { + if (!(s->what & layer_state_t::eBufferChanged)) { return; } - s->what &= ~static_cast<uint64_t>(layer_state_t::eReleaseBufferListenerChanged); - s->releaseBufferListener = nullptr; auto listener = TransactionCompletedListener::getInstance(); - listener->removeReleaseBufferCallback(s->releaseCallbackId); - s->releaseCallbackId = ReleaseCallbackId::INVALID_ID; + listener->removeReleaseBufferCallback(s->bufferData.releaseCallbackId); } -void SurfaceComposerClient::Transaction::setReleaseBufferCallback(layer_state_t* s, +void SurfaceComposerClient::Transaction::setReleaseBufferCallback(BufferData* bufferData, const ReleaseCallbackId& id, ReleaseBufferCallback callback) { if (!callback) { return; } - if (!s->buffer) { + if (!bufferData->buffer) { ALOGW("Transaction::setReleaseBufferCallback" "ignored trying to set a callback on a null buffer."); return; } - s->what |= layer_state_t::eReleaseBufferListenerChanged; - s->releaseBufferListener = TransactionCompletedListener::getIInstance(); - s->releaseCallbackId = id; + bufferData->releaseBufferListener = TransactionCompletedListener::getIInstance(); + bufferData->releaseCallbackId = id; auto listener = TransactionCompletedListener::getInstance(); listener->setReleaseBufferCallback(id, callback); } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAcquireFence( - const sp<SurfaceControl>& sc, const sp<Fence>& fence) { - layer_state_t* s = getLayerState(sc); - if (!s) { - mStatus = BAD_INDEX; - return *this; - } - s->what |= layer_state_t::eAcquireFenceChanged; - s->acquireFence = fence; - - registerSurfaceControlForCallback(sc); - return *this; -} - SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDataspace( const sp<SurfaceControl>& sc, ui::Dataspace dataspace) { layer_state_t* s = getLayerState(sc); @@ -1506,20 +1498,6 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyPr return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameNumber( - const sp<SurfaceControl>& sc, uint64_t frameNumber) { - layer_state_t* s = getLayerState(sc); - if (!s) { - mStatus = BAD_INDEX; - return *this; - } - - s->what |= layer_state_t::eFrameNumberChanged; - s->frameNumber = frameNumber; - - return *this; -} - SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo( const sp<SurfaceControl>& sc, const WindowInfo& info) { layer_state_t* s = getLayerState(sc); @@ -1792,7 +1770,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; @@ -2216,12 +2194,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/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h index 4ade240dcf..f3bd139e8c 100644 --- a/libs/gui/include/gui/DisplayEventDispatcher.h +++ b/libs/gui/include/gui/DisplayEventDispatcher.h @@ -33,6 +33,9 @@ struct VsyncEventData { // The current frame interval in ns when this frame was scheduled. int64_t frameInterval = 0; + + // The anticipated Vsync present time. + int64_t expectedPresentTime = 0; }; class DisplayEventDispatcher : public LooperCallback { 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 cd289cb401..408497dd36 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. */ @@ -241,24 +246,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 03e4aacdbe..b01eed4d3c 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -36,6 +36,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> @@ -57,6 +58,44 @@ struct client_cache_t { bool isValid() const { return token != nullptr; } }; +struct BufferData { + enum class BufferDataChange : uint32_t { + fenceChanged = 0x01, + frameNumberChanged = 0x02, + cachedBufferChanged = 0x04, + }; + + sp<GraphicBuffer> buffer; + sp<Fence> acquireFence; + + // Used by BlastBufferQueue to forward the framenumber generated by the + // graphics producer. + uint64_t frameNumber = 0; + + // Listens to when the buffer is safe to be released. This is used for blast + // layers only. The callback includes a release fence as well as the graphic + // buffer id to identify the buffer. + sp<ITransactionCompletedListener> releaseBufferListener = nullptr; + + // Keeps track of the release callback id associated with the listener. This + // is not sent to the server since the id can be reconstructed there. This + // is used to remove the old callback from the client process map if it is + // overwritten by another setBuffer call. + ReleaseCallbackId releaseCallbackId = ReleaseCallbackId::INVALID_ID; + + // Stores which endpoint the release information should be sent to. We don't want to send the + // releaseCallbackId and release fence to all listeners so we store which listener the setBuffer + // was called with. + sp<IBinder> releaseBufferEndpoint; + + Flags<BufferDataChange> flags; + + client_cache_t cachedBuffer; + + status_t write(Parcel& output) const; + status_t read(const Parcel& input); +}; + /* * Used to communicate layer information between SurfaceFlinger and its clients. */ @@ -81,7 +120,7 @@ struct layer_state_t { eTransparentRegionChanged = 0x00000020, eFlagsChanged = 0x00000040, eLayerStackChanged = 0x00000080, - eReleaseBufferListenerChanged = 0x00000400, + /* unused 0x00000400, */ eShadowRadiusChanged = 0x00000800, eLayerCreated = 0x00001000, eBufferCropChanged = 0x00002000, @@ -93,7 +132,7 @@ struct layer_state_t { eTransformToDisplayInverseChanged = 0x00080000, eCropChanged = 0x00100000, eBufferChanged = 0x00200000, - eAcquireFenceChanged = 0x00400000, + /* unused 0x00400000, */ eDataspaceChanged = 0x00800000, eHdrMetadataChanged = 0x01000000, eSurfaceDamageRegionChanged = 0x02000000, @@ -104,7 +143,7 @@ struct layer_state_t { eInputInfoChanged = 0x40000000, eCornerRadiusChanged = 0x80000000, eDestinationFrameChanged = 0x1'00000000, - eCachedBufferChanged = 0x2'00000000, + /* unused = 0x2'00000000, */ eBackgroundColorChanged = 0x4'00000000, eMetadataChanged = 0x8'00000000, eColorSpaceAgnosticChanged = 0x10'00000000, @@ -113,7 +152,7 @@ struct layer_state_t { eBackgroundBlurRadiusChanged = 0x80'00000000, eProducerDisconnect = 0x100'00000000, eFixedTransformHintChanged = 0x200'00000000, - eFrameNumberChanged = 0x400'00000000, + /* unused 0x400'00000000, */ eBlurRegionsChanged = 0x800'00000000, eAutoRefreshChanged = 0x1000'00000000, eStretchChanged = 0x2000'00000000, @@ -145,7 +184,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; @@ -167,9 +206,7 @@ struct layer_state_t { uint32_t transform; bool transformToDisplayInverse; Rect crop; - Rect orientedDisplaySpaceRect; - sp<GraphicBuffer> buffer; - sp<Fence> acquireFence; + BufferData bufferData; ui::Dataspace dataspace; HdrMetadata hdrMetadata; Region surfaceDamageRegion; @@ -180,8 +217,6 @@ struct layer_state_t { sp<gui::WindowInfoHandle> windowInfoHandle = new gui::WindowInfoHandle(); - client_cache_t cachedBuffer; - LayerMetadata metadata; // The following refer to the alpha, and dataspace, respectively of @@ -215,10 +250,6 @@ struct layer_state_t { // otherwise the value will be a valid ui::Rotation. ui::Transform::RotationFlags fixedTransformHint; - // Used by BlastBufferQueue to forward the framenumber generated by the - // graphics producer. - uint64_t frameNumber; - // Indicates that the consumer should acquire the next frame as soon as it // can and not wait for a frame to become available. This is only relevant // in shared buffer mode. @@ -234,22 +265,6 @@ struct layer_state_t { Rect bufferCrop; Rect destinationFrame; - // Listens to when the buffer is safe to be released. This is used for blast - // layers only. The callback includes a release fence as well as the graphic - // buffer id to identify the buffer. - sp<ITransactionCompletedListener> releaseBufferListener; - - // Keeps track of the release callback id associated with the listener. This - // is not sent to the server since the id can be reconstructed there. This - // is used to remove the old callback from the client process map if it is - // overwritten by another setBuffer call. - ReleaseCallbackId releaseCallbackId; - - // Stores which endpoint the release information should be sent to. We don't want to send the - // releaseCallbackId and release fence to all listeners so we store which listener the setBuffer - // was called with. - sp<IBinder> releaseBufferEndpoint; - // Force inputflinger to drop all input events for the layer and its children. gui::DropInputMode dropInputMode; }; @@ -272,11 +287,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. // @@ -290,10 +306,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 9af9e64062..82249a32f9 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -399,8 +399,7 @@ public: void cacheBuffers(); void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc); - void setReleaseBufferCallback(layer_state_t*, const ReleaseCallbackId&, - ReleaseBufferCallback); + void setReleaseBufferCallback(BufferData*, const ReleaseCallbackId&, ReleaseBufferCallback); void removeReleaseBufferCallback(layer_state_t*); public: @@ -457,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. @@ -473,10 +472,10 @@ public: Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc, bool transformToDisplayInverse); Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer, + const std::optional<sp<Fence>>& fence = std::nullopt, + const std::optional<uint64_t>& frameNumber = std::nullopt, const ReleaseCallbackId& id = ReleaseCallbackId::INVALID_ID, ReleaseBufferCallback callback = nullptr); - Transaction& setCachedBuffer(const sp<SurfaceControl>& sc, int32_t bufferId); - Transaction& setAcquireFence(const sp<SurfaceControl>& sc, const sp<Fence>& fence); Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace); Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata); Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc, @@ -501,8 +500,6 @@ public: // ONLY FOR BLAST ADAPTER Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc); - // Set the framenumber generated by the graphics producer to mimic BufferQueue behaviour. - Transaction& setFrameNumber(const sp<SurfaceControl>& sc, uint64_t frameNumber); Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const gui::WindowInfo& info); Transaction& setFocusedWindow(const gui::FocusRequest& request); @@ -568,7 +565,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); @@ -640,12 +637,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 4727740ab5..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,40 +88,50 @@ 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 { + enum class Feature : uint32_t { DISABLE_TOUCH_PAD_GESTURES = 1u << 0, NO_INPUT_CHANNEL = 1u << 1, DISABLE_USER_ACTIVITY = 1u << 2, @@ -170,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. @@ -267,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 99777e94c6..b474086d1d 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) @@ -810,7 +810,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/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 78a8b429c1..16208a7773 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -220,7 +220,7 @@ public: t.apply(true); } - void requestFocus() { + void requestFocus(int displayId = ADISPLAY_ID_DEFAULT) { SurfaceComposerClient::Transaction t; FocusRequest request; request.token = mInputInfo.token; @@ -228,7 +228,7 @@ public: request.focusedToken = nullptr; request.focusedWindowName = ""; request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); - request.displayId = 0; + request.displayId = displayId; t.setFocusedWindow(request); t.apply(true); } @@ -255,11 +255,6 @@ private: mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height)); - // TODO: Fill in from SF? - mInputInfo.ownerPid = 11111; - mInputInfo.ownerUid = 11111; - mInputInfo.displayId = 0; - InputApplicationInfo aInfo; aInfo.token = new BBinder(); aInfo.name = "Test app info"; @@ -373,23 +368,33 @@ public: int32_t mBufferPostDelay; }; -void injectTap(int x, int y) { - char *buf1, *buf2; +void injectTapOnDisplay(int x, int y, int displayId) { + char *buf1, *buf2, *bufDisplayId; asprintf(&buf1, "%d", x); asprintf(&buf2, "%d", y); + asprintf(&bufDisplayId, "%d", displayId); if (fork() == 0) { - execlp("input", "input", "tap", buf1, buf2, NULL); + execlp("input", "input", "-d", bufDisplayId, "tap", buf1, buf2, NULL); } } -void injectKey(uint32_t keycode) { - char *buf1; +void injectTap(int x, int y) { + injectTapOnDisplay(x, y, ADISPLAY_ID_DEFAULT); +} + +void injectKeyOnDisplay(uint32_t keycode, int displayId) { + char *buf1, *bufDisplayId; asprintf(&buf1, "%d", keycode); + asprintf(&bufDisplayId, "%d", displayId); if (fork() == 0) { - execlp("input", "input", "keyevent", buf1, NULL); + execlp("input", "input", "-d", bufDisplayId, "keyevent", buf1, NULL); } } +void injectKey(uint32_t keycode) { + injectKeyOnDisplay(keycode, ADISPLAY_ID_NONE); +} + TEST_F(InputSurfacesTest, can_receive_input) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(100, 100); @@ -946,4 +951,74 @@ TEST_F(InputSurfacesTest, drop_input_policy) { injectKey(AKEYCODE_V); EXPECT_EQ(surface->consumeEvent(100), nullptr); } + +class MultiDisplayTests : public InputSurfacesTest { +public: + MultiDisplayTests() : InputSurfacesTest() { ProcessState::self()->startThreadPool(); } + void TearDown() { + if (mVirtualDisplay) { + SurfaceComposerClient::destroyDisplay(mVirtualDisplay); + } + InputSurfacesTest::TearDown(); + } + + void createDisplay(int32_t width, int32_t height, bool isSecure, ui::LayerStack layerStack) { + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&mProducer, &consumer); + consumer->setConsumerName(String8("Virtual disp consumer")); + consumer->setDefaultBufferSize(width, height); + + mVirtualDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), isSecure); + SurfaceComposerClient::Transaction t; + t.setDisplaySurface(mVirtualDisplay, mProducer); + t.setDisplayFlags(mVirtualDisplay, 0x01 /* DisplayDevice::eReceivesInput */); + t.setDisplayLayerStack(mVirtualDisplay, layerStack); + t.apply(true); + } + + sp<IBinder> mVirtualDisplay; + sp<IGraphicBufferProducer> mProducer; +}; + +TEST_F(MultiDisplayTests, drop_input_for_secure_layer_on_nonsecure_display) { + ui::LayerStack layerStack = ui::LayerStack::fromValue(42); + createDisplay(1000, 1000, false /*isSecure*/, layerStack); + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->doTransaction([&](auto &t, auto &sc) { + t.setFlags(sc, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure); + t.setLayerStack(sc, layerStack); + }); + surface->showAt(100, 100); + + injectTap(101, 101); + + EXPECT_EQ(surface->consumeEvent(100), nullptr); + + surface->requestFocus(layerStack.id); + surface->assertFocusChange(true); + injectKeyOnDisplay(AKEYCODE_V, layerStack.id); + EXPECT_EQ(surface->consumeEvent(100), nullptr); +} + +TEST_F(MultiDisplayTests, dont_drop_input_for_secure_layer_on_secure_display) { + ui::LayerStack layerStack = ui::LayerStack::fromValue(42); + createDisplay(1000, 1000, true /*isSecure*/, layerStack); + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->doTransaction([&](auto &t, auto &sc) { + t.setFlags(sc, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure); + t.setLayerStack(sc, layerStack); + }); + surface->showAt(100, 100); + + injectTapOnDisplay(101, 101, layerStack.id); + EXPECT_NE(surface->consumeEvent(), nullptr); + EXPECT_NE(surface->consumeEvent(), nullptr); + + surface->requestFocus(layerStack.id); + surface->assertFocusChange(true); + injectKeyOnDisplay(AKEYCODE_V, layerStack.id); + + surface->expectKey(AKEYCODE_V); +} + } // namespace android::test diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index c745505038..a9f4d09838 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -753,21 +753,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..390ff965ec 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -20,10 +20,8 @@ #include <attestation/HmacKeyManager.h> #include <cutils/compiler.h> #include <inttypes.h> -#include <limits.h> #include <string.h> -#include <android-base/properties.h> #include <android-base/stringprintf.h> #include <gui/constants.h> #include <input/Input.h> @@ -43,15 +41,6 @@ namespace android { namespace { -// 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. -bool isPerWindowInputRotationEnabled() { - static const bool PER_WINDOW_INPUT_ROTATION = - base::GetBoolProperty("persist.debug.per_window_input_rotation", false); - - return PER_WINDOW_INPUT_ROTATION; -} - float transformAngle(const ui::Transform& transform, float angleRadians) { // Construct and transform a vector oriented at the specified clockwise angle from vertical. // Coordinate system: down is increasing Y, right is increasing X. @@ -76,36 +65,13 @@ float transformAngle(const ui::Transform& transform, float angleRadians) { return result; } -// Rotates the given point to the specified orientation. If the display width and height are -// provided, the point is rotated in the screen space. Otherwise, the point is rotated about the -// origin. This helper is used to avoid the extra overhead of creating new Transforms. -vec2 rotatePoint(uint32_t orientation, float x, float y, int32_t displayWidth = 0, - int32_t displayHeight = 0) { - if (orientation == ui::Transform::ROT_0) { - return {x, y}; - } - - vec2 xy(x, y); - if (orientation == ui::Transform::ROT_90) { - xy.x = displayHeight - y; - xy.y = x; - } else if (orientation == ui::Transform::ROT_180) { - xy.x = displayWidth - x; - xy.y = displayHeight - y; - } else if (orientation == ui::Transform::ROT_270) { - xy.x = y; - xy.y = displayWidth - x; - } - return xy; -} - -vec2 applyTransformWithoutTranslation(const ui::Transform& transform, float x, float y) { - const vec2 transformedXy = transform.transform(x, y); +vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) { + const vec2 transformedXy = transform.transform(xy); const vec2 transformedOrigin = transform.transform(0, 0); return transformedXy - transformedOrigin; } -bool shouldDisregardWindowTranslation(uint32_t source) { +bool shouldDisregardTranslation(uint32_t source) { // Pointer events are the only type of events that refer to absolute coordinates on the display, // so we should apply the entire window transform. For other types of events, we should make // sure to not apply the window translation/offset. @@ -170,6 +136,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 +299,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); @@ -432,8 +397,7 @@ void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int3 int32_t buttonState, MotionClassification classification, const ui::Transform& transform, float xPrecision, float yPrecision, float rawXCursorPosition, float rawYCursorPosition, - uint32_t displayOrientation, int32_t displayWidth, - int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, + const ui::Transform& rawTransform, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { InputEvent::initialize(id, deviceId, source, displayId, hmac); @@ -449,9 +413,7 @@ void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int3 mYPrecision = yPrecision; mRawXCursorPosition = rawXCursorPosition; mRawYCursorPosition = rawYCursorPosition; - mDisplayOrientation = displayOrientation; - mDisplayWidth = displayWidth; - mDisplayHeight = displayHeight; + mRawTransform = rawTransform; mDownTime = downTime; mPointerProperties.clear(); mPointerProperties.appendArray(pointerProperties, pointerCount); @@ -475,9 +437,7 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mYPrecision = other->mYPrecision; mRawXCursorPosition = other->mRawXCursorPosition; mRawYCursorPosition = other->mRawYCursorPosition; - mDisplayOrientation = other->mDisplayOrientation; - mDisplayWidth = other->mDisplayWidth; - mDisplayHeight = other->mDisplayHeight; + mRawTransform = other->mRawTransform; mDownTime = other->mDownTime; mPointerProperties = other->mPointerProperties; @@ -540,25 +500,17 @@ float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const { const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex); - if (!isPerWindowInputRotationEnabled()) return coords->getAxisValue(axis); - if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) { - // For compatibility, convert raw coordinates into "oriented screen space". Once app - // developers are educated about getRaw, we can consider removing this. - const vec2 xy = shouldDisregardWindowTranslation(mSource) - ? rotatePoint(mDisplayOrientation, coords->getX(), coords->getY()) - : rotatePoint(mDisplayOrientation, coords->getX(), coords->getY(), mDisplayWidth, - mDisplayHeight); + const vec2 xy = calculateTransformedXY(mSource, mRawTransform, coords->getXYValue()); static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); return xy[axis]; } if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) { - // For compatibility, since we convert raw coordinates into "oriented screen space", we - // need to convert the relative axes into the same orientation for consistency. - const vec2 relativeXy = rotatePoint(mDisplayOrientation, - coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), - coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)); + const vec2 relativeXy = + transformWithoutTranslation(mRawTransform, + {coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), + coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)}); return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y; } @@ -570,20 +522,16 @@ float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex); if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) { - const vec2 xy = shouldDisregardWindowTranslation(mSource) - ? applyTransformWithoutTranslation(mTransform, coords->getX(), coords->getY()) - : mTransform.transform(coords->getXYValue()); + const vec2 xy = calculateTransformedXY(mSource, mTransform, coords->getXYValue()); static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); return xy[axis]; } if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) { const vec2 relativeXy = - applyTransformWithoutTranslation(mTransform, - coords->getAxisValue( - AMOTION_EVENT_AXIS_RELATIVE_X), - coords->getAxisValue( - AMOTION_EVENT_AXIS_RELATIVE_Y)); + transformWithoutTranslation(mTransform, + {coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), + coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)}); return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y; } @@ -608,6 +556,8 @@ void MotionEvent::offsetLocation(float xOffset, float yOffset) { void MotionEvent::scale(float globalScaleFactor) { mTransform.set(mTransform.tx() * globalScaleFactor, mTransform.ty() * globalScaleFactor); + mRawTransform.set(mRawTransform.tx() * globalScaleFactor, + mRawTransform.ty() * globalScaleFactor); mXPrecision *= globalScaleFactor; mYPrecision *= globalScaleFactor; @@ -709,9 +659,11 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mYPrecision = parcel->readFloat(); mRawXCursorPosition = parcel->readFloat(); mRawYCursorPosition = parcel->readFloat(); - mDisplayOrientation = parcel->readUint32(); - mDisplayWidth = parcel->readInt32(); - mDisplayHeight = parcel->readInt32(); + + result = android::readFromParcel(mRawTransform, *parcel); + if (result != OK) { + return result; + } mDownTime = parcel->readInt64(); mPointerProperties.clear(); @@ -771,9 +723,11 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeFloat(mYPrecision); parcel->writeFloat(mRawXCursorPosition); parcel->writeFloat(mRawYCursorPosition); - parcel->writeUint32(mDisplayOrientation); - parcel->writeInt32(mDisplayWidth); - parcel->writeInt32(mDisplayHeight); + + result = android::writeToParcel(mRawTransform, *parcel); + if (result != OK) { + return result; + } parcel->writeInt64(mDownTime); for (size_t i = 0; i < pointerCount; i++) { @@ -835,9 +789,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: @@ -854,6 +808,12 @@ std::string MotionEvent::actionToString(int32_t action) { return android::base::StringPrintf("%" PRId32, action); } +vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& transform, + const vec2& xy) { + return shouldDisregardTranslation(source) ? transformWithoutTranslation(transform, xy) + : transform.transform(xy); +} + // --- FocusEvent --- void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) { @@ -899,6 +859,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 +926,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: @@ -986,6 +968,13 @@ void PooledInputEventFactory::recycle(InputEvent* event) { return; } break; + case AINPUT_EVENT_TYPE_TOUCH_MODE: + if (mTouchModeEventPool.size() < mMaxPoolSize) { + mTouchModeEventPool.push( + std::unique_ptr<TouchModeEvent>(static_cast<TouchModeEvent*>(event))); + return; + } + break; } delete event; } 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..02a5a0807b 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); } @@ -200,6 +203,8 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { case InputMessage::Type::MOTION: { // int32_t eventId msg->body.motion.eventId = body.motion.eventId; + // uint32_t pointerCount + msg->body.motion.pointerCount = body.motion.pointerCount; // nsecs_t eventTime msg->body.motion.eventTime = body.motion.eventTime; // int32_t deviceId @@ -242,14 +247,14 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.motion.xCursorPosition = body.motion.xCursorPosition; // float yCursorPosition msg->body.motion.yCursorPosition = body.motion.yCursorPosition; - // uint32_t displayOrientation - msg->body.motion.displayOrientation = body.motion.displayOrientation; - // int32_t displayWidth - msg->body.motion.displayWidth = body.motion.displayWidth; - // int32_t displayHeight - msg->body.motion.displayHeight = body.motion.displayHeight; - // uint32_t pointerCount - msg->body.motion.pointerCount = body.motion.pointerCount; + + msg->body.motion.dsdxRaw = body.motion.dsdxRaw; + msg->body.motion.dtdxRaw = body.motion.dtdxRaw; + msg->body.motion.dtdyRaw = body.motion.dtdyRaw; + msg->body.motion.dsdyRaw = body.motion.dsdyRaw; + msg->body.motion.txRaw = body.motion.txRaw; + msg->body.motion.tyRaw = body.motion.tyRaw; + //struct Pointer pointers[MAX_POINTERS] for (size_t i = 0; i < body.motion.pointerCount; i++) { // PointerProperties properties @@ -293,6 +298,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; + } } } @@ -535,8 +544,8 @@ status_t InputPublisher::publishMotionEvent( std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, const ui::Transform& transform, float xPrecision, - float yPrecision, float xCursorPosition, float yCursorPosition, uint32_t displayOrientation, - int32_t displayWidth, int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, + float yPrecision, float xCursorPosition, float yCursorPosition, + const ui::Transform& rawTransform, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { if (ATRACE_ENABLED()) { @@ -596,9 +605,12 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.yPrecision = yPrecision; msg.body.motion.xCursorPosition = xCursorPosition; msg.body.motion.yCursorPosition = yCursorPosition; - msg.body.motion.displayOrientation = displayOrientation; - msg.body.motion.displayWidth = displayWidth; - msg.body.motion.displayHeight = displayHeight; + msg.body.motion.dsdxRaw = rawTransform.dsdx(); + msg.body.motion.dtdxRaw = rawTransform.dtdx(); + msg.body.motion.dtdyRaw = rawTransform.dtdy(); + msg.body.motion.dsdyRaw = rawTransform.dsdy(); + msg.body.motion.txRaw = rawTransform.tx(); + msg.body.motion.tyRaw = rawTransform.ty(); msg.body.motion.downTime = downTime; msg.body.motion.eventTime = eventTime; msg.body.motion.pointerCount = pointerCount; @@ -665,6 +677,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 +719,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 +861,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 +894,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; @@ -1358,6 +1396,10 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage ui::Transform transform; transform.set({msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx, msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1}); + ui::Transform displayTransform; + displayTransform.set({msg->body.motion.dsdxRaw, msg->body.motion.dtdxRaw, + msg->body.motion.txRaw, msg->body.motion.dtdyRaw, + msg->body.motion.dsdyRaw, msg->body.motion.tyRaw, 0, 0, 1}); event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source, msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action, msg->body.motion.actionButton, msg->body.motion.flags, @@ -1365,9 +1407,12 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage msg->body.motion.buttonState, msg->body.motion.classification, transform, msg->body.motion.xPrecision, msg->body.motion.yPrecision, msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, - msg->body.motion.displayOrientation, msg->body.motion.displayWidth, - msg->body.motion.displayHeight, msg->body.motion.downTime, - msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); + displayTransform, msg->body.motion.downTime, 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) { @@ -1412,14 +1457,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 +1521,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/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index b1ef7534e4..caf3a611e2 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -226,39 +226,23 @@ protected: static constexpr float Y_SCALE = 3.0; static constexpr float X_OFFSET = 1; static constexpr float Y_OFFSET = 1.1; - - static const std::optional<bool> INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE; + static constexpr float RAW_X_SCALE = 4.0; + static constexpr float RAW_Y_SCALE = -5.0; + static constexpr float RAW_X_OFFSET = 12; + static constexpr float RAW_Y_OFFSET = -41.1; int32_t mId; ui::Transform mTransform; - - void SetUp() override; - void TearDown() override; + ui::Transform mRawTransform; void initializeEventWithHistory(MotionEvent* event); void assertEqualsEventWithHistory(const MotionEvent* event); }; -const std::optional<bool> MotionEventTest::INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE = - !base::GetProperty("persist.debug.per_window_input_rotation", "").empty() - ? std::optional(base::GetBoolProperty("persist.debug.per_window_input_rotation", false)) - : std::nullopt; - -void MotionEventTest::SetUp() { - // Ensure per_window_input_rotation is enabled. - base::SetProperty("persist.debug.per_window_input_rotation", "true"); -} - -void MotionEventTest::TearDown() { - const auto val = INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE.has_value() - ? (*INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE ? "true" : "false") - : ""; - base::SetProperty("persist.debug.per_window_input_rotation", val); -} - void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { mId = InputEvent::nextId(); mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1}); + mRawTransform.set({RAW_X_SCALE, 0, RAW_X_OFFSET, 0, RAW_Y_SCALE, RAW_Y_OFFSET, 0, 0, 1}); PointerProperties pointerProperties[2]; pointerProperties[0].clear(); @@ -294,9 +278,8 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, mTransform, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, - ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties, - pointerCoords); + mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, + pointerProperties, pointerCoords); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); @@ -373,39 +356,37 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1)); ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime()); - ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(211, event->getRawPointerCoords(0)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(221, event->getRawPointerCoords(1)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - - ASSERT_EQ(11, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0)); - ASSERT_EQ(21, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0)); - ASSERT_EQ(111, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1)); - ASSERT_EQ(121, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1)); - ASSERT_EQ(211, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0)); - ASSERT_EQ(221, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1)); - - ASSERT_EQ(10, event->getHistoricalRawX(0, 0)); - ASSERT_EQ(20, event->getHistoricalRawX(1, 0)); - ASSERT_EQ(110, event->getHistoricalRawX(0, 1)); - ASSERT_EQ(120, event->getHistoricalRawX(1, 1)); - ASSERT_EQ(210, event->getRawX(0)); - ASSERT_EQ(220, event->getRawX(1)); - - ASSERT_EQ(11, event->getHistoricalRawY(0, 0)); - ASSERT_EQ(21, event->getHistoricalRawY(1, 0)); - ASSERT_EQ(111, event->getHistoricalRawY(0, 1)); - ASSERT_EQ(121, event->getHistoricalRawY(1, 1)); - ASSERT_EQ(211, event->getRawY(0)); - ASSERT_EQ(221, event->getRawY(1)); + ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y)); + + ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, + event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0)); + ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, + event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0)); + ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, + event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1)); + ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, + event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1)); + ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0)); + ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1)); + + ASSERT_EQ(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0)); + ASSERT_EQ(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0)); + ASSERT_EQ(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1)); + ASSERT_EQ(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1)); + ASSERT_EQ(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0)); + ASSERT_EQ(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1)); + + ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0)); + ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0)); + ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1)); + ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1)); + ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0)); + ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1)); ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0)); ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0)); @@ -543,8 +524,8 @@ TEST_F(MotionEventTest, Scale) { ASSERT_EQ(X_OFFSET * 2, event.getXOffset()); ASSERT_EQ(Y_OFFSET * 2, event.getYOffset()); - ASSERT_EQ(210 * 2, event.getRawX(0)); - ASSERT_EQ(211 * 2, event.getRawY(0)); + ASSERT_EQ((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0)); + ASSERT_EQ((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0)); ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0)); ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0)); ASSERT_EQ(212, event.getPressure(0)); @@ -592,10 +573,10 @@ TEST_F(MotionEventTest, Transform) { // The geometrical representation is irrelevant to the test, it's just easy to generate // and check rotation. We set the orientation to the same angle. // Coordinate system: down is increasing Y, right is increasing X. - const float PI_180 = float(M_PI / 180); - const float RADIUS = 10; - const float ARC = 36; - const float ROTATION = ARC * 2; + static constexpr float PI_180 = float(M_PI / 180); + static constexpr float RADIUS = 10; + static constexpr float ARC = 36; + static constexpr float ROTATION = ARC * 2; const size_t pointerCount = 11; PointerProperties pointerProperties[pointerCount]; @@ -616,9 +597,8 @@ TEST_F(MotionEventTest, Transform) { AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, identityTransform, 0 /*xPrecision*/, 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, - ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, - 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, - pointerCoords); + identityTransform, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, + pointerProperties, pointerCoords); float originalRawX = 0 + 3; float originalRawY = -RADIUS + 2; @@ -661,7 +641,7 @@ TEST_F(MotionEventTest, Transform) { MotionEvent createTouchDownEvent(float x, float y, float dx, float dy, const ui::Transform& transform, - uint32_t displayOrientation = ui::Transform::ROT_0) { + const ui::Transform& rawTransform) { std::vector<PointerProperties> pointerProperties; pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER}); std::vector<PointerCoords> pointerCoords; @@ -677,19 +657,18 @@ MotionEvent createTouchDownEvent(float x, float y, float dx, float dy, /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, transform, /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, displayOrientation, - /* displayWidth */ 400, - /* displayHeight */ 800, eventTime, eventTime, pointerCoords.size(), - pointerProperties.data(), pointerCoords.data()); + AMOTION_EVENT_INVALID_CURSOR_POSITION, rawTransform, eventTime, eventTime, + pointerCoords.size(), pointerProperties.data(), pointerCoords.data()); return event; } TEST_F(MotionEventTest, ApplyTransform) { // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). ui::Transform identity; - ui::Transform xform(ui::Transform::ROT_90, 800, 400); - xform.set(xform.tx() + 20, xform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90); + ui::Transform transform(ui::Transform::ROT_90, 800, 400); + transform.set(transform.tx() + 20, transform.ty() + 40); + ui::Transform rawTransform(ui::Transform::ROT_90, 800, 400); + MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, rawTransform); ASSERT_EQ(700, event.getRawX(0)); ASSERT_EQ(60, event.getRawY(0)); ASSERT_NE(event.getRawX(0), event.getX(0)); @@ -698,10 +677,10 @@ TEST_F(MotionEventTest, ApplyTransform) { ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); - MotionEvent changedEvent = createTouchDownEvent(60, 100, 42, 96, identity); - const std::array<float, 9> rowMajor{xform[0][0], xform[1][0], xform[2][0], - xform[0][1], xform[1][1], xform[2][1], - xform[0][2], xform[1][2], xform[2][2]}; + MotionEvent changedEvent = createTouchDownEvent(60, 100, 42, 96, identity, identity); + const std::array<float, 9> rowMajor{transform[0][0], transform[1][0], transform[2][0], + transform[0][1], transform[1][1], transform[2][1], + transform[0][2], transform[1][2], transform[2][2]}; changedEvent.applyTransform(rowMajor); // transformContent effectively rotates the raw coordinates, so those should now include @@ -727,9 +706,9 @@ TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) { AINPUT_SOURCE_JOYSTICK}; for (uint32_t source : NON_POINTER_SOURCES) { // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). - ui::Transform xform(ui::Transform::ROT_90, 800, 400); - xform.set(xform.tx() + 20, xform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90); + ui::Transform transform(ui::Transform::ROT_90, 800, 400); + transform.set(transform.tx() + 20, transform.ty() + 40); + MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, transform); event.setSource(source); // Since this event comes from a non-pointer source, it should include rotation but not @@ -741,72 +720,34 @@ TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) { } } -TEST_F(MotionEventTest, RawCompatTransform) { - { - // Make sure raw is raw regardless of transform translation. - ui::Transform xform; - xform.set(20, 40); - MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform); - ASSERT_EQ(60, event.getRawX(0)); - ASSERT_EQ(100, event.getRawY(0)); - ASSERT_NE(event.getRawX(0), event.getX(0)); - ASSERT_NE(event.getRawY(0), event.getY(0)); - // Relative values should not be modified. - ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); - ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); - } +TEST_F(MotionEventTest, AxesAreCorrectlyTransformed) { + const ui::Transform identity; + ui::Transform transform; + transform.set({1.1, -2.2, 3.3, -4.4, 5.5, -6.6, 0, 0, 1}); + ui::Transform rawTransform; + rawTransform.set({-6.6, 5.5, -4.4, 3.3, -2.2, 1.1, 0, 0, 1}); + auto transformWithoutTranslation = [](const ui::Transform& t, float x, float y) { + auto newPoint = t.transform(x, y); + auto newOrigin = t.transform(0, 0); + return newPoint - newOrigin; + }; - // Next check that getRaw contains rotation (for compatibility) but otherwise is still - // "Screen-space". The following tests check all 3 rotations. - { - // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). - ui::Transform xform(ui::Transform::ROT_90, 800, 400); - xform.set(xform.tx() + 20, xform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90); - ASSERT_EQ(700, event.getRawX(0)); - ASSERT_EQ(60, event.getRawY(0)); - ASSERT_NE(event.getRawX(0), event.getX(0)); - ASSERT_NE(event.getRawY(0), event.getY(0)); - // Relative values should be rotated but not translated. - ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); - ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); - } + const MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, rawTransform); - { - // Same as above, but check rotate-180. - ui::Transform xform(ui::Transform::ROT_180, 400, 800); - xform.set(xform.tx() + 20, xform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_180); - ASSERT_EQ(340, event.getRawX(0)); - ASSERT_EQ(700, event.getRawY(0)); - // Relative values should be rotated but not translated. - ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); - ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); - } + // The x and y axes should have the window transform applied. + const auto newPoint = transform.transform(60, 100); + ASSERT_EQ(newPoint.x, event.getX(0)); + ASSERT_EQ(newPoint.y, event.getY(0)); - { - // Same as above, but check rotate-270. - ui::Transform xform(ui::Transform::ROT_270, 800, 400); - xform.set(xform.tx() + 20, xform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_270); - ASSERT_EQ(100, event.getRawX(0)); - ASSERT_EQ(340, event.getRawY(0)); - // Relative values should be rotated but not translated. - ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); - ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); - } + // The raw values should have the display transform applied. + const auto raw = rawTransform.transform(60, 100); + ASSERT_EQ(raw.x, event.getRawX(0)); + ASSERT_EQ(raw.y, event.getRawY(0)); - { - // Finally, check that raw isn't effected by transform - ui::Transform xform(ui::Transform::ROT_270, 800, 400); - xform.set(xform.tx() + 20, xform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90); - ASSERT_EQ(700, event.getRawX(0)); - ASSERT_EQ(60, event.getRawY(0)); - // Relative values should be rotated but not translated. - ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); - ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); - } + // Relative values should have the window transform applied without any translation. + const auto rel = transformWithoutTranslation(transform, 42, 96); + ASSERT_EQ(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); + ASSERT_EQ(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); } TEST_F(MotionEventTest, Initialize_SetsClassification) { @@ -832,8 +773,7 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) { DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(classification, event.getClassification()); } @@ -854,9 +794,9 @@ TEST_F(MotionEventTest, Initialize_SetsCursorPosition) { event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0, - 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/, - pointerCount, pointerProperties, pointerCoords); + 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, identityTransform, + 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, + pointerCoords); event.offsetLocation(20, 60); ASSERT_EQ(280, event.getRawXCursorPosition()); ASSERT_EQ(540, event.getRawYCursorPosition()); diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 5d1f2c3bfc..d09f2ac748 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) { @@ -159,13 +160,14 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { constexpr float yScale = 3; constexpr float xOffset = -10; constexpr float yOffset = -20; + constexpr float rawXScale = 4; + constexpr float rawYScale = -5; + constexpr float rawXOffset = -11; + constexpr float rawYOffset = 42; constexpr float xPrecision = 0.25; constexpr float yPrecision = 0.5; constexpr float xCursorPosition = 1.3; constexpr float yCursorPosition = 50.6; - constexpr uint32_t displayOrientation = ui::Transform::ROT_0; - constexpr int32_t displayWidth = 1000; - constexpr int32_t displayHeight = 2000; constexpr nsecs_t downTime = 3; constexpr size_t pointerCount = 3; constexpr nsecs_t eventTime = 4; @@ -191,12 +193,14 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { ui::Transform transform; transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1}); + ui::Transform rawTransform; + rawTransform.set({rawXScale, 0, rawXOffset, 0, rawYScale, rawYOffset, 0, 0, 1}); status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action, actionButton, flags, edgeFlags, metaState, buttonState, classification, transform, xPrecision, yPrecision, - xCursorPosition, yCursorPosition, displayOrientation, - displayWidth, displayHeight, downTime, eventTime, - pointerCount, pointerProperties, pointerCoords); + xCursorPosition, yCursorPosition, rawTransform, + downTime, eventTime, pointerCount, pointerProperties, + pointerCoords); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; @@ -233,9 +237,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition()); EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition()); EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition()); - EXPECT_EQ(displayOrientation, motionEvent->getDisplayOrientation()); - EXPECT_EQ(displayWidth, motionEvent->getDisplaySize().x); - EXPECT_EQ(displayHeight, motionEvent->getDisplaySize().y); + EXPECT_EQ(rawTransform, motionEvent->getRawTransform()); EXPECT_EQ(downTime, motionEvent->getDownTime()); EXPECT_EQ(eventTime, motionEvent->getEventTime()); EXPECT_EQ(pointerCount, motionEvent->getPointerCount()); @@ -246,28 +248,18 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i)); EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), - motionEvent->getRawX(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), - motionEvent->getRawY(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) * xScale + xOffset, - motionEvent->getX(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) * yScale + yOffset, - motionEvent->getY(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - motionEvent->getPressure(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), - motionEvent->getSize(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), - motionEvent->getTouchMajor(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), - motionEvent->getTouchMinor(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), - motionEvent->getToolMajor(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), - motionEvent->getToolMinor(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), - motionEvent->getOrientation(i)); + const auto& pc = pointerCoords[i]; + EXPECT_EQ(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i)); + EXPECT_EQ(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i)); + EXPECT_EQ(pc.getX() * xScale + xOffset, motionEvent->getX(i)); + EXPECT_EQ(pc.getY() * yScale + yOffset, motionEvent->getY(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getSize(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getTouchMajor(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent->getTouchMinor(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent->getToolMajor(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent->getToolMinor(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), motionEvent->getOrientation(i)); } status = mConsumer->sendFinishedSignal(seq, false); @@ -413,6 +405,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 +481,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; @@ -460,12 +496,12 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZer } ui::Transform identityTransform; - status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, - 0, 0, 0, MotionClassification::NONE, identityTransform, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, - ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount, - pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, + 0, 0, 0, MotionClassification::NONE, identityTransform, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, + 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -477,12 +513,12 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha PointerCoords pointerCoords[pointerCount]; ui::Transform identityTransform; - status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, - 0, 0, 0, MotionClassification::NONE, identityTransform, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, - ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount, - pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, + 0, 0, 0, MotionClassification::NONE, identityTransform, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, + 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -499,12 +535,12 @@ TEST_F(InputPublisherAndConsumerTest, } ui::Transform identityTransform; - status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, - 0, 0, 0, MotionClassification::NONE, identityTransform, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, - ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount, - pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, + 0, 0, 0, MotionClassification::NONE, identityTransform, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, + 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -520,6 +556,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..2f88704d63 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -49,7 +49,7 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Key, downTime, 88); CHECK_OFFSET(InputMessage::Body::Motion, eventId, 0); - CHECK_OFFSET(InputMessage::Body::Motion, empty1, 4); + CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 4); CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8); CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Motion, source, 20); @@ -74,11 +74,13 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124); CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128); CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132); - CHECK_OFFSET(InputMessage::Body::Motion, displayOrientation, 136); - CHECK_OFFSET(InputMessage::Body::Motion, displayWidth, 140); - CHECK_OFFSET(InputMessage::Body::Motion, displayHeight, 144); - CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 148); - CHECK_OFFSET(InputMessage::Body::Motion, pointers, 152); + CHECK_OFFSET(InputMessage::Body::Motion, dsdxRaw, 136); + CHECK_OFFSET(InputMessage::Body::Motion, dtdxRaw, 140); + CHECK_OFFSET(InputMessage::Body::Motion, dtdyRaw, 144); + CHECK_OFFSET(InputMessage::Body::Motion, dsdyRaw, 148); + CHECK_OFFSET(InputMessage::Body::Motion, txRaw, 152); + CHECK_OFFSET(InputMessage::Body::Motion, tyRaw, 156); + CHECK_OFFSET(InputMessage::Body::Motion, pointers, 160); CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0); CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4); @@ -102,6 +104,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 +129,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/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 13e2b02ca4..3039362c2b 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -184,8 +184,7 @@ static std::vector<MotionEvent> createMotionEventStream( AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, identityTransform, 0 /*xPrecision*/, 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords); events.emplace_back(event); diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp index b29c9a4877..f2b59ea9ab 100644 --- a/libs/input/tests/VerifiedInputEvent_test.cpp +++ b/libs/input/tests/VerifiedInputEvent_test.cpp @@ -43,12 +43,12 @@ static MotionEvent getMotionEventWithFlags(int32_t flags) { ui::Transform transform; transform.set({2, 0, 4, 0, 3, 5, 0, 0, 1}); + ui::Transform identity; event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, - 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 100 /*downTime*/, + 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, identity, 100 /*downTime*/, 200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); return event; } diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp index 2dd6c4fcaa..79d9b9313c 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -77,6 +77,7 @@ namespace android { struct FrameCallback { AChoreographer_frameCallback callback; AChoreographer_frameCallback64 callback64; + AChoreographer_extendedFrameCallback extendedCallback; void* data; nsecs_t dueTime; @@ -95,6 +96,27 @@ struct RefreshRateCallback { class Choreographer; +/** + * Implementation of AChoreographerFrameCallbackData. + */ +struct ChoreographerFrameCallbackDataImpl { + struct FrameTimeline { + int64_t vsyncId{0}; + int64_t expectedPresentTimeNanos{0}; + int64_t deadlineNanos{0}; + }; + + int64_t frameTimeNanos{0}; + + size_t frameTimelinesLength; + + std::vector<FrameTimeline> frameTimelines; + + size_t preferredFrameTimelineIndex; + + const Choreographer* choreographer; +}; + struct { std::mutex lock; std::vector<Choreographer*> ptrs GUARDED_BY(lock); @@ -107,7 +129,9 @@ class Choreographer : public DisplayEventDispatcher, public MessageHandler { public: explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock); void postFrameCallbackDelayed(AChoreographer_frameCallback cb, - AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay); + AChoreographer_frameCallback64 cb64, + AChoreographer_extendedFrameCallback extendedCallback, void* data, + nsecs_t delay); void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) EXCLUDES(gChoreographers.lock); void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data); @@ -127,9 +151,8 @@ public: static Choreographer* getForThread(); virtual ~Choreographer() override EXCLUDES(gChoreographers.lock); - int64_t getVsyncId() const; - int64_t getFrameDeadline() const; int64_t getFrameInterval() const; + bool inCallback() const; private: Choreographer(const Choreographer&) = delete; @@ -145,6 +168,8 @@ private: void scheduleCallbacks(); + ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const; + std::mutex mLock; // Protected by mLock std::priority_queue<FrameCallback> mFrameCallbacks; @@ -152,6 +177,7 @@ private: nsecs_t mLatestVsyncPeriod = -1; VsyncEventData mLastVsyncEventData; + bool mInCallback = false; const sp<Looper> mLooper; const std::thread::id mThreadId; @@ -192,6 +218,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"); @@ -210,10 +237,12 @@ Choreographer::~Choreographer() { } } -void Choreographer::postFrameCallbackDelayed( - AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) { +void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb, + AChoreographer_frameCallback64 cb64, + AChoreographer_extendedFrameCallback extendedCallback, + void* data, nsecs_t delay) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - FrameCallback callback{cb, cb64, data, now + delay}; + FrameCallback callback{cb, cb64, extendedCallback, data, now + delay}; { std::lock_guard<std::mutex> _l{mLock}; mFrameCallbacks.push(callback); @@ -305,8 +334,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); } } @@ -368,7 +398,15 @@ void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t } mLastVsyncEventData = vsyncEventData; for (const auto& cb : callbacks) { - if (cb.callback64 != nullptr) { + if (cb.extendedCallback != nullptr) { + const ChoreographerFrameCallbackDataImpl frameCallbackData = + createFrameCallbackData(timestamp); + mInCallback = true; + cb.extendedCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>( + &frameCallbackData), + cb.data); + mInCallback = false; + } else if (cb.callback64 != nullptr) { cb.callback64(timestamp, cb.data); } else if (cb.callback != nullptr) { cb.callback(timestamp, cb.data); @@ -377,8 +415,8 @@ void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t } void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) { - ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", - this, to_string(displayId).c_str(), toString(connected)); + ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this, + to_string(displayId).c_str(), toString(connected)); } void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) { @@ -397,28 +435,36 @@ void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) { void Choreographer::handleMessage(const Message& message) { switch (message.what) { - case MSG_SCHEDULE_CALLBACKS: - scheduleCallbacks(); - break; - case MSG_SCHEDULE_VSYNC: - scheduleVsync(); - break; - case MSG_HANDLE_REFRESH_RATE_UPDATES: - handleRefreshRateUpdates(); - break; + case MSG_SCHEDULE_CALLBACKS: + scheduleCallbacks(); + break; + case MSG_SCHEDULE_VSYNC: + scheduleVsync(); + break; + case MSG_HANDLE_REFRESH_RATE_UPDATES: + handleRefreshRateUpdates(); + break; } } -int64_t Choreographer::getVsyncId() const { - return mLastVsyncEventData.id; +int64_t Choreographer::getFrameInterval() const { + return mLastVsyncEventData.frameInterval; } -int64_t Choreographer::getFrameDeadline() const { - return mLastVsyncEventData.deadlineTimestamp; +bool Choreographer::inCallback() const { + return mInCallback; } -int64_t Choreographer::getFrameInterval() const { - return mLastVsyncEventData.frameInterval; +ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const { + std::vector<ChoreographerFrameCallbackDataImpl::FrameTimeline> frameTimelines; + frameTimelines.push_back({.vsyncId = mLastVsyncEventData.id, + .expectedPresentTimeNanos = mLastVsyncEventData.expectedPresentTime, + .deadlineNanos = mLastVsyncEventData.deadlineTimestamp}); + return {.frameTimeNanos = timestamp, + .frameTimelinesLength = 1, + .preferredFrameTimelineIndex = 0, + .frameTimelines = frameTimelines, + .choreographer = this}; } } // namespace android @@ -433,6 +479,12 @@ static inline const Choreographer* AChoreographer_to_Choreographer( return reinterpret_cast<const Choreographer*>(choreographer); } +static inline const ChoreographerFrameCallbackDataImpl* +AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl( + const AChoreographerFrameCallbackData* data) { + return reinterpret_cast<const ChoreographerFrameCallbackDataImpl*>(data); +} + // Glue for private C api namespace android { void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) { @@ -485,6 +537,11 @@ void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographe void* data, uint32_t delayMillis) { return AChoreographer_postFrameCallbackDelayed64(choreographer, callback, data, delayMillis); } +void AChoreographer_routePostExtendedFrameCallback(AChoreographer* choreographer, + AChoreographer_extendedFrameCallback callback, + void* data) { + return AChoreographer_postExtendedFrameCallback(choreographer, callback, data); +} void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer, AChoreographer_refreshRateCallback callback, void* data) { @@ -495,13 +552,29 @@ void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreogra void* data) { return AChoreographer_unregisterRefreshRateCallback(choreographer, callback, data); } - -int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer) { - return AChoreographer_to_Choreographer(choreographer)->getVsyncId(); +int64_t AChoreographerFrameCallbackData_routeGetFrameTimeNanos( + const AChoreographerFrameCallbackData* data) { + return AChoreographerFrameCallbackData_getFrameTimeNanos(data); } - -int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer) { - return AChoreographer_to_Choreographer(choreographer)->getFrameDeadline(); +size_t AChoreographerFrameCallbackData_routeGetFrameTimelinesLength( + const AChoreographerFrameCallbackData* data) { + return AChoreographerFrameCallbackData_getFrameTimelinesLength(data); +} +size_t AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex( + const AChoreographerFrameCallbackData* data) { + return AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(data); +} +int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId( + const AChoreographerFrameCallbackData* data, size_t index) { + return AChoreographerFrameCallbackData_getFrameTimelineVsyncId(data, index); +} +int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime( + const AChoreographerFrameCallbackData* data, size_t index) { + return AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(data, index); +} +int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline( + const AChoreographerFrameCallbackData* data, size_t index) { + return AChoreographerFrameCallbackData_getFrameTimelineDeadline(data, index); } int64_t AChoreographer_getFrameInterval(const AChoreographer* choreographer) { @@ -521,24 +594,32 @@ AChoreographer* AChoreographer_getInstance() { } void AChoreographer_postFrameCallback(AChoreographer* choreographer, - AChoreographer_frameCallback callback, void* data) { - AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed( - callback, nullptr, data, 0); + AChoreographer_frameCallback callback, void* data) { + AChoreographer_to_Choreographer(choreographer) + ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0); } void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, - AChoreographer_frameCallback callback, void* data, long delayMillis) { - AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed( - callback, nullptr, data, ms2ns(delayMillis)); + AChoreographer_frameCallback callback, void* data, + long delayMillis) { + AChoreographer_to_Choreographer(choreographer) + ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis)); +} +void AChoreographer_postExtendedFrameCallback(AChoreographer* choreographer, + AChoreographer_extendedFrameCallback callback, + void* data) { + AChoreographer_to_Choreographer(choreographer) + ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0); } void AChoreographer_postFrameCallback64(AChoreographer* choreographer, - AChoreographer_frameCallback64 callback, void* data) { - AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed( - nullptr, callback, data, 0); + AChoreographer_frameCallback64 callback, void* data) { + AChoreographer_to_Choreographer(choreographer) + ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0); } void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, - AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) { - AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed( - nullptr, callback, data, ms2ns(delayMillis)); + AChoreographer_frameCallback64 callback, void* data, + uint32_t delayMillis) { + AChoreographer_to_Choreographer(choreographer) + ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, ms2ns(delayMillis)); } void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer, AChoreographer_refreshRateCallback callback, @@ -551,6 +632,58 @@ void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer, AChoreographer_to_Choreographer(choreographer)->unregisterRefreshRateCallback(callback, data); } +int64_t AChoreographerFrameCallbackData_getFrameTimeNanos( + const AChoreographerFrameCallbackData* data) { + const ChoreographerFrameCallbackDataImpl* frameCallbackData = + AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data); + LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(), + "Data is only valid in callback"); + return frameCallbackData->frameTimeNanos; +} +size_t AChoreographerFrameCallbackData_getFrameTimelinesLength( + const AChoreographerFrameCallbackData* data) { + const ChoreographerFrameCallbackDataImpl* frameCallbackData = + AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data); + LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(), + "Data is only valid in callback"); + return frameCallbackData->frameTimelinesLength; +} +size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex( + const AChoreographerFrameCallbackData* data) { + const ChoreographerFrameCallbackDataImpl* frameCallbackData = + AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data); + LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(), + "Data is only valid in callback"); + return frameCallbackData->preferredFrameTimelineIndex; +} +int64_t AChoreographerFrameCallbackData_getFrameTimelineVsyncId( + const AChoreographerFrameCallbackData* data, size_t index) { + const ChoreographerFrameCallbackDataImpl* frameCallbackData = + AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data); + LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(), + "Data is only valid in callback"); + LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds"); + return frameCallbackData->frameTimelines[index].vsyncId; +} +int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime( + const AChoreographerFrameCallbackData* data, size_t index) { + const ChoreographerFrameCallbackDataImpl* frameCallbackData = + AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data); + LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(), + "Data is only valid in callback"); + LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds"); + return frameCallbackData->frameTimelines[index].expectedPresentTimeNanos; +} +int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline( + const AChoreographerFrameCallbackData* data, size_t index) { + const ChoreographerFrameCallbackDataImpl* frameCallbackData = + AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data); + LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(), + "Data is only valid in callback"); + LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds"); + return frameCallbackData->frameTimelines[index].deadlineNanos; +} + AChoreographer* AChoreographer_create() { Choreographer* choreographer = new Choreographer(nullptr); status_t result = choreographer->initialize(); diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h index 7d25ce8253..4aa7e69097 100644 --- a/libs/nativedisplay/include-private/private/android/choreographer.h +++ b/libs/nativedisplay/include-private/private/android/choreographer.h @@ -29,19 +29,6 @@ void AChoreographer_initJVM(JNIEnv* env); // for consumption by callbacks. void AChoreographer_signalRefreshRateCallbacks(int64_t vsyncPeriod); -// Returns the vsync id of the last frame callback. Client are expected to call -// this function from their frame callback function to get the vsyncId and pass -// it together with a buffer or transaction to the Surface Composer. Calling -// this function from anywhere else will return an undefined value. -int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer); - -// Returns the deadline timestamp (in CLOCK_MONOTONIC) of the last frame callback. -// Client are expected to call this function from their frame callback function -// to get the deadline and use it to know whether a frame is likely to miss -// presentation. Calling this function from anywhere else will return an undefined -// value. -int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer); - // Returns the current interval in ns between frames. // Client are expected to call this function from their frame callback function. // Calling this function from anywhere else will return an undefined value. @@ -63,11 +50,26 @@ void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer, void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer, AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis); +void AChoreographer_routePostExtendedFrameCallback(AChoreographer* choreographer, + AChoreographer_extendedFrameCallback callback, + void* data); void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer, AChoreographer_refreshRateCallback callback, void* data); void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreographer, AChoreographer_refreshRateCallback callback, void* data); +int64_t AChoreographerFrameCallbackData_routeGetFrameTimeNanos( + const AChoreographerFrameCallbackData* data); +size_t AChoreographerFrameCallbackData_routeGetFrameTimelinesLength( + const AChoreographerFrameCallbackData* data); +size_t AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex( + const AChoreographerFrameCallbackData* data); +int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId( + const AChoreographerFrameCallbackData* data, size_t index); +int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime( + const AChoreographerFrameCallbackData* data, size_t index); +int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline( + const AChoreographerFrameCallbackData* data, size_t index); } // namespace android 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/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt index 9ed4915481..4dbfde83cd 100644 --- a/libs/nativedisplay/libnativedisplay.map.txt +++ b/libs/nativedisplay/libnativedisplay.map.txt @@ -7,6 +7,13 @@ LIBNATIVEDISPLAY { AChoreographer_postFrameCallbackDelayed64; # apex # introduced=30 AChoreographer_registerRefreshRateCallback; # apex # introduced=30 AChoreographer_unregisterRefreshRateCallback; # apex # introduced=30 + AChoreographer_postExtendedFrameCallback; # apex # introduced=33 + AChoreographerFrameCallbackData_getFrameTimeNanos; # apex # introduced=33 + AChoreographerFrameCallbackData_getFrameTimelinesLength; # apex # introduced=33 + AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex; # apex # introduced=33 + AChoreographerFrameCallbackData_getFrameTimelineVsyncId; # apex # introduced=33 + AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime; # apex # introduced=33 + AChoreographerFrameCallbackData_getFrameTimelineDeadline; # apex # introduced=33 AChoreographer_create; # apex # introduced=30 AChoreographer_destroy; # apex # introduced=30 AChoreographer_getFd; # apex # introduced=30 @@ -28,9 +35,14 @@ LIBNATIVEDISPLAY_PLATFORM { android::AChoreographer_routePostFrameCallbackDelayed64*; android::AChoreographer_routeRegisterRefreshRateCallback*; android::AChoreographer_routeUnregisterRefreshRateCallback*; + android::AChoreographer_routePostExtendedFrameCallback*; + android::AChoreographerFrameCallbackData_routeGetFrameTimeNanos*; + android::AChoreographerFrameCallbackData_routeGetFrameTimelinesLength*; + android::AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex*; + android::AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId*; + android::AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime*; + android::AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline*; android::AChoreographer_signalRefreshRateCallbacks*; - android::AChoreographer_getVsyncId*; - android::AChoreographer_getFrameDeadline*; android::AChoreographer_getFrameInterval*; android::ADisplay_acquirePhysicalDisplays*; android::ADisplay_release*; 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/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index 75f2385174..2db99923ff 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -133,12 +133,41 @@ int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transfo int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpace) { static_assert(static_cast<int>(ADATASPACE_UNKNOWN) == static_cast<int>(HAL_DATASPACE_UNKNOWN)); - static_assert(static_cast<int>(ADATASPACE_SCRGB_LINEAR) == static_cast<int>(HAL_DATASPACE_V0_SCRGB_LINEAR)); + static_assert(static_cast<int>(STANDARD_SHIFT) == static_cast<int>(HAL_DATASPACE_STANDARD_SHIFT)); + static_assert(static_cast<int>(STANDARD_MASK) == static_cast<int>(HAL_DATASPACE_STANDARD_MASK)); + static_assert(static_cast<int>(STANDARD_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED)); + static_assert(static_cast<int>(STANDARD_BT709) == static_cast<int>(HAL_DATASPACE_STANDARD_BT709)); + static_assert(static_cast<int>(STANDARD_BT601_625) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625)); + static_assert(static_cast<int>(STANDARD_BT601_625_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED)); + static_assert(static_cast<int>(STANDARD_BT601_525) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525)); + static_assert(static_cast<int>(STANDARD_BT601_525_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED)); + static_assert(static_cast<int>(STANDARD_BT470M) == static_cast<int>(HAL_DATASPACE_STANDARD_BT470M)); + static_assert(static_cast<int>(STANDARD_FILM) == static_cast<int>(HAL_DATASPACE_STANDARD_FILM)); + static_assert(static_cast<int>(STANDARD_DCI_P3) == static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3)); + static_assert(static_cast<int>(STANDARD_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB)); + static_assert(static_cast<int>(TRANSFER_SHIFT) == static_cast<int>(HAL_DATASPACE_TRANSFER_SHIFT)); + static_assert(static_cast<int>(TRANSFER_MASK) == static_cast<int>(HAL_DATASPACE_TRANSFER_MASK)); + static_assert(static_cast<int>(TRANSFER_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED)); + static_assert(static_cast<int>(TRANSFER_LINEAR) == static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR)); + static_assert(static_cast<int>(TRANSFER_SMPTE_170M) == static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M)); + static_assert(static_cast<int>(TRANSFER_GAMMA2_2) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2)); + static_assert(static_cast<int>(TRANSFER_GAMMA2_6) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6)); + static_assert(static_cast<int>(TRANSFER_GAMMA2_8) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8)); + static_assert(static_cast<int>(TRANSFER_ST2084) == static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084)); + static_assert(static_cast<int>(TRANSFER_HLG) == static_cast<int>(HAL_DATASPACE_TRANSFER_HLG)); + static_assert(static_cast<int>(RANGE_MASK) == static_cast<int>(HAL_DATASPACE_RANGE_MASK)); + static_assert(static_cast<int>(RANGE_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED)); + static_assert(static_cast<int>(RANGE_FULL) == static_cast<int>(HAL_DATASPACE_RANGE_FULL)); + static_assert(static_cast<int>(RANGE_LIMITED) == static_cast<int>(HAL_DATASPACE_RANGE_LIMITED)); + static_assert(static_cast<int>(RANGE_EXTENDED) == static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED)); static_assert(static_cast<int>(ADATASPACE_SRGB) == static_cast<int>(HAL_DATASPACE_V0_SRGB)); static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB)); static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3)); static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ)); static_assert(static_cast<int>(ADATASPACE_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_ADOBE_RGB)); + static_assert(static_cast<int>(ADATASPACE_JFIF) == static_cast<int>(HAL_DATASPACE_V0_JFIF)); + static_assert(static_cast<int>(ADATASPACE_BT601_625) == static_cast<int>(HAL_DATASPACE_V0_BT601_625)); + static_assert(static_cast<int>(ADATASPACE_BT601_525) == static_cast<int>(HAL_DATASPACE_V0_BT601_525)); static_assert(static_cast<int>(ADATASPACE_BT2020) == static_cast<int>(HAL_DATASPACE_BT2020)); static_assert(static_cast<int>(ADATASPACE_BT709) == static_cast<int>(HAL_DATASPACE_V0_BT709)); static_assert(static_cast<int>(ADATASPACE_DCI_P3) == static_cast<int>(HAL_DATASPACE_DCI_P3)); diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h index e759513a63..612fb3937b 100644 --- a/libs/nativewindow/include/android/data_space.h +++ b/libs/nativewindow/include/android/data_space.h @@ -43,6 +43,346 @@ enum ADataSpace { ADATASPACE_UNKNOWN = 0, /** + * Color-description aspects + * + * The following aspects define various characteristics of the color + * specification. These represent bitfields, so that a data space value + * can specify each of them independently. + */ + + /** + * Standard aspect + * + * Defines the chromaticity coordinates of the source primaries in terms of + * the CIE 1931 definition of x and y specified in ISO 11664-1. + */ + STANDARD_SHIFT = 16, + + STANDARD_MASK = 63 << 16, // 63 << STANDARD_SHIFT = 0x3F + + /** + * Chromacity coordinates are unknown or are determined by the application. + * Implementations shall use the following suggested standards: + * + * All YCbCr formats: BT709 if size is 720p or larger (since most video + * content is letterboxed this corresponds to width is + * 1280 or greater, or height is 720 or greater). + * BT601_625 if size is smaller than 720p or is JPEG. + * All RGB formats: BT709. + * + * For all other formats standard is undefined, and implementations should use + * an appropriate standard for the data represented. + */ + STANDARD_UNSPECIFIED = 0 << 16, // STANDARD_SHIFT + + /** + * Primaries: x y + * green 0.300 0.600 + * blue 0.150 0.060 + * red 0.640 0.330 + * white (D65) 0.3127 0.3290 + * + * Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation + * for RGB conversion. + */ + STANDARD_BT709 = 1 << 16, // 1 << STANDARD_SHIFT + + /** + * Primaries: x y + * green 0.290 0.600 + * blue 0.150 0.060 + * red 0.640 0.330 + * white (D65) 0.3127 0.3290 + * + * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation + * for RGB conversion from the one purely determined by the primaries + * to minimize the color shift into RGB space that uses BT.709 + * primaries. + */ + STANDARD_BT601_625 = 2 << 16, // 2 << STANDARD_SHIFT, + + /** + * Primaries: x y + * green 0.290 0.600 + * blue 0.150 0.060 + * red 0.640 0.330 + * white (D65) 0.3127 0.3290 + * + * Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation + * for RGB conversion. + */ + STANDARD_BT601_625_UNADJUSTED = 3 << 16, // 3 << STANDARD_SHIFT + + /** + * Primaries: x y + * green 0.310 0.595 + * blue 0.155 0.070 + * red 0.630 0.340 + * white (D65) 0.3127 0.3290 + * + * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation + * for RGB conversion from the one purely determined by the primaries + * to minimize the color shift into RGB space that uses BT.709 + * primaries. + */ + STANDARD_BT601_525 = 4 << 16, // 4 << STANDARD_SHIFT + + /** + * Primaries: x y + * green 0.310 0.595 + * blue 0.155 0.070 + * red 0.630 0.340 + * white (D65) 0.3127 0.3290 + * + * Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation + * for RGB conversion (as in SMPTE 240M). + */ + STANDARD_BT601_525_UNADJUSTED = 5 << 16, // 5 << STANDARD_SHIFT + + /** + * Primaries: x y + * green 0.170 0.797 + * blue 0.131 0.046 + * red 0.708 0.292 + * white (D65) 0.3127 0.3290 + * + * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation + * for RGB conversion. + */ + STANDARD_BT2020 = 6 << 16, // 6 << STANDARD_SHIFT + + /** + * Primaries: x y + * green 0.170 0.797 + * blue 0.131 0.046 + * red 0.708 0.292 + * white (D65) 0.3127 0.3290 + * + * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation + * for RGB conversion using the linear domain. + */ + STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16, // 7 << STANDARD_SHIFT + + /** + * Primaries: x y + * green 0.21 0.71 + * blue 0.14 0.08 + * red 0.67 0.33 + * white (C) 0.310 0.316 + * + * Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation + * for RGB conversion. + */ + STANDARD_BT470M = 8 << 16, // 8 << STANDARD_SHIFT + + /** + * Primaries: x y + * green 0.243 0.692 + * blue 0.145 0.049 + * red 0.681 0.319 + * white (C) 0.310 0.316 + * + * Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation + * for RGB conversion. + */ + STANDARD_FILM = 9 << 16, // 9 << STANDARD_SHIFT + + /** + * SMPTE EG 432-1 and SMPTE RP 431-2. (DCI-P3) + * Primaries: x y + * green 0.265 0.690 + * blue 0.150 0.060 + * red 0.680 0.320 + * white (D65) 0.3127 0.3290 + */ + STANDARD_DCI_P3 = 10 << 16, // 10 << STANDARD_SHIFT + + /** + * Adobe RGB + * Primaries: x y + * green 0.210 0.710 + * blue 0.150 0.060 + * red 0.640 0.330 + * white (D65) 0.3127 0.3290 + */ + STANDARD_ADOBE_RGB = 11 << 16, // 11 << STANDARD_SHIFT + + /** + * Transfer aspect + * + * Transfer characteristics are the opto-electronic transfer characteristic + * at the source as a function of linear optical intensity (luminance). + * + * For digital signals, E corresponds to the recorded value. Normally, the + * transfer function is applied in RGB space to each of the R, G and B + * components independently. This may result in color shift that can be + * minized by applying the transfer function in Lab space only for the L + * component. Implementation may apply the transfer function in RGB space + * for all pixel formats if desired. + */ + TRANSFER_SHIFT = 22, + + TRANSFER_MASK = 31 << 22, // 31 << TRANSFER_SHIFT = 0x1F + + /** + * Transfer characteristics are unknown or are determined by the + * application. + * + * Implementations should use the following transfer functions: + * + * For YCbCr formats: use TRANSFER_SMPTE_170M + * For RGB formats: use TRANSFER_SRGB + * + * For all other formats transfer function is undefined, and implementations + * should use an appropriate standard for the data represented. + */ + TRANSFER_UNSPECIFIED = 0 << 22, // 0 << TRANSFER_SHIFT + + /** + * Transfer characteristic curve: + * E = L + * L - luminance of image 0 <= L <= 1 for conventional colorimetry + * E - corresponding electrical signal + */ + TRANSFER_LINEAR = 1 << 22, // 1 << TRANSFER_SHIFT + + /** + * Transfer characteristic curve: + * + * E = 1.055 * L^(1/2.4) - 0.055 for 0.0031308 <= L <= 1 + * = 12.92 * L for 0 <= L < 0.0031308 + * L - luminance of image 0 <= L <= 1 for conventional colorimetry + * E - corresponding electrical signal + */ + TRANSFER_SRGB = 2 << 22, // 2 << TRANSFER_SHIFT + + /** + * BT.601 525, BT.601 625, BT.709, BT.2020 + * + * Transfer characteristic curve: + * E = 1.099 * L ^ 0.45 - 0.099 for 0.018 <= L <= 1 + * = 4.500 * L for 0 <= L < 0.018 + * L - luminance of image 0 <= L <= 1 for conventional colorimetry + * E - corresponding electrical signal + */ + TRANSFER_SMPTE_170M = 3 << 22, // 3 << TRANSFER_SHIFT + + /** + * Assumed display gamma 2.2. + * + * Transfer characteristic curve: + * E = L ^ (1/2.2) + * L - luminance of image 0 <= L <= 1 for conventional colorimetry + * E - corresponding electrical signal + */ + TRANSFER_GAMMA2_2 = 4 << 22, // 4 << TRANSFER_SHIFT + + /** + * display gamma 2.6. + * + * Transfer characteristic curve: + * E = L ^ (1/2.6) + * L - luminance of image 0 <= L <= 1 for conventional colorimetry + * E - corresponding electrical signal + */ + TRANSFER_GAMMA2_6 = 5 << 22, // 5 << TRANSFER_SHIFT + + /** + * display gamma 2.8. + * + * Transfer characteristic curve: + * E = L ^ (1/2.8) + * L - luminance of image 0 <= L <= 1 for conventional colorimetry + * E - corresponding electrical signal + */ + TRANSFER_GAMMA2_8 = 6 << 22, // 6 << TRANSFER_SHIFT + + /** + * SMPTE ST 2084 (Dolby Perceptual Quantizer) + * + * Transfer characteristic curve: + * E = ((c1 + c2 * L^n) / (1 + c3 * L^n)) ^ m + * c1 = c3 - c2 + 1 = 3424 / 4096 = 0.8359375 + * c2 = 32 * 2413 / 4096 = 18.8515625 + * c3 = 32 * 2392 / 4096 = 18.6875 + * m = 128 * 2523 / 4096 = 78.84375 + * n = 0.25 * 2610 / 4096 = 0.1593017578125 + * L - luminance of image 0 <= L <= 1 for HDR colorimetry. + * L = 1 corresponds to 10000 cd/m2 + * E - corresponding electrical signal + */ + TRANSFER_ST2084 = 7 << 22, // 7 << TRANSFER_SHIFT + + /** + * ARIB STD-B67 Hybrid Log Gamma + * + * Transfer characteristic curve: + * E = r * L^0.5 for 0 <= L <= 1 + * = a * ln(L - b) + c for 1 < L + * a = 0.17883277 + * b = 0.28466892 + * c = 0.55991073 + * r = 0.5 + * L - luminance of image 0 <= L for HDR colorimetry. L = 1 corresponds + * to reference white level of 100 cd/m2 + * E - corresponding electrical signal + */ + TRANSFER_HLG = 8 << 22, // 8 << TRANSFER_SHIFT + + /** + * Range aspect + * + * Defines the range of values corresponding to the unit range of 0-1. + * This is defined for YCbCr only, but can be expanded to RGB space. + */ + RANGE_SHIFT = 27, + + RANGE_MASK = 7 << 27, // 7 << RANGE_SHIFT = 0x7 + + /** + * Range is unknown or are determined by the application. Implementations + * shall use the following suggested ranges: + * + * All YCbCr formats: limited range. + * All RGB or RGBA formats (including RAW and Bayer): full range. + * All Y formats: full range + * + * For all other formats range is undefined, and implementations should use + * an appropriate range for the data represented. + */ + RANGE_UNSPECIFIED = 0 << 27, // 0 << RANGE_SHIFT = 0x0 + + /** + * Full range uses all values for Y, Cb and Cr from + * 0 to 2^b-1, where b is the bit depth of the color format. + */ + RANGE_FULL = 1 << 27, // 1 << RANGE_SHIFT = 0x8000000 + + /** + * Limited range uses values 16/256*2^b to 235/256*2^b for Y, and + * 1/16*2^b to 15/16*2^b for Cb, Cr, R, G and B, where b is the bit depth of + * the color format. + * + * E.g. For 8-bit-depth formats: + * Luma (Y) samples should range from 16 to 235, inclusive + * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive + * + * For 10-bit-depth formats: + * Luma (Y) samples should range from 64 to 940, inclusive + * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive + */ + RANGE_LIMITED = 2 << 27, // 2 << RANGE_SHIFT = 0x10000000 + + /** + * Extended range is used for scRGB. Intended for use with + * floating point pixel formats. [0.0 - 1.0] is the standard + * sRGB space. Values outside the range 0.0 - 1.0 can encode + * color outside the sRGB gamut. + * Used to blend / merge multiple dataspaces on a single display. + */ + RANGE_EXTENDED = 3 << 27, // 3 << RANGE_SHIFT = 0x18000000 + + /** * scRGB linear encoding: * * The red, green, and blue components are stored in extended sRGB space, @@ -112,6 +452,33 @@ enum ADataSpace { ADATASPACE_ADOBE_RGB = 151715840, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL /** + * JPEG File Interchange Format (JFIF) + * + * Same model as BT.601-625, but all values (Y, Cb, Cr) range from 0 to 255 + * + * Use full range, SMPTE 170M transfer and BT.601_625 standard. + */ + ADATASPACE_JFIF = 146931712, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_FULL + + /** + * ITU-R Recommendation 601 (BT.601) - 525-line + * + * Standard-definition television, 525 Lines (NTSC) + * + * Use limited range, SMPTE 170M transfer and BT.601_525 standard. + */ + ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED + + /** + * ITU-R Recommendation 709 (BT.709) + * + * High-definition television + * + * Use limited range, SMPTE 170M transfer and BT.709 standard. + */ + ADATASPACE_BT601_525 = 281280512, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED + + /** * ITU-R Recommendation 2020 (BT.2020) * * Ultra High-definition television 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..2b6833e6e9 100644 --- a/libs/renderengine/skia/filters/BlurFilter.cpp +++ b/libs/renderengine/skia/filters/BlurFilter.cpp @@ -34,20 +34,20 @@ namespace skia { BlurFilter::BlurFilter() { SkString blurString(R"( - uniform shader input; + uniform shader child; uniform float2 in_blurOffset; 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 = child.eval(xy); + c += child.eval(float2(clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x), + clamp( in_blurOffset.y + xy.y, 0, in_maxSizeXY.y))); + c += child.eval(float2(clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x), + clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y))); + c += child.eval(float2(clamp(-in_blurOffset.x + xy.x, 0, in_maxSizeXY.x), + clamp( in_blurOffset.y + xy.y, 0, in_maxSizeXY.y))); + c += child.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)); } )"); @@ -101,7 +101,7 @@ sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t // start by downscaling and doing the first blur pass SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone); SkRuntimeShaderBuilder blurBuilder(mBlurEffect); - blurBuilder.child("input") = + blurBuilder.child("child") = input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix); blurBuilder.uniform("in_blurOffset") = SkV2{stepX * kInputScale, stepY * kInputScale}; blurBuilder.uniform("in_maxSizeXY") = @@ -112,7 +112,7 @@ sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t // And now we'll build our chain of scaled blur stages for (auto i = 1; i < numberOfPasses; i++) { const float stepScale = (float)i * kInputScale; - blurBuilder.child("input") = + blurBuilder.child("child") = tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear); blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale}; blurBuilder.uniform("in_maxSizeXY") = diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp index fc45af945b..73dadef166 100644 --- a/libs/renderengine/skia/filters/LinearEffect.cpp +++ b/libs/renderengine/skia/filters/LinearEffect.cpp @@ -389,9 +389,9 @@ static void generateOETF(ui::Dataspace dataspace, SkString& shader) { static void generateEffectiveOOTF(bool undoPremultipliedAlpha, SkString& shader) { shader.append(R"( - uniform shader input; + uniform shader child; half4 main(float2 xy) { - float4 c = float4(sample(input, xy)); + float4 c = float4(child.eval(xy)); )"); if (undoPremultipliedAlpha) { shader.append(R"( @@ -451,7 +451,7 @@ sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEff ATRACE_CALL(); SkRuntimeShaderBuilder effectBuilder(runtimeEffect); - effectBuilder.child("input") = shader; + effectBuilder.child("child") = shader; if (linearEffect.inputDataspace == linearEffect.outputDataspace) { effectBuilder.uniform("in_rgbToXyz") = mat4(); 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..36d001fe0f 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -54,7 +54,7 @@ cc_library_static { target: { windows: { enabled: true, - } + }, }, defaults: [ @@ -86,6 +86,7 @@ cc_library_static { export_include_dirs: [ "include", + "include_mock", "include_private", "include_types", ], @@ -137,7 +138,6 @@ cc_library_shared { "HdrCapabilities.cpp", "PixelFormat.cpp", "PublicFormat.cpp", - "Size.cpp", "StaticDisplayInfo.cpp", ], @@ -159,7 +159,7 @@ cc_library_shared { "android.hardware.graphics.allocator@2.0", "android.hardware.graphics.allocator@3.0", "android.hardware.graphics.allocator@4.0", - "android.hardware.graphics.common-V2-ndk", + "android.hardware.graphics.common-V3-ndk", "android.hardware.graphics.common@1.2", "android.hardware.graphics.mapper@2.0", "android.hardware.graphics.mapper@2.1", @@ -176,7 +176,7 @@ cc_library_shared { export_shared_lib_headers: [ "android.hardware.graphics.common@1.2", - "android.hardware.graphics.common-V2-ndk", + "android.hardware.graphics.common-V3-ndk", "android.hardware.graphics.mapper@4.0", "libgralloctypes", ], @@ -266,6 +266,6 @@ filegroup { "Rect.cpp", "Region.cpp", "PixelFormat.cpp", - "Transform.cpp" + "Transform.cpp", ], } diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp index cd68c1c0ec..b34d90699f 100644 --- a/libs/ui/Transform.cpp +++ b/libs/ui/Transform.cpp @@ -396,6 +396,11 @@ Transform Transform::inverse() const { result.mMatrix[1][0] = -b*idet; result.mMatrix[1][1] = a*idet; result.mType = mType; + if (getOrientation() & ROT_90) { + // Recalculate the type if there is a 90-degree rotation component, since the inverse + // of ROT_90 is ROT_270 and vice versa. + result.mType |= UNKNOWN_TYPE; + } vec2 T(-x, -y); T = result.transform(T); 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/Fence.h b/libs/ui/include/ui/Fence.h index 6efecd3c0e..7634007771 100644 --- a/libs/ui/include/ui/Fence.h +++ b/libs/ui/include/ui/Fence.h @@ -26,6 +26,10 @@ namespace android { +namespace mock { +class MockFence; +} + class String8; // =========================================================================== @@ -109,7 +113,7 @@ public: // fence transitioned to the signaled state. If the fence is not signaled // then SIGNAL_TIME_PENDING is returned. If the fence is invalid or if an // error occurs then SIGNAL_TIME_INVALID is returned. - nsecs_t getSignalTime() const; + virtual nsecs_t getSignalTime() const; enum class Status { Invalid, // Fence is invalid @@ -144,7 +148,10 @@ public: private: // Only allow instantiation using ref counting. friend class LightRefBase<Fence>; - ~Fence() = default; + virtual ~Fence() = default; + + // Allow mocking for unit testing + friend class mock::MockFence; base::unique_fd mFenceFd; }; 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/include_mock/ui/MockFence.h b/libs/ui/include_mock/ui/MockFence.h new file mode 100644 index 0000000000..162ec02455 --- /dev/null +++ b/libs/ui/include_mock/ui/MockFence.h @@ -0,0 +1,32 @@ +/* + * 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 <gmock/gmock.h> +#include <ui/Fence.h> + +namespace android::mock { + +class MockFence : public android::Fence { +public: + MockFence() = default; + virtual ~MockFence() = default; + + MOCK_METHOD(nsecs_t, getSignalTime, (), (const, override)); +}; + +}; // namespace android::mock diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index 516aad824e..0ee15f26cf 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -27,28 +27,40 @@ cc_test { name: "Region_test", shared_libs: ["libui"], srcs: ["Region_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], } cc_test { name: "colorspace_test", shared_libs: ["libui"], srcs: ["colorspace_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], } cc_test { name: "DisplayId_test", shared_libs: ["libui"], srcs: ["DisplayId_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], } cc_test { name: "FlattenableHelpers_test", shared_libs: ["libui"], srcs: ["FlattenableHelpers_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], } cc_test { @@ -68,7 +80,10 @@ cc_test { "GraphicBufferAllocator_test.cpp", "mock/MockGrallocAllocator.cpp", ], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], } cc_test { @@ -83,14 +98,20 @@ cc_test { "libutils", ], srcs: ["GraphicBuffer_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], } // This test has a main method, and requires a separate binary to be built. cc_test { name: "GraphicBufferOverBinder_test", srcs: ["GraphicBufferOverBinder_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], shared_libs: [ "libbinder", "libgui", @@ -105,7 +126,10 @@ cc_test { test_suites: ["device-tests"], shared_libs: ["libui"], srcs: ["Rect_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], } cc_test { @@ -113,5 +137,29 @@ cc_test { test_suites: ["device-tests"], shared_libs: ["libui"], srcs: ["Size_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], +} + +cc_test { + name: "MockFence_test", + shared_libs: ["libui"], + static_libs: ["libgmock"], + srcs: ["MockFence_test.cpp"], + cflags: [ + "-Wall", + "-Werror", + ], +} + +cc_test { + name: "Transform_test", + shared_libs: ["libui"], + srcs: ["Transform_test.cpp"], + cflags: [ + "-Wall", + "-Werror", + ], } 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/MockFence_test.cpp b/libs/ui/tests/MockFence_test.cpp new file mode 100644 index 0000000000..6e520b1aee --- /dev/null +++ b/libs/ui/tests/MockFence_test.cpp @@ -0,0 +1,45 @@ +/* + * 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 <ui/MockFence.h> + +#include <gtest/gtest.h> + +namespace android::ui { + +using testing::Return; + +class MockFenceTest : public testing::Test { +public: + sp<Fence> getFenceForTesting() const { return mMockFence; } + + const mock::MockFence& getMockFence() const { return *mMockFence; } + +private: + sp<mock::MockFence> mMockFence = sp<mock::MockFence>::make(); +}; + +TEST_F(MockFenceTest, getSignalTime) { + sp<Fence> fence = getFenceForTesting(); + + EXPECT_CALL(getMockFence(), getSignalTime).WillOnce(Return(Fence::SIGNAL_TIME_PENDING)); + EXPECT_EQ(Fence::SIGNAL_TIME_PENDING, fence->getSignalTime()); + + EXPECT_CALL(getMockFence(), getSignalTime).WillOnce(Return(1234)); + EXPECT_EQ(1234, fence->getSignalTime()); +} + +} // 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/libs/ui/tests/Transform_test.cpp b/libs/ui/tests/Transform_test.cpp new file mode 100644 index 0000000000..6964284eaa --- /dev/null +++ b/libs/ui/tests/Transform_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 <ui/Transform.h> + +#include <gtest/gtest.h> + +namespace android::ui { + +TEST(TransformTest, inverseRotation_hasCorrectType) { + const auto testRotationFlagsForInverse = [](Transform::RotationFlags rotation, + Transform::RotationFlags expectedInverse, + bool isRotation) { + const Transform t(rotation, 0, 0); + EXPECT_EQ(t.getOrientation(), rotation); + const Transform inverse = t.inverse(); + EXPECT_EQ(inverse.getOrientation(), expectedInverse); + + if (isRotation) { + EXPECT_TRUE(t.getType() & Transform::ROTATE); + EXPECT_TRUE(inverse.getType() & Transform::ROTATE); + } else { + EXPECT_FALSE(t.getType() & Transform::ROTATE); + EXPECT_FALSE(inverse.getType() & Transform::ROTATE); + } + }; + + testRotationFlagsForInverse(Transform::ROT_0, Transform::ROT_0, false); + testRotationFlagsForInverse(Transform::ROT_90, Transform::ROT_270, true); + testRotationFlagsForInverse(Transform::ROT_180, Transform::ROT_180, true); + testRotationFlagsForInverse(Transform::ROT_270, Transform::ROT_90, true); + testRotationFlagsForInverse(Transform::FLIP_H, Transform::FLIP_H, false); + testRotationFlagsForInverse(Transform::FLIP_V, Transform::FLIP_V, false); +} + +} // namespace android::ui 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/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index a9cbd5ad02..29d8a0f441 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -345,7 +345,7 @@ void InputClassifier::HalDeathRecipient::serviceDied( // --- InputClassifier --- -InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) +InputClassifier::InputClassifier(InputListenerInterface& listener) : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {} void InputClassifier::setMotionClassifierEnabled(bool enabled) { @@ -369,12 +369,12 @@ void InputClassifier::setMotionClassifierEnabled(bool enabled) { void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { // pass through - mListener->notifyConfigurationChanged(args); + mListener.notifyConfigurationChanged(args); } void InputClassifier::notifyKey(const NotifyKeyArgs* args) { // pass through - mListener->notifyKey(args); + mListener.notifyKey(args); } void InputClassifier::notifyMotion(const NotifyMotionArgs* args) { @@ -382,28 +382,28 @@ void InputClassifier::notifyMotion(const NotifyMotionArgs* args) { // MotionClassifier is only used for touch events, for now const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args); if (!sendToMotionClassifier) { - mListener->notifyMotion(args); + mListener.notifyMotion(args); return; } NotifyMotionArgs newArgs(*args); newArgs.classification = mMotionClassifier->classify(newArgs); - mListener->notifyMotion(&newArgs); + mListener.notifyMotion(&newArgs); } void InputClassifier::notifySensor(const NotifySensorArgs* args) { // pass through - mListener->notifySensor(args); + mListener.notifySensor(args); } void InputClassifier::notifyVibratorState(const NotifyVibratorStateArgs* args) { // pass through - mListener->notifyVibratorState(args); + mListener.notifyVibratorState(args); } void InputClassifier::notifySwitch(const NotifySwitchArgs* args) { // pass through - mListener->notifySwitch(args); + mListener.notifySwitch(args); } void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { @@ -412,12 +412,12 @@ void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { mMotionClassifier->reset(*args); } // continue to next stage - mListener->notifyDeviceReset(args); + mListener.notifyDeviceReset(args); } void InputClassifier::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) { // pass through - mListener->notifyPointerCaptureChanged(args); + mListener.notifyPointerCaptureChanged(args); } void InputClassifier::setMotionClassifier( diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h index 1eef02006e..deeae7c6f4 100644 --- a/services/inputflinger/InputClassifier.h +++ b/services/inputflinger/InputClassifier.h @@ -18,7 +18,6 @@ #define _UI_INPUT_CLASSIFIER_H #include <android-base/thread_annotations.h> -#include <utils/RefBase.h> #include <thread> #include <unordered_map> @@ -88,7 +87,7 @@ public: * Base interface for an InputListener stage. * Provides classification to events. */ -class InputClassifierInterface : public virtual RefBase, public InputListenerInterface { +class InputClassifierInterface : public InputListenerInterface { public: virtual void setMotionClassifierEnabled(bool enabled) = 0; /** @@ -96,7 +95,7 @@ public: * This method may be called on any thread (usually by the input manager). */ virtual void dump(std::string& dump) = 0; -protected: + InputClassifierInterface() { } virtual ~InputClassifierInterface() { } }; @@ -223,7 +222,7 @@ private: */ class InputClassifier : public InputClassifierInterface { public: - explicit InputClassifier(const sp<InputListenerInterface>& listener); + explicit InputClassifier(InputListenerInterface& listener); virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; virtual void notifyKey(const NotifyKeyArgs* args) override; @@ -245,7 +244,7 @@ private: // Protect access to mMotionClassifier, since it may become null via a hidl callback std::mutex mLock; // The next stage to pass input events to - sp<InputListenerInterface> mListener; + InputListenerInterface& mListener; std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock); std::thread mInitializeMotionClassifierThread; diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index 71b0f5fec9..158d0ebd58 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -44,8 +44,8 @@ bool NotifyConfigurationChangedArgs::operator==(const NotifyConfigurationChanged return id == rhs.id && eventTime == rhs.eventTime; } -void NotifyConfigurationChangedArgs::notify(const sp<InputListenerInterface>& listener) const { - listener->notifyConfigurationChanged(this); +void NotifyConfigurationChangedArgs::notify(InputListenerInterface& listener) const { + listener.notifyConfigurationChanged(this); } // --- NotifyKeyArgs --- @@ -89,8 +89,8 @@ bool NotifyKeyArgs::operator==(const NotifyKeyArgs& rhs) const { downTime == rhs.downTime; } -void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const { - listener->notifyKey(this); +void NotifyKeyArgs::notify(InputListenerInterface& listener) const { + listener.notifyKey(this); } // --- NotifyMotionArgs --- @@ -188,8 +188,8 @@ bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const { return true; } -void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const { - listener->notifyMotion(this); +void NotifyMotionArgs::notify(InputListenerInterface& listener) const { + listener.notifyMotion(this); } // --- NotifySwitchArgs --- @@ -212,8 +212,8 @@ bool NotifySwitchArgs::operator==(const NotifySwitchArgs rhs) const { switchValues == rhs.switchValues && switchMask == rhs.switchMask; } -void NotifySwitchArgs::notify(const sp<InputListenerInterface>& listener) const { - listener->notifySwitch(this); +void NotifySwitchArgs::notify(InputListenerInterface& listener) const { + listener.notifySwitch(this); } // --- NotifySensorArgs --- @@ -247,8 +247,8 @@ bool NotifySensorArgs::operator==(const NotifySensorArgs rhs) const { hwTimestamp == rhs.hwTimestamp && values == rhs.values; } -void NotifySensorArgs::notify(const sp<InputListenerInterface>& listener) const { - listener->notifySensor(this); +void NotifySensorArgs::notify(InputListenerInterface& listener) const { + listener.notifySensor(this); } // --- NotifyVibratorStateArgs --- @@ -265,8 +265,8 @@ bool NotifyVibratorStateArgs::operator==(const NotifyVibratorStateArgs rhs) cons isOn == rhs.isOn; } -void NotifyVibratorStateArgs::notify(const sp<InputListenerInterface>& listener) const { - listener->notifyVibratorState(this); +void NotifyVibratorStateArgs::notify(InputListenerInterface& listener) const { + listener.notifyVibratorState(this); } // --- NotifyDeviceResetArgs --- @@ -281,8 +281,8 @@ bool NotifyDeviceResetArgs::operator==(const NotifyDeviceResetArgs& rhs) const { return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId; } -void NotifyDeviceResetArgs::notify(const sp<InputListenerInterface>& listener) const { - listener->notifyDeviceReset(this); +void NotifyDeviceResetArgs::notify(InputListenerInterface& listener) const { + listener.notifyDeviceReset(this); } // --- NotifyPointerCaptureChangedArgs --- @@ -299,8 +299,8 @@ bool NotifyPointerCaptureChangedArgs::operator==(const NotifyPointerCaptureChang return id == rhs.id && eventTime == rhs.eventTime && request == rhs.request; } -void NotifyPointerCaptureChangedArgs::notify(const sp<InputListenerInterface>& listener) const { - listener->notifyPointerCaptureChanged(this); +void NotifyPointerCaptureChangedArgs::notify(InputListenerInterface& listener) const { + listener.notifyPointerCaptureChanged(this); } // --- QueuedInputListener --- @@ -312,9 +312,8 @@ static inline void traceEvent(const char* functionName, int32_t id) { } } -QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) : - mInnerListener(innerListener) { -} +QueuedInputListener::QueuedInputListener(InputListenerInterface& innerListener) + : mInnerListener(innerListener) {} QueuedInputListener::~QueuedInputListener() { size_t count = mArgsQueue.size(); diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 7b3658dfde..221e193136 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -58,8 +58,8 @@ InputManager::InputManager( const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { mDispatcher = createInputDispatcher(dispatcherPolicy); - mClassifier = new InputClassifier(mDispatcher); - mReader = createInputReader(readerPolicy, mClassifier); + mClassifier = std::make_unique<InputClassifier>(*mDispatcher); + mReader = createInputReader(readerPolicy, *mClassifier); } InputManager::~InputManager() { @@ -102,16 +102,16 @@ status_t InputManager::stop() { return status; } -sp<InputReaderInterface> InputManager::getReader() { - return mReader; +InputReaderInterface& InputManager::getReader() { + return *mReader; } -sp<InputClassifierInterface> InputManager::getClassifier() { - return mClassifier; +InputClassifierInterface& InputManager::getClassifier() { + return *mClassifier; } -sp<InputDispatcherInterface> InputManager::getDispatcher() { - return mDispatcher; +InputDispatcherInterface& InputManager::getDispatcher() { + return *mDispatcher; } // Used by tests only. diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index 4c07c22154..a6baf2f56d 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -30,7 +30,6 @@ #include <input/InputTransport.h> #include <android/os/BnInputFlinger.h> -#include <android/os/IInputFlinger.h> #include <utils/Errors.h> #include <utils/RefBase.h> #include <utils/Timers.h> @@ -76,13 +75,13 @@ public: virtual status_t stop() = 0; /* Gets the input reader. */ - virtual sp<InputReaderInterface> getReader() = 0; + virtual InputReaderInterface& getReader() = 0; /* Gets the input classifier */ - virtual sp<InputClassifierInterface> getClassifier() = 0; + virtual InputClassifierInterface& getClassifier() = 0; /* Gets the input dispatcher. */ - virtual sp<InputDispatcherInterface> getDispatcher() = 0; + virtual InputDispatcherInterface& getDispatcher() = 0; }; class InputManager : public InputManagerInterface, public BnInputFlinger { @@ -97,9 +96,9 @@ public: status_t start() override; status_t stop() override; - sp<InputReaderInterface> getReader() override; - sp<InputClassifierInterface> getClassifier() override; - sp<InputDispatcherInterface> getDispatcher() override; + InputReaderInterface& getReader() override; + InputClassifierInterface& getClassifier() override; + InputDispatcherInterface& getDispatcher() override; status_t dump(int fd, const Vector<String16>& args) override; binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override; @@ -107,11 +106,11 @@ public: binder::Status setFocusedWindow(const gui::FocusRequest&) override; private: - sp<InputReaderInterface> mReader; + std::unique_ptr<InputReaderInterface> mReader; - sp<InputClassifierInterface> mClassifier; + std::unique_ptr<InputClassifierInterface> mClassifier; - sp<InputDispatcherInterface> mDispatcher; + std::unique_ptr<InputDispatcherInterface> mDispatcher; }; } // namespace android 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/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 6ce0313929..41e9ce2e41 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -21,6 +21,7 @@ #include <gui/constants.h> #include "../dispatcher/InputDispatcher.h" +using android::base::Result; using android::gui::WindowInfo; using android::gui::WindowInfoHandle; using android::os::IInputConstants; @@ -162,15 +163,15 @@ public: } protected: - explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, const std::string name) - : mDispatcher(dispatcher) { - mClientChannel = *mDispatcher->createInputChannel(name); + explicit FakeInputReceiver(InputDispatcher& dispatcher, const std::string name) { + Result<std::unique_ptr<InputChannel>> channelResult = dispatcher.createInputChannel(name); + LOG_ALWAYS_FATAL_IF(!channelResult.ok()); + mClientChannel = std::move(*channelResult); mConsumer = std::make_unique<InputConsumer>(mClientChannel); } virtual ~FakeInputReceiver() {} - sp<InputDispatcher> mDispatcher; std::shared_ptr<InputChannel> mClientChannel; std::unique_ptr<InputConsumer> mConsumer; PreallocatedInputEventFactory mEventFactory; @@ -182,7 +183,7 @@ public: static const int32_t HEIGHT = 200; FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle, - const sp<InputDispatcher>& dispatcher, const std::string name) + InputDispatcher& dispatcher, const std::string name) : FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) { inputApplicationHandle->updateInfo(); updateInfo(); @@ -236,8 +237,8 @@ static MotionEvent generateMotionEvent() { /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, identityTransform, /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, currentTime, currentTime, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, currentTime, + currentTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); return event; } @@ -272,13 +273,13 @@ static NotifyMotionArgs generateMotionArgs() { static void benchmarkNotifyMotion(benchmark::State& state) { // Create dispatcher sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy(); - sp<InputDispatcher> dispatcher = new InputDispatcher(fakePolicy); + std::unique_ptr<InputDispatcher> dispatcher = std::make_unique<InputDispatcher>(fakePolicy); dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); dispatcher->start(); // Create a window that will receive motion events std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window"); + sp<FakeWindowHandle> window = new FakeWindowHandle(application, *dispatcher, "Fake Window"); dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -306,13 +307,13 @@ static void benchmarkNotifyMotion(benchmark::State& state) { static void benchmarkInjectMotion(benchmark::State& state) { // Create dispatcher sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy(); - sp<InputDispatcher> dispatcher = new InputDispatcher(fakePolicy); + std::unique_ptr<InputDispatcher> dispatcher = std::make_unique<InputDispatcher>(fakePolicy); dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); dispatcher->start(); // Create a window that will receive motion events std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window"); + sp<FakeWindowHandle> window = new FakeWindowHandle(application, *dispatcher, "Fake Window"); dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h index c4262ad2d8..ba602831eb 100644 --- a/services/inputflinger/dispatcher/Connection.h +++ b/services/inputflinger/dispatcher/Connection.h @@ -20,6 +20,7 @@ #include "InputState.h" #include <input/InputTransport.h> +#include <utils/RefBase.h> #include <deque> namespace android::inputdispatcher { diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h index b3c5709cbc..4636820538 100644 --- a/services/inputflinger/dispatcher/DragState.h +++ b/services/inputflinger/dispatcher/DragState.h @@ -18,7 +18,7 @@ #define _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H #include <gui/WindowInfo.h> -#include <utils/RefBase.h> +#include <utils/StrongPointer.h> #include <string> namespace android { diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 82b4fe476e..c03581d8da 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -40,14 +40,15 @@ VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry) { entry.repeatCount}; } -VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry) { - const float rawX = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X); - const float rawY = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y); +VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry, + const ui::Transform& rawTransform) { + const vec2 rawXY = MotionEvent::calculateTransformedXY(entry.source, rawTransform, + entry.pointerCoords[0].getXYValue()); const int actionMasked = entry.action & AMOTION_EVENT_ACTION_MASK; return {{VerifiedInputEvent::Type::MOTION, entry.deviceId, entry.eventTime, entry.source, entry.displayId}, - rawX, - rawY, + rawXY.x, + rawXY.y, actionMasked, entry.downTime, entry.flags & VERIFIED_MOTION_EVENT_FLAGS, @@ -176,10 +177,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 +193,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, @@ -296,15 +310,14 @@ std::string SensorEntry::getDescription() const { volatile int32_t DispatchEntry::sNextSeqAtomic; DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags, - ui::Transform transform, float globalScaleFactor, - uint32_t displayOrientation, int2 displaySize) + const ui::Transform& transform, const ui::Transform& rawTransform, + float globalScaleFactor) : seq(nextSeq()), eventEntry(std::move(eventEntry)), targetFlags(targetFlags), transform(transform), + rawTransform(rawTransform), globalScaleFactor(globalScaleFactor), - displayOrientation(displayOrientation), - displaySize(displaySize), deliveryTime(0), resolvedAction(0), resolvedFlags(0) {} @@ -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..477781a2ad 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 @@ -214,9 +226,8 @@ struct DispatchEntry { std::shared_ptr<EventEntry> eventEntry; // the event to dispatch int32_t targetFlags; ui::Transform transform; + ui::Transform rawTransform; float globalScaleFactor; - uint32_t displayOrientation; - int2 displaySize; // Both deliveryTime and timeoutTime are only populated when the entry is sent to the app, // and will be undefined before that. nsecs_t deliveryTime; // time when the event was actually delivered @@ -229,8 +240,8 @@ struct DispatchEntry { int32_t resolvedFlags; DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags, - ui::Transform transform, float globalScaleFactor, uint32_t displayOrientation, - int2 displaySize); + const ui::Transform& transform, const ui::Transform& rawTransform, + float globalScaleFactor); inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; } @@ -243,56 +254,8 @@ 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; -}; +VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry, + const ui::Transform& rawTransform); } // namespace android::inputdispatcher 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 dccfa99a38..3db3907f5c 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) || @@ -331,18 +346,15 @@ static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inp // Use identity transform for joystick and position-based (touchpad) events because they // don't depend on the window transform. return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, identityTransform, - 1.0f /*globalScaleFactor*/, - inputTarget.displayOrientation, - inputTarget.displaySize); + identityTransform, 1.0f /*globalScaleFactor*/); } } if (inputTarget.useDefaultPointerTransform()) { const ui::Transform& transform = inputTarget.getDefaultPointerTransform(); return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform, - inputTarget.globalScaleFactor, - inputTarget.displayOrientation, - inputTarget.displaySize); + inputTarget.displayTransform, + inputTarget.globalScaleFactor); } ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); @@ -393,27 +405,13 @@ static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inp std::unique_ptr<DispatchEntry> dispatchEntry = std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags, - firstPointerTransform, inputTarget.globalScaleFactor, - inputTarget.displayOrientation, - inputTarget.displaySize); + firstPointerTransform, inputTarget.displayTransform, + inputTarget.globalScaleFactor); return dispatchEntry; } -static void addGestureMonitors(const std::vector<Monitor>& monitors, - std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0, - float yOffset = 0) { - if (monitors.empty()) { - return; - } - outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size()); - for (const Monitor& monitor : monitors) { - outTouchedMonitors.emplace_back(monitor, xOffset, yOffset); - } -} - -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 +420,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 +430,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 +439,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 +447,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 +461,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 +474,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 +510,32 @@ 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; +} + +bool isFromSource(uint32_t source, uint32_t test) { + return (source & test) == test; +} + +vec2 transformWithoutTranslation(const ui::Transform& transform, float x, float y) { + const vec2 transformedXy = transform.transform(x, y); + const vec2 transformedOrigin = transform.transform(0, 0); + return transformedXy - transformedOrigin; +} + +} // namespace + // --- InputDispatcher --- InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) @@ -539,30 +562,29 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic mLooper = new Looper(false); mReporter = createInputReporter(); + mWindowInfoListener = new DispatcherWindowListener(*this); + SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener); + mKeyRepeatState.lastKeyEntry = nullptr; policy->getDispatcherConfiguration(&mConfig); } 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 */); } } -void InputDispatcher::onFirstRef() { - SurfaceComposerClient::getDefault()->addWindowInfosListener(this); -} - status_t InputDispatcher::start() { if (mThread) { return ALREADY_EXISTS; @@ -595,7 +617,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 +820,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); @@ -885,7 +915,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { */ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) { const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN && - (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER); + isFromSource(motionEntry.source, AINPUT_SOURCE_CLASS_POINTER); // Optimize case where the current application is unresponsive and the user // decides to touch a window in a different application. @@ -910,11 +940,9 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt } // Alternatively, maybe there's a gesture monitor that could handle this event - std::vector<TouchedMonitor> gestureMonitors = - findTouchedGestureMonitorsLocked(displayId, {}); - for (TouchedMonitor& gestureMonitor : gestureMonitors) { + for (const auto& monitor : getValueByKey(mGestureMonitorsByDisplay, displayId)) { sp<Connection> connection = - getConnectionLocked(gestureMonitor.monitor.inputChannel->getConnectionToken()); + getConnectionLocked(monitor.inputChannel->getConnectionToken()); if (connection != nullptr && connection->responsive) { // This monitor could take more input. Drop all events preceding this // event, so that gesture monitor could get a chance to receive the stream @@ -956,9 +984,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 +1007,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 +1034,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 +1053,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; } @@ -1052,28 +1069,13 @@ sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayI return nullptr; } -std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked( - int32_t displayId, const std::vector<sp<WindowInfoHandle>>& portalWindows) 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; -} - void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason 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 +1133,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 +1160,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 +1210,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 +1247,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 +1392,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 +1460,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 +1493,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 +1541,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); @@ -1575,7 +1609,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< return true; } - bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER; + const bool isPointerEvent = isFromSource(entry->source, AINPUT_SOURCE_CLASS_POINTER); // Identify targets. std::vector<InputTarget> inputTargets; @@ -1613,23 +1647,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 +1687,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 +1791,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 +1848,7 @@ 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; } @@ -1859,7 +1878,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 @@ -1911,16 +1930,16 @@ InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked( * Given a list of monitors, remove the ones we cannot find a connection for, and the ones * that are currently unresponsive. */ -std::vector<TouchedMonitor> InputDispatcher::selectResponsiveMonitorsLocked( - const std::vector<TouchedMonitor>& monitors) const { - std::vector<TouchedMonitor> responsiveMonitors; +std::vector<Monitor> InputDispatcher::selectResponsiveMonitorsLocked( + const std::vector<Monitor>& monitors) const { + std::vector<Monitor> responsiveMonitors; std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors), - [this](const TouchedMonitor& monitor) REQUIRES(mLock) { - sp<Connection> connection = getConnectionLocked( - monitor.monitor.inputChannel->getConnectionToken()); + [this](const Monitor& monitor) REQUIRES(mLock) { + sp<Connection> connection = + getConnectionLocked(monitor.inputChannel->getConnectionToken()); if (connection == nullptr) { ALOGE("Could not find connection for monitor %s", - monitor.monitor.inputChannel->getName().c_str()); + monitor.inputChannel->getName().c_str()); return false; } if (!connection->responsive) { @@ -1976,7 +1995,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT); bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN || maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction); - const bool isFromMouse = entry.source == AINPUT_SOURCE_MOUSE; + const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE); bool wrongDevice = false; if (newGesture) { bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; @@ -2021,14 +2040,9 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)); 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*/); - - std::vector<TouchedMonitor> newGestureMonitors = isDown - ? findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows) - : std::vector<TouchedMonitor>{}; + const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN; + newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, + isDown /*addOutsideTargets*/); // Figure out whether splitting will be allowed for this window. if (newTouchedWindowHandle != nullptr && @@ -2075,7 +2089,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); @@ -2089,8 +2103,10 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( newTouchedWindowHandle = nullptr; } - // Also don't send the new touch event to unresponsive gesture monitors - newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors); + const std::vector<Monitor> newGestureMonitors = isDown + ? selectResponsiveMonitorsLocked( + getValueByKey(mGestureMonitorsByDisplay, displayId)) + : std::vector<Monitor>{}; if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) { ALOGI("Dropping event because there is no touchable window or gesture monitor at " @@ -2204,10 +2220,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)); } @@ -2217,10 +2233,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)); @@ -2310,9 +2326,8 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( touchedWindow.pointerIds, inputTargets); } - for (const TouchedMonitor& touchedMonitor : tempTouchState.gestureMonitors) { - addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset, - touchedMonitor.yOffset, inputTargets); + for (const auto& monitor : tempTouchState.gestureMonitors) { + addMonitoringTargetLocked(monitor, displayId, inputTargets); } // Drop the outside or hover touch windows since we will not care about them @@ -2410,13 +2425,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(); } @@ -2445,8 +2459,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)) { @@ -2463,7 +2476,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(); } } @@ -2491,9 +2504,13 @@ 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()) { + inputTarget.displayTransform = displayInfoIt->second.transform; + } else { + ALOGI_IF(isPerWindowInputRotationEnabled(), + "DisplayInfo not found for window on display: %d", windowInfo->displayId); + } inputTargets.push_back(inputTarget); it = inputTargets.end() - 1; } @@ -2505,27 +2522,29 @@ void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHa } void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, - int32_t displayId, float xOffset, - float yOffset) { + int32_t displayId) { std::unordered_map<int32_t, std::vector<Monitor>>::const_iterator it = mGlobalMonitorsByDisplay.find(displayId); if (it != mGlobalMonitorsByDisplay.end()) { const std::vector<Monitor>& monitors = it->second; for (const Monitor& monitor : monitors) { - addMonitoringTargetLocked(monitor, xOffset, yOffset, inputTargets); + addMonitoringTargetLocked(monitor, displayId, inputTargets); } } } -void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xOffset, - float yOffset, +void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, int32_t displayId, std::vector<InputTarget>& inputTargets) { InputTarget target; target.inputChannel = monitor.inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; ui::Transform t; - t.set(xOffset, yOffset); + if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) { + const auto& displayTransform = it->second.transform; + target.displayTransform = displayTransform; + t = displayTransform; + } target.setDefaultPointerTransform(t); inputTargets.push_back(target); } @@ -2662,8 +2681,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, @@ -2751,9 +2769,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; } } @@ -2779,6 +2797,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: @@ -2786,17 +2808,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, @@ -2809,21 +2831,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; } @@ -2831,7 +2853,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) { @@ -2922,10 +2944,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; @@ -2955,11 +2978,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; @@ -2975,11 +2998,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 } @@ -3006,6 +3029,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; @@ -3017,7 +3041,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; } } @@ -3111,10 +3135,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, @@ -3124,9 +3149,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(); @@ -3205,9 +3230,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, motionEntry.xPrecision, motionEntry.yPrecision, motionEntry.xCursorPosition, motionEntry.yCursorPosition, - dispatchEntry->displayOrientation, - dispatchEntry->displaySize.x, - dispatchEntry->displaySize.y, + dispatchEntry->rawTransform, motionEntry.downTime, motionEntry.eventTime, motionEntry.pointerCount, motionEntry.pointerProperties, usingCoords); @@ -3223,6 +3246,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); @@ -3245,7 +3278,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; } } @@ -3264,11 +3297,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, " @@ -3312,16 +3345,18 @@ std::array<uint8_t, 32> InputDispatcher::sign(const VerifiedInputEvent& event) c const std::array<uint8_t, 32> InputDispatcher::getSignature( const MotionEntry& motionEntry, const DispatchEntry& dispatchEntry) const { - int32_t actionMasked = dispatchEntry.resolvedAction & AMOTION_EVENT_ACTION_MASK; - if ((actionMasked == AMOTION_EVENT_ACTION_UP) || (actionMasked == AMOTION_EVENT_ACTION_DOWN)) { + const int32_t actionMasked = dispatchEntry.resolvedAction & AMOTION_EVENT_ACTION_MASK; + if (actionMasked != AMOTION_EVENT_ACTION_UP && actionMasked != AMOTION_EVENT_ACTION_DOWN) { // Only sign events up and down events as the purely move events // are tied to their up/down counterparts so signing would be redundant. - VerifiedMotionEvent verifiedEvent = verifiedMotionEventFromMotionEntry(motionEntry); - verifiedEvent.actionMasked = actionMasked; - verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS; - return sign(verifiedEvent); + return INVALID_HMAC; } - return INVALID_HMAC; + + VerifiedMotionEvent verifiedEvent = + verifiedMotionEventFromMotionEntry(motionEntry, dispatchEntry.rawTransform); + verifiedEvent.actionMasked = actionMasked; + verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS; + return sign(verifiedEvent); } const std::array<uint8_t, 32> InputDispatcher::getSignature( @@ -3335,10 +3370,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) { @@ -3346,16 +3381,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); @@ -3370,7 +3408,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)); } } } @@ -3437,7 +3483,7 @@ int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionTok gotOne = true; } if (gotOne) { - runCommandsLockedInterruptible(); + runCommandsLockedInterruptable(); if (status == WOULD_BLOCK) { return 1; } @@ -3514,12 +3560,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 = @@ -3546,17 +3592,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; } } @@ -3583,10 +3630,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 = @@ -3609,13 +3656,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; } } @@ -3729,9 +3777,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 @@ -3785,14 +3833,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; } @@ -3863,33 +3911,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; @@ -3910,17 +3958,21 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { mLock.lock(); if (shouldSendMotionToInputFilterLocked(args)) { + ui::Transform displayTransform; + if (const auto it = mDisplayInfos.find(args->displayId); it != mDisplayInfos.end()) { + displayTransform = it->second.transform; + } + mLock.unlock(); MotionEvent event; - ui::Transform transform; event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC, args->action, args->actionButton, args->flags, args->edgeFlags, - args->metaState, args->buttonState, args->classification, transform, - args->xPrecision, args->yPrecision, args->xCursorPosition, - args->yCursorPosition, ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, - INVALID_DISPLAY_SIZE, args->downTime, args->eventTime, - args->pointerCount, args->pointerProperties, args->pointerCoords); + args->metaState, args->buttonState, args->classification, + displayTransform, args->xPrecision, args->yPrecision, + args->xCursorPosition, args->yCursorPosition, displayTransform, + args->downTime, args->eventTime, args->pointerCount, + args->pointerProperties, args->pointerCoords); policyFlags |= POLICY_FLAG_FILTERED; if (!mPolicy->filterInputEvent(&event, policyFlags)) { @@ -3942,6 +3994,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 @@ -3952,12 +4011,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 @@ -3980,10 +4039,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); } @@ -3992,11 +4051,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; @@ -4004,10 +4063,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 @@ -4024,10 +4083,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 @@ -4045,11 +4104,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; @@ -4118,12 +4177,17 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( case AINPUT_EVENT_TYPE_MOTION: { const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event); - int32_t action = motionEvent.getAction(); - size_t pointerCount = motionEvent.getPointerCount(); + const int32_t action = motionEvent.getAction(); + const bool isPointerEvent = + isFromSource(event->getSource(), AINPUT_SOURCE_CLASS_POINTER); + // If a pointer event has no displayId specified, inject it to the default display. + const uint32_t displayId = isPointerEvent && (event->getDisplayId() == ADISPLAY_ID_NONE) + ? ADISPLAY_ID_DEFAULT + : event->getDisplayId(); + const size_t pointerCount = motionEvent.getPointerCount(); const PointerProperties* pointerProperties = motionEvent.getPointerProperties(); - int32_t actionButton = motionEvent.getActionButton(); + const int32_t actionButton = motionEvent.getActionButton(); int32_t flags = motionEvent.getFlags(); - int32_t displayId = motionEvent.getDisplayId(); if (!validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) { return InputEventInjectionResult::FAILED; } @@ -4148,8 +4212,8 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( std::unique_ptr<MotionEntry> injectedEntry = std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes, resolvedDeviceId, motionEvent.getSource(), - motionEvent.getDisplayId(), policyFlags, action, - actionButton, flags, motionEvent.getMetaState(), + displayId, policyFlags, action, actionButton, + flags, motionEvent.getMetaState(), motionEvent.getButtonState(), motionEvent.getClassification(), motionEvent.getEdgeFlags(), @@ -4161,6 +4225,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( pointerProperties, samplePointerCoords, motionEvent.getXOffset(), motionEvent.getYOffset()); + transformMotionEntryForInjectionLocked(*injectedEntry); injectedEntries.push(std::move(injectedEntry)); for (size_t i = motionEvent.getHistorySize(); i > 0; i--) { sampleEventTimes += 1; @@ -4168,9 +4233,8 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( std::unique_ptr<MotionEntry> nextInjectedEntry = std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes, resolvedDeviceId, motionEvent.getSource(), - motionEvent.getDisplayId(), policyFlags, - action, actionButton, flags, - motionEvent.getMetaState(), + displayId, policyFlags, action, actionButton, + flags, motionEvent.getMetaState(), motionEvent.getButtonState(), motionEvent.getClassification(), motionEvent.getEdgeFlags(), @@ -4182,6 +4246,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( uint32_t(pointerCount), pointerProperties, samplePointerCoords, motionEvent.getXOffset(), motionEvent.getYOffset()); + transformMotionEntryForInjectionLocked(*nextInjectedEntry); injectedEntries.push(std::move(nextInjectedEntry)); } break; @@ -4227,10 +4292,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; } @@ -4241,16 +4306,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; } @@ -4263,10 +4328,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; } @@ -4313,11 +4378,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. @@ -4345,6 +4410,38 @@ void InputDispatcher::setInjectionResult(EventEntry& entry, } } +void InputDispatcher::transformMotionEntryForInjectionLocked(MotionEntry& entry) const { + const bool isRelativeMouseEvent = isFromSource(entry.source, AINPUT_SOURCE_MOUSE_RELATIVE); + if (!isRelativeMouseEvent && !isFromSource(entry.source, AINPUT_SOURCE_CLASS_POINTER)) { + return; + } + + // Input injection works in the logical display coordinate space, but the input pipeline works + // display space, so we need to transform the injected events accordingly. + const auto it = mDisplayInfos.find(entry.displayId); + if (it == mDisplayInfos.end()) return; + const auto& transformToDisplay = it->second.transform.inverse(); + + for (uint32_t i = 0; i < entry.pointerCount; i++) { + PointerCoords& pc = entry.pointerCoords[i]; + const auto xy = isRelativeMouseEvent + ? transformWithoutTranslation(transformToDisplay, pc.getX(), pc.getY()) + : transformToDisplay.transform(pc.getXYValue()); + pc.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x); + pc.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y); + + // Axes with relative values never represent points on a screen, so they should never have + // translation applied. If a device does not report relative values, these values are always + // 0, and will remain unaffected by the following operation. + const auto rel = + transformWithoutTranslation(transformToDisplay, + pc.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), + pc.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)); + pc.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, rel.x); + pc.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, rel.y); + } +} + void InputDispatcher::incrementPendingForegroundDispatches(EventEntry& entry) { InjectionState* injectionState = entry.injectionState; if (injectionState) { @@ -4477,8 +4574,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) || @@ -4512,6 +4608,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) { @@ -4592,6 +4689,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 { @@ -4723,7 +4832,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); @@ -4802,6 +4911,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) { @@ -5020,14 +5130,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"; @@ -5039,9 +5141,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++) { @@ -5049,7 +5159,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, " @@ -5057,13 +5167,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, @@ -5074,13 +5183,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 { @@ -5155,6 +5263,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) { @@ -5221,9 +5335,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; @@ -5387,9 +5501,9 @@ status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) { TouchState& state = stateIt->second; std::shared_ptr<InputChannel> requestingChannel; std::optional<int32_t> foundDeviceId; - for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) { - if (touchedMonitor.monitor.inputChannel->getConnectionToken() == token) { - requestingChannel = touchedMonitor.monitor.inputChannel; + for (const auto& monitor : state.gestureMonitors) { + if (monitor.inputChannel->getConnectionToken() == token) { + requestingChannel = monitor.inputChannel; foundDeviceId = state.deviceId; } } @@ -5503,46 +5617,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; + } - std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( - &InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); - commandEntry->connection = connection; - postCommandLocked(std::move(commandEntry)); + // 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::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::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::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::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::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) { @@ -5585,17 +5745,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, @@ -5626,109 +5780,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; @@ -5736,122 +5805,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)); } /** @@ -5899,7 +5883,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) { @@ -5924,11 +5908,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); @@ -5956,21 +5941,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(); @@ -6004,19 +5989,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"); @@ -6030,18 +6015,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. @@ -6057,16 +6042,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); @@ -6075,21 +6060,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()); @@ -6201,7 +6177,7 @@ void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& ch disablePointerCaptureForcedLocked(); if (mFocusedDisplayId == changes.displayId) { - notifyFocusChangedLocked(changes.oldFocus, changes.newFocus); + sendFocusChangedCommandLocked(changes.oldFocus, changes.newFocus); } } @@ -6233,22 +6209,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) { @@ -6266,16 +6234,29 @@ 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( @@ -6285,7 +6266,7 @@ bool InputDispatcher::shouldDropInput( isWindowObscuredLocked(windowHandle))) { ALOGW("Dropping %s event targeting %s as requested by input feature %s on display " "%" PRId32 ".", - NamedEnum::string(entry.type).c_str(), windowHandle->getName().c_str(), + ftl::enum_string(entry.type).c_str(), windowHandle->getName().c_str(), windowHandle->getInfo()->inputFeatures.string().c_str(), windowHandle->getInfo()->displayId); return true; @@ -6293,4 +6274,10 @@ bool InputDispatcher::shouldDropInput( return false; } +void InputDispatcher::DispatcherWindowListener::onWindowInfosChanged( + const std::vector<gui::WindowInfo>& windowInfos, + const std::vector<DisplayInfo>& displayInfos) { + mDispatcher.onWindowInfosChanged(windowInfos, displayInfos); +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 04913d479a..55ca6c943e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -47,7 +47,6 @@ #include <unistd.h> #include <utils/BitSet.h> #include <utils/Looper.h> -#include <utils/RefBase.h> #include <utils/Timers.h> #include <utils/threads.h> #include <condition_variable> @@ -81,12 +80,10 @@ class Connection; * * A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa. */ -class InputDispatcher : public android::InputDispatcherInterface, public gui::WindowInfosListener { -protected: - ~InputDispatcher() override; - +class InputDispatcher : public android::InputDispatcherInterface { public: explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy); + ~InputDispatcher() override; void dump(std::string& dump) override; void monitor() override; @@ -143,7 +140,9 @@ public: void displayRemoved(int32_t displayId) override; - void onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos) override; + // Public because it's also used by tests to simulate the WindowInfosListener callback + void onWindowInfosChanged(const std::vector<android::gui::WindowInfo>& windowInfos, + const std::vector<android::gui::DisplayInfo>& displayInfos); private: enum class DropReason { @@ -171,7 +170,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 +233,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); @@ -261,6 +278,7 @@ private: bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid); void setInjectionResult(EventEntry& entry, android::os::InputEventInjectionResult injectionResult); + void transformMotionEntryForInjectionLocked(MotionEntry&) const REQUIRES(mLock); std::condition_variable mInjectionSyncFinished; void incrementPendingForegroundDispatches(EventEntry& entry); @@ -296,8 +314,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 +337,22 @@ private: float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock); android::os::BlockUntrustedTouchesMode mBlockUntrustedTouchesMode GUARDED_BY(mLock); - std::unordered_map<int32_t, std::vector<sp<android::gui::WindowInfoHandle>>> + class DispatcherWindowListener : public gui::WindowInfosListener { + public: + explicit DispatcherWindowListener(InputDispatcher& dispatcher) : mDispatcher(dispatcher){}; + void onWindowInfosChanged( + const std::vector<android::gui::WindowInfo>& windowInfos, + const std::vector<android::gui::DisplayInfo>& displayInfos) override; + + private: + InputDispatcher& mDispatcher; + }; + sp<gui::WindowInfosListener> mWindowInfoListener; + + 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 +375,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 +443,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 +495,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,20 +527,16 @@ 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 - REQUIRES(mLock); - std::vector<TouchedMonitor> selectResponsiveMonitorsLocked( - const std::vector<TouchedMonitor>& gestureMonitors) const REQUIRES(mLock); + std::vector<Monitor> selectResponsiveMonitorsLocked( + const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock); void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets) REQUIRES(mLock); - void addMonitoringTargetLocked(const Monitor& monitor, float xOffset, float yOffset, + void addMonitoringTargetLocked(const Monitor& monitor, int32_t displayId, std::vector<InputTarget>& inputTargets) REQUIRES(mLock); - void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId, - float xOffset = 0, float yOffset = 0) REQUIRES(mLock); + void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId) + REQUIRES(mLock); void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock); bool checkInjectionPermission(const sp<android::gui::WindowInfoHandle>& windowHandle, const InjectionState* injectionState); @@ -611,53 +635,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) @@ -672,8 +673,6 @@ private: sp<InputReporterInterface> mReporter; sp<com::android::internal::compat::IPlatformCompatNative> mCompatService; - - void onFirstRef() override; }; } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcherFactory.cpp b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp index 8d7fa7573b..bca1600db9 100644 --- a/services/inputflinger/dispatcher/InputDispatcherFactory.cpp +++ b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp @@ -19,9 +19,9 @@ namespace android { -sp<InputDispatcherInterface> createInputDispatcher( +std::unique_ptr<InputDispatcherInterface> createInputDispatcher( const sp<InputDispatcherPolicyInterface>& policy) { - return new android::inputdispatcher::InputDispatcher(policy); + return std::make_unique<android::inputdispatcher::InputDispatcher>(policy); } } // namespace android diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index 7c463c8697..0725389ed6 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -21,7 +21,6 @@ #include <input/InputTransport.h> #include <ui/Transform.h> #include <utils/BitSet.h> -#include <utils/RefBase.h> namespace android::inputdispatcher { @@ -101,11 +100,8 @@ struct InputTarget { // (ignored for KeyEvents) float globalScaleFactor = 1.0f; - // Current display orientation - uint32_t displayOrientation = ui::Transform::ROT_0; - - // Display-size in its natural rotation. Used for compatibility transform of raw coordinates. - int2 displaySize = {INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE}; + // Current display transform. Used for compatibility for raw coordinates. + ui::Transform displayTransform; // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. 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/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp index bbce75926f..43a82d5cca 100644 --- a/services/inputflinger/dispatcher/Monitor.cpp +++ b/services/inputflinger/dispatcher/Monitor.cpp @@ -22,8 +22,4 @@ namespace android::inputdispatcher { Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel, int32_t pid) : inputChannel(inputChannel), pid(pid) {} -// --- TouchedMonitor --- -TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset) - : monitor(monitor), xOffset(xOffset), yOffset(yOffset) {} - } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h index 7be0760bac..365d5bee4c 100644 --- a/services/inputflinger/dispatcher/Monitor.h +++ b/services/inputflinger/dispatcher/Monitor.h @@ -29,15 +29,6 @@ struct Monitor { explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel, int32_t pid); }; -// For tracking the offsets we need to apply when adding gesture monitor targets. -struct TouchedMonitor { - Monitor monitor; - float xOffset = 0.f; - float yOffset = 0.f; - - explicit TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset); -}; - } // namespace android::inputdispatcher #endif // _UI_INPUT_INPUTDISPATCHER_MONITOR_H diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index 20b6eadf5b..759b3e7097 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,17 +75,7 @@ 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) { +void TouchState::addGestureMonitors(const std::vector<Monitor>& newMonitors) { const size_t newSize = gestureMonitors.size() + newMonitors.size(); gestureMonitors.reserve(newSize); gestureMonitors.insert(std::end(gestureMonitors), std::begin(newMonitors), @@ -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..4a62051db8 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -36,12 +36,7 @@ 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; + std::vector<Monitor> gestureMonitors; TouchState(); ~TouchState(); @@ -50,12 +45,13 @@ struct TouchState { void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds); void addPortalWindow(const sp<android::gui::WindowInfoHandle>& windowHandle); - void addGestureMonitors(const std::vector<TouchedMonitor>& monitors); + void addGestureMonitors(const std::vector<Monitor>& monitors); void removeWindowByToken(const sp<IBinder>& token); void filterNonAsIsTouchWindows(); 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/InputDispatcherFactory.h b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h index a359557f80..38d0c32529 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h @@ -17,7 +17,7 @@ #ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H #define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H -#include <utils/RefBase.h> +#include <utils/StrongPointer.h> #include "InputDispatcherInterface.h" #include "InputDispatcherPolicyInterface.h" @@ -25,7 +25,7 @@ namespace android { // This factory method is used to encapsulate implementation details in internal header files. -sp<InputDispatcherInterface> createInputDispatcher( +std::unique_ptr<InputDispatcherInterface> createInputDispatcher( const sp<InputDispatcherPolicyInterface>& policy); } // namespace android diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index a7dccf0d19..714e7a0ad9 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -33,12 +33,10 @@ namespace android { /* Notifies the system about input events generated by the input reader. * The dispatcher is expected to be mostly asynchronous. */ -class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface { -protected: +class InputDispatcherInterface : public InputListenerInterface { +public: InputDispatcherInterface() {} virtual ~InputDispatcherInterface() {} - -public: /* Dumps the state of the input dispatcher. * * This method may be called on any thread (usually by the input manager). */ @@ -202,6 +200,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/include/InputListener.h b/services/inputflinger/include/InputListener.h index fe74214b36..db6310478c 100644 --- a/services/inputflinger/include/InputListener.h +++ b/services/inputflinger/include/InputListener.h @@ -22,7 +22,6 @@ #include <input/Input.h> #include <input/InputDevice.h> #include <input/TouchVideoFrame.h> -#include <utils/RefBase.h> namespace android { @@ -40,7 +39,7 @@ struct NotifyArgs { virtual ~NotifyArgs() { } - virtual void notify(const sp<InputListenerInterface>& listener) const = 0; + virtual void notify(InputListenerInterface& listener) const = 0; }; @@ -57,7 +56,7 @@ struct NotifyConfigurationChangedArgs : public NotifyArgs { virtual ~NotifyConfigurationChangedArgs() { } - virtual void notify(const sp<InputListenerInterface>& listener) const; + void notify(InputListenerInterface& listener) const override; }; @@ -88,7 +87,7 @@ struct NotifyKeyArgs : public NotifyArgs { virtual ~NotifyKeyArgs() { } - virtual void notify(const sp<InputListenerInterface>& listener) const; + void notify(InputListenerInterface& listener) const override; }; @@ -142,7 +141,7 @@ struct NotifyMotionArgs : public NotifyArgs { bool operator==(const NotifyMotionArgs& rhs) const; - virtual void notify(const sp<InputListenerInterface>& listener) const; + void notify(InputListenerInterface& listener) const override; }; /* Describes a sensor event. */ @@ -167,7 +166,7 @@ struct NotifySensorArgs : public NotifyArgs { ~NotifySensorArgs() override {} - void notify(const sp<InputListenerInterface>& listener) const override; + void notify(InputListenerInterface& listener) const override; }; /* Describes a switch event. */ @@ -187,7 +186,7 @@ struct NotifySwitchArgs : public NotifyArgs { virtual ~NotifySwitchArgs() { } - virtual void notify(const sp<InputListenerInterface>& listener) const; + void notify(InputListenerInterface& listener) const override; }; @@ -206,7 +205,7 @@ struct NotifyDeviceResetArgs : public NotifyArgs { virtual ~NotifyDeviceResetArgs() { } - virtual void notify(const sp<InputListenerInterface>& listener) const; + void notify(InputListenerInterface& listener) const override; }; /* Describes a change in the state of Pointer Capture. */ @@ -224,7 +223,7 @@ struct NotifyPointerCaptureChangedArgs : public NotifyArgs { virtual ~NotifyPointerCaptureChangedArgs() {} - virtual void notify(const sp<InputListenerInterface>& listener) const; + void notify(InputListenerInterface& listener) const override; }; /* Describes a vibrator state event. */ @@ -242,18 +241,19 @@ struct NotifyVibratorStateArgs : public NotifyArgs { virtual ~NotifyVibratorStateArgs() {} - virtual void notify(const sp<InputListenerInterface>& listener) const; + void notify(InputListenerInterface& listener) const override; }; /* * The interface used by the InputReader to notify the InputListener about input events. */ -class InputListenerInterface : public virtual RefBase { -protected: +class InputListenerInterface { +public: InputListenerInterface() { } + InputListenerInterface(const InputListenerInterface&) = delete; + InputListenerInterface& operator=(const InputListenerInterface&) = delete; virtual ~InputListenerInterface() { } -public: virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) = 0; virtual void notifyKey(const NotifyKeyArgs* args) = 0; virtual void notifyMotion(const NotifyMotionArgs* args) = 0; @@ -264,17 +264,15 @@ public: virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) = 0; }; - /* * An implementation of the listener interface that queues up and defers dispatch * of decoded events until flushed. */ class QueuedInputListener : public InputListenerInterface { -protected: - virtual ~QueuedInputListener(); public: - explicit QueuedInputListener(const sp<InputListenerInterface>& innerListener); + explicit QueuedInputListener(InputListenerInterface& innerListener); + virtual ~QueuedInputListener(); virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; virtual void notifyKey(const NotifyKeyArgs* args) override; @@ -288,7 +286,7 @@ public: void flush(); private: - sp<InputListenerInterface> mInnerListener; + InputListenerInterface& mInnerListener; std::vector<NotifyArgs*> mArgsQueue; }; diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 3c8ac1cf7b..1aab856205 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -51,12 +51,11 @@ namespace android { * The implementation must guarantee thread safety for this interface. However, since the input * listener is NOT thread safe, all calls to the listener must happen from the same thread. */ -class InputReaderInterface : public virtual RefBase { -protected: +class InputReaderInterface { +public: InputReaderInterface() { } virtual ~InputReaderInterface() { } -public: /* Dumps the state of the input reader. * * This method may be called on any thread (usually by the input manager). */ diff --git a/services/inputflinger/include/InputReaderFactory.h b/services/inputflinger/include/InputReaderFactory.h index 9db6233d28..dad14d6c92 100644 --- a/services/inputflinger/include/InputReaderFactory.h +++ b/services/inputflinger/include/InputReaderFactory.h @@ -22,8 +22,7 @@ class InputReaderInterface; class InputReaderPolicyInterface; class InputListenerInterface; -sp<InputReaderInterface> createInputReader( - const sp<InputReaderPolicyInterface>& policy, - const sp<InputListenerInterface>& listener); +std::unique_ptr<InputReaderInterface> createInputReader( + const sp<InputReaderPolicyInterface>& policy, InputListenerInterface& listener); } // namespace android diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index 85d7247915..b1069497d3 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -20,7 +20,6 @@ #include <input/DisplayViewport.h> #include <input/Input.h> #include <utils/BitSet.h> -#include <utils/RefBase.h> namespace android { 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..d07be3b025 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() { @@ -568,7 +574,7 @@ void InputDevice::bumpGeneration() { void InputDevice::notifyReset(nsecs_t when) { NotifyDeviceResetArgs args(mContext->getNextId(), when, mId); - mContext->getListener()->notifyDeviceReset(&args); + mContext->getListener().notifyDeviceReset(&args); } std::optional<int32_t> InputDevice::getAssociatedDisplayId() { diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 5120860503..0b632f7fe2 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -42,10 +42,11 @@ namespace android { InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub, const sp<InputReaderPolicyInterface>& policy, - const sp<InputListenerInterface>& listener) + InputListenerInterface& listener) : mContext(this), mEventHub(eventHub), mPolicy(policy), + mQueuedListener(listener), mGlobalMetaState(0), mLedMetaState(AMETA_NUM_LOCK_ON), mGeneration(1), @@ -53,14 +54,8 @@ InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub, mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX), mConfigurationChangesToRefresh(0) { - mQueuedListener = new QueuedInputListener(listener); - - { // acquire lock - std::scoped_lock _l(mLock); - - refreshConfigurationLocked(0); - updateGlobalMetaStateLocked(); - } // release lock + refreshConfigurationLocked(0); + updateGlobalMetaStateLocked(); } InputReader::~InputReader() {} @@ -144,7 +139,7 @@ void InputReader::loopOnce() { // resulting in a deadlock. This situation is actually quite plausible because the // listener is actually the input dispatcher, which calls into the window manager, // which occasionally calls into the input reader. - mQueuedListener->flush(); + mQueuedListener.flush(); } void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { @@ -340,7 +335,7 @@ void InputReader::handleConfigurationChangedLocked(nsecs_t when) { // Enqueue configuration changed. NotifyConfigurationChangedArgs args(mContext.getNextId(), when); - mQueuedListener->notifyConfigurationChanged(&args); + mQueuedListener.notifyConfigurationChanged(&args); } void InputReader::refreshConfigurationLocked(uint32_t changes) { @@ -374,7 +369,7 @@ void InputReader::refreshConfigurationLocked(uint32_t changes) { mCurrentPointerCaptureRequest = mConfig.pointerCaptureRequest; const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now, mCurrentPointerCaptureRequest); - mQueuedListener->notifyPointerCaptureChanged(&args); + mQueuedListener.notifyPointerCaptureChanged(&args); } } } @@ -561,6 +556,7 @@ void InputReader::toggleCapsLockState(int32_t deviceId) { } if (device->isIgnored()) { + ALOGW("Ignoring toggleCapsLock for ignored deviceId %" PRId32 ".", deviceId); return; } @@ -951,8 +947,8 @@ InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() { return mReader->mPolicy.get(); } -InputListenerInterface* InputReader::ContextImpl::getListener() { - return mReader->mQueuedListener.get(); +InputListenerInterface& InputReader::ContextImpl::getListener() { + return mReader->mQueuedListener; } EventHubInterface* InputReader::ContextImpl::getEventHub() { diff --git a/services/inputflinger/reader/InputReaderFactory.cpp b/services/inputflinger/reader/InputReaderFactory.cpp index a8971411a4..2d9ffc3ec8 100644 --- a/services/inputflinger/reader/InputReaderFactory.cpp +++ b/services/inputflinger/reader/InputReaderFactory.cpp @@ -20,9 +20,9 @@ namespace android { -sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy, - const sp<InputListenerInterface>& listener) { - return new InputReader(std::make_unique<EventHub>(), policy, listener); +std::unique_ptr<InputReaderInterface> createInputReader( + const sp<InputReaderPolicyInterface>& policy, InputListenerInterface& listener) { + return std::make_unique<InputReader>(std::make_unique<EventHub>(), policy, listener); } } // namespace android
\ No newline at end of file diff --git a/services/inputflinger/reader/Macros.h b/services/inputflinger/reader/Macros.h index 827e31a1bd..0dfe7f1c81 100644 --- a/services/inputflinger/reader/Macros.h +++ b/services/inputflinger/reader/Macros.h @@ -31,7 +31,7 @@ #define DEBUG_VIRTUAL_KEYS 0 // Log debug messages about pointers. -#define DEBUG_POINTERS 0 +static constexpr bool DEBUG_POINTERS = false; // Log debug messages about pointer assignment calculations. #define DEBUG_POINTER_ASSIGNMENT 0 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/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index e44aa0fe27..fb1d16695d 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -52,8 +52,7 @@ struct StylusState; class InputReader : public InputReaderInterface { public: InputReader(std::shared_ptr<EventHubInterface> eventHub, - const sp<InputReaderPolicyInterface>& policy, - const sp<InputListenerInterface>& listener); + const sp<InputReaderPolicyInterface>& policy, InputListenerInterface& listener); virtual ~InputReader(); void dump(std::string& dump) override; @@ -143,7 +142,7 @@ protected: void dispatchExternalStylusState(const StylusState& outState) REQUIRES(mReader->mLock) override; InputReaderPolicyInterface* getPolicy() REQUIRES(mReader->mLock) override; - InputListenerInterface* getListener() REQUIRES(mReader->mLock) override; + InputListenerInterface& getListener() REQUIRES(mReader->mLock) override; EventHubInterface* getEventHub() REQUIRES(mReader->mLock) override; int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override; void updateLedMetaState(int32_t metaState) REQUIRES(mReader->mLock) override; @@ -164,7 +163,7 @@ private: // in parallel to passing it to the InputReader. std::shared_ptr<EventHubInterface> mEventHub; sp<InputReaderPolicyInterface> mPolicy; - sp<QueuedInputListener> mQueuedListener; + QueuedInputListener mQueuedListener; InputReaderConfiguration mConfig GUARDED_BY(mLock); diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h index dc807f7886..823d160f86 100644 --- a/services/inputflinger/reader/include/InputReaderContext.h +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -55,7 +55,7 @@ public: virtual void dispatchExternalStylusState(const StylusState& outState) = 0; virtual InputReaderPolicyInterface* getPolicy() = 0; - virtual InputListenerInterface* getListener() = 0; + virtual InputListenerInterface& getListener() = 0; virtual EventHubInterface* getEventHub() = 0; virtual int32_t getNextId() = 0; diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 2ac41b1e67..f3d7cdc48b 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -176,7 +176,7 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* bumpGeneration(); if (changes) { NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId()); - getListener()->notifyDeviceReset(&args); + getListener().notifyDeviceReset(&args); } } @@ -424,7 +424,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&releaseArgs); + getListener().notifyMotion(&releaseArgs); } } @@ -434,7 +434,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); if (buttonsPressed) { BitSet32 pressed(buttonsPressed); @@ -449,7 +449,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&pressArgs); + getListener().notifyMotion(&pressArgs); } } @@ -464,7 +464,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&hoverArgs); + getListener().notifyMotion(&hoverArgs); } // Send scroll events. @@ -479,7 +479,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&scrollArgs); + getListener().notifyMotion(&scrollArgs); } } 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..f1c0e5a570 100644 --- a/services/inputflinger/reader/mapper/InputMapper.h +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -48,7 +48,7 @@ public: inline const std::string getDeviceName() { return mDeviceContext.getName(); } inline InputReaderContext* getContext() { return mDeviceContext.getContext(); } inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); } - inline InputListenerInterface* getListener() { return getContext()->getListener(); } + inline InputListenerInterface& getListener() { return getContext()->getListener(); } virtual uint32_t getSources() = 0; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); @@ -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/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp index 0dc312ecd2..6bdb121336 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp @@ -344,7 +344,7 @@ void JoystickInputMapper::sync(nsecs_t when, nsecs_t readTime, bool force) { &pointerProperties, &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 104d087387..a8602a4c02 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -354,7 +354,7 @@ void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, getDisplayId(), policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); - getListener()->notifyKey(&args); + getListener().notifyKey(&args); } ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { @@ -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/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index fab7f4ca14..4bd1cd8d12 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -95,13 +95,13 @@ void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { } if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) { -#if DEBUG_POINTERS - if (newSlot) { - ALOGW("MultiTouch device emitted invalid slot index %d but it " - "should be between 0 and %zd; ignoring this slot.", - mCurrentSlot, mSlotCount - 1); + if (DEBUG_POINTERS) { + if (newSlot) { + ALOGW("MultiTouch device emitted invalid slot index %d but it " + "should be between 0 and %zd; ignoring this slot.", + mCurrentSlot, mSlotCount - 1); + } } -#endif } else { Slot* slot = &mSlots[mCurrentSlot]; // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of @@ -273,19 +273,19 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { if (id) { outState->rawPointerData.canceledIdBits.markBit(id.value()); } -#if DEBUG_POINTERS - ALOGI("Stop processing slot %zu for it received a palm event from device %s", inIndex, - getDeviceName().c_str()); -#endif + if (DEBUG_POINTERS) { + ALOGI("Stop processing slot %zu for it received a palm event from device %s", + inIndex, getDeviceName().c_str()); + } continue; } if (outCount >= MAX_POINTERS) { -#if DEBUG_POINTERS - ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; " - "ignoring the rest.", - getDeviceName().c_str(), MAX_POINTERS); -#endif + if (DEBUG_POINTERS) { + ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; " + "ignoring the rest.", + getDeviceName().c_str(), MAX_POINTERS); + } break; // too many fingers! } diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp index e9d0189f1f..b83a8fcf4e 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -127,7 +127,7 @@ void RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) { AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); - getListener()->notifyMotion(&scrollArgs); + getListener().notifyMotion(&scrollArgs); } mRotaryEncoderScrollAccumulator.finishSync(); diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp index a507632d0e..677a372997 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 @@ -411,7 +411,7 @@ void SensorInputMapper::sync(nsecs_t when, bool force) { sensor.sensorInfo.accuracy /* accuracyChanged */, timestamp /* hwTimestamp */, values); - getListener()->notifySensor(&args); + getListener().notifySensor(&args); sensor.lastSampleTimeNs = timestamp; sensor.accuracy = sensor.sensorInfo.accuracy; } diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp index 4f736810bc..3237824994 100644 --- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp @@ -58,7 +58,7 @@ void SwitchInputMapper::sync(nsecs_t when) { uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask; NotifySwitchArgs args(getContext()->getNextId(), when, 0 /*policyFlags*/, updatedSwitchValues, mUpdatedSwitchMask); - getListener()->notifySwitch(&args); + getListener().notifySwitch(&args); mUpdatedSwitchMask = 0; } diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h index 7347b2cec4..96c4378644 100644 --- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -110,7 +110,7 @@ static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nse !(currentButtonState & buttonState))) { NotifyKeyArgs args(context->getNextId(), when, readTime, deviceId, source, displayId, policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when); - context->getListener()->notifyKey(&args); + context->getListener().notifyKey(&args); } } diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index ac5f6b652b..fd33df9347 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); @@ -398,7 +399,7 @@ void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* c // Send reset, unless this is the first time the device has been configured, // in which case the reader will call reset itself after all mappers are ready. NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId()); - getListener()->notifyDeviceReset(&args); + getListener().notifyDeviceReset(&args); } } @@ -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() { @@ -748,16 +749,17 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mPhysicalLeft = naturalPhysicalLeft; mPhysicalTop = naturalPhysicalTop; - const int32_t oldSurfaceWidth = mRawSurfaceWidth; - const int32_t oldSurfaceHeight = mRawSurfaceHeight; - mRawSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth; - mRawSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight; - mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth; - mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight; - mSurfaceRight = mSurfaceLeft + naturalLogicalWidth; - mSurfaceBottom = mSurfaceTop + naturalLogicalHeight; - if (isPerWindowInputRotationEnabled()) { + // When per-window input rotation is enabled, InputReader works in the display + // space, so the surface bounds are the bounds of the display device. + const int32_t oldSurfaceWidth = mRawSurfaceWidth; + const int32_t oldSurfaceHeight = mRawSurfaceHeight; + mRawSurfaceWidth = naturalDeviceWidth; + mRawSurfaceHeight = naturalDeviceHeight; + mSurfaceLeft = 0; + mSurfaceTop = 0; + mSurfaceRight = mRawSurfaceWidth; + mSurfaceBottom = mRawSurfaceHeight; // When per-window input rotation is enabled, InputReader works in the un-rotated // coordinate space, so we don't need to do anything if the device is already // orientation-aware. If the device is not orientation-aware, then we need to apply @@ -773,6 +775,14 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mRawSurfaceWidth == oldSurfaceWidth && mRawSurfaceHeight == oldSurfaceHeight && viewportOrientationChanged; } else { + mRawSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth; + mRawSurfaceHeight = + naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight; + mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth; + mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight; + mSurfaceRight = mSurfaceLeft + naturalLogicalWidth; + mSurfaceBottom = mSurfaceTop + naturalLogicalHeight; + mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0; } @@ -1919,7 +1929,7 @@ void TouchInputMapper::dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32 NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), AINPUT_SOURCE_KEYBOARD, mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); - getListener()->notifyKey(&args); + getListener().notifyKey(&args); } void TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { @@ -2611,7 +2621,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u metaState, buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } // Update state. @@ -3526,7 +3536,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } if (mPointerSimple.hovering && !hovering) { @@ -3540,7 +3550,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } if (down) { @@ -3556,7 +3566,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } // Send move. @@ -3567,7 +3577,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } if (hovering) { @@ -3582,7 +3592,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } // Send hover move. @@ -3593,7 +3603,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) { @@ -3615,7 +3625,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin &pointerCoords, mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } // Save state. @@ -3693,7 +3703,7 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t p MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties, pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime, std::move(frames)); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties, @@ -3771,6 +3781,12 @@ bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { const float xScaled = (x - mRawPointerAxes.x.minValue) * mXScale; const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale; + if (isPerWindowInputRotationEnabled()) { + return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue && + xScaled >= mPhysicalLeft && xScaled <= (mPhysicalLeft + mPhysicalWidth) && + y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue && + yScaled >= mPhysicalTop && yScaled <= (mPhysicalTop + mPhysicalHeight); + } return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue && xScaled >= mSurfaceLeft && xScaled <= mSurfaceRight && y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue && diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index e104220e47..3340672021 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; @@ -427,6 +435,7 @@ private: // The surface origin specifies how the surface coordinates should be translated // to align with the logical display coordinate space. + // TODO(b/188939842): Remove surface coordinates when Per-Window Input Rotation is enabled. int32_t mSurfaceLeft; int32_t mSurfaceTop; int32_t mSurfaceRight; @@ -818,4 +827,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/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp index 3df6f36db5..8c7879ba27 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp @@ -54,7 +54,7 @@ void VibratorInputMapper::vibrate(const VibrationSequence& sequence, ssize_t rep // Request InputReader to notify InputManagerService for vibration started. NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), true); - getListener()->notifyVibratorState(&args); + getListener().notifyVibratorState(&args); nextStep(); } @@ -133,7 +133,7 @@ void VibratorInputMapper::stopVibrating() { // Request InputReader to notify InputManagerService for vibration complete. NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), false); - getListener()->notifyVibratorState(&args); + getListener().notifyVibratorState(&args); } void VibratorInputMapper::dump(std::string& dump) { diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp index 3a9994eed9..f13187ddb7 100644 --- a/services/inputflinger/tests/InputClassifier_test.cpp +++ b/services/inputflinger/tests/InputClassifier_test.cpp @@ -56,18 +56,10 @@ static NotifyMotionArgs generateBasicMotionArgs() { class InputClassifierTest : public testing::Test { protected: - sp<InputClassifierInterface> mClassifier; - sp<TestInputListener> mTestListener; + TestInputListener mTestListener; + std::unique_ptr<InputClassifierInterface> mClassifier; - virtual void SetUp() override { - mTestListener = new TestInputListener(); - mClassifier = new InputClassifier(mTestListener); - } - - virtual void TearDown() override { - mClassifier.clear(); - mTestListener.clear(); - } + void SetUp() override { mClassifier = std::make_unique<InputClassifier>(mTestListener); } }; /** @@ -80,7 +72,7 @@ TEST_F(InputClassifierTest, SendToNextStage_NotifyConfigurationChangedArgs) { mClassifier->notifyConfigurationChanged(&args); NotifyConfigurationChangedArgs outArgs; - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled(&outArgs)); + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyConfigurationChangedWasCalled(&outArgs)); ASSERT_EQ(args, outArgs); } @@ -93,7 +85,7 @@ TEST_F(InputClassifierTest, SendToNextStage_NotifyKeyArgs) { mClassifier->notifyKey(&args); NotifyKeyArgs outArgs; - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&outArgs)); + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyKeyWasCalled(&outArgs)); ASSERT_EQ(args, outArgs); } @@ -106,7 +98,7 @@ TEST_F(InputClassifierTest, SendToNextStage_NotifyMotionArgs) { NotifyMotionArgs motionArgs = generateBasicMotionArgs(); mClassifier->notifyMotion(&motionArgs); NotifyMotionArgs args; - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyMotionWasCalled(&args)); ASSERT_EQ(motionArgs, args); } @@ -120,7 +112,7 @@ TEST_F(InputClassifierTest, SendToNextStage_NotifySwitchArgs) { mClassifier->notifySwitch(&args); NotifySwitchArgs outArgs; - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifySwitchWasCalled(&outArgs)); + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifySwitchWasCalled(&outArgs)); ASSERT_EQ(args, outArgs); } @@ -133,7 +125,7 @@ TEST_F(InputClassifierTest, SendToNextStage_NotifyDeviceResetArgs) { mClassifier->notifyDeviceReset(&args); NotifyDeviceResetArgs outArgs; - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyDeviceResetWasCalled(&outArgs)); + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyDeviceResetWasCalled(&outArgs)); ASSERT_EQ(args, outArgs); } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 471c5f93f4..84e427673e 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 { @@ -85,13 +92,29 @@ public: FakeInputDispatcherPolicy() {} void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) { - assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_KEY, args.eventTime, args.action, - args.displayId); + assertFilterInputEventWasCalledInternal([&args](const InputEvent& event) { + ASSERT_EQ(event.getType(), AINPUT_EVENT_TYPE_KEY); + EXPECT_EQ(event.getDisplayId(), args.displayId); + + const auto& keyEvent = static_cast<const KeyEvent&>(event); + EXPECT_EQ(keyEvent.getEventTime(), args.eventTime); + EXPECT_EQ(keyEvent.getAction(), args.action); + }); } - void assertFilterInputEventWasCalled(const NotifyMotionArgs& args) { - assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_MOTION, args.eventTime, args.action, - args.displayId); + void assertFilterInputEventWasCalled(const NotifyMotionArgs& args, vec2 point) { + assertFilterInputEventWasCalledInternal([&](const InputEvent& event) { + ASSERT_EQ(event.getType(), AINPUT_EVENT_TYPE_MOTION); + EXPECT_EQ(event.getDisplayId(), args.displayId); + + const auto& motionEvent = static_cast<const MotionEvent&>(event); + EXPECT_EQ(motionEvent.getEventTime(), args.eventTime); + EXPECT_EQ(motionEvent.getAction(), args.action); + EXPECT_EQ(motionEvent.getX(0), point.x); + EXPECT_EQ(motionEvent.getY(0), point.y); + EXPECT_EQ(motionEvent.getRawX(0), point.x); + EXPECT_EQ(motionEvent.getRawY(0), point.y); + }); } void assertFilterInputEventWasNotCalled() { @@ -418,26 +441,11 @@ private: mDropTargetWindowToken = token; } - void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action, - int32_t displayId) { + void assertFilterInputEventWasCalledInternal( + const std::function<void(const InputEvent&)>& verify) { std::scoped_lock lock(mLock); ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called."; - ASSERT_EQ(mFilteredEvent->getType(), type); - - if (type == AINPUT_EVENT_TYPE_KEY) { - const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*mFilteredEvent); - EXPECT_EQ(keyEvent.getEventTime(), eventTime); - EXPECT_EQ(keyEvent.getAction(), action); - EXPECT_EQ(keyEvent.getDisplayId(), displayId); - } else if (type == AINPUT_EVENT_TYPE_MOTION) { - const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*mFilteredEvent); - EXPECT_EQ(motionEvent.getEventTime(), eventTime); - EXPECT_EQ(motionEvent.getAction(), action); - EXPECT_EQ(motionEvent.getDisplayId(), displayId); - } else { - FAIL() << "Unknown type: " << type; - } - + verify(*mFilteredEvent); mFilteredEvent = nullptr; } }; @@ -447,11 +455,11 @@ private: class InputDispatcherTest : public testing::Test { protected: sp<FakeInputDispatcherPolicy> mFakePolicy; - sp<InputDispatcher> mDispatcher; + std::unique_ptr<InputDispatcher> mDispatcher; void SetUp() override { mFakePolicy = new FakeInputDispatcherPolicy(); - mDispatcher = new InputDispatcher(mFakePolicy); + mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy); mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); // Start InputDispatcher thread ASSERT_EQ(OK, mDispatcher->start()); @@ -460,7 +468,7 @@ protected: void TearDown() override { ASSERT_EQ(OK, mDispatcher->stop()); mFakePolicy.clear(); - mDispatcher.clear(); + mDispatcher.reset(); } /** @@ -535,8 +543,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME, + ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -549,8 +557,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, - ARBITRARY_TIME, ARBITRARY_TIME, + identityTransform, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -562,8 +569,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, - ARBITRARY_TIME, ARBITRARY_TIME, + identityTransform, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -576,8 +582,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, - ARBITRARY_TIME, ARBITRARY_TIME, + identityTransform, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -589,8 +594,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, - ARBITRARY_TIME, ARBITRARY_TIME, + identityTransform, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -601,8 +605,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME, + ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -612,8 +616,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME, + ARBITRARY_TIME, /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -625,8 +629,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME, + ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -637,8 +641,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME, + ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -651,8 +655,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME, + ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -683,7 +687,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 +804,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()); } @@ -906,7 +915,7 @@ public: static const int32_t HEIGHT = 800; FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle, - const sp<InputDispatcher>& dispatcher, const std::string name, + const std::unique_ptr<InputDispatcher>& dispatcher, const std::string name, int32_t displayId, std::optional<sp<IBinder>> token = std::nullopt) : mName(name) { if (token == std::nullopt) { @@ -944,7 +953,7 @@ public: sp<FakeWindowHandle> clone( const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle, - const sp<InputDispatcher>& dispatcher, int32_t displayId) { + const std::unique_ptr<InputDispatcher>& dispatcher, int32_t displayId) { sp<FakeWindowHandle> handle = new FakeWindowHandle(inputApplicationHandle, dispatcher, mInfo.name + "(Mirror)", displayId, mInfo.token); @@ -967,16 +976,24 @@ public: void setApplicationToken(sp<IBinder> token) { mInfo.applicationInfo.token = token; } - void setFrame(const Rect& frame) { + void setFrame(const Rect& frame, const ui::Transform& displayTransform = ui::Transform()) { mInfo.frameLeft = frame.left; mInfo.frameTop = frame.top; mInfo.frameRight = frame.right; mInfo.frameBottom = frame.bottom; - mInfo.transform.set(-frame.left, -frame.top); mInfo.touchableRegion.clear(); mInfo.addTouchableRegion(frame); + + const Rect logicalDisplayFrame = displayTransform.transform(frame); + ui::Transform translate; + translate.set(-logicalDisplayFrame.left, -logicalDisplayFrame.top); + mInfo.transform = translate * displayTransform; } + 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; } @@ -1141,7 +1158,7 @@ private: std::atomic<int32_t> FakeWindowHandle::sId{1}; static InputEventInjectionResult injectKey( - const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount, + const std::unique_ptr<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount, int32_t displayId = ADISPLAY_ID_NONE, InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT, std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT, @@ -1163,7 +1180,7 @@ static InputEventInjectionResult injectKey( injectionTimeout, policyFlags); } -static InputEventInjectionResult injectKeyDown(const sp<InputDispatcher>& dispatcher, +static InputEventInjectionResult injectKeyDown(const std::unique_ptr<InputDispatcher>& dispatcher, int32_t displayId = ADISPLAY_ID_NONE) { return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId); } @@ -1171,14 +1188,14 @@ static InputEventInjectionResult injectKeyDown(const sp<InputDispatcher>& dispat // Inject a down event that has key repeat disabled. This allows InputDispatcher to idle without // sending a subsequent key up. When key repeat is enabled, the dispatcher cannot idle because it // has to be woken up to process the repeating key. -static InputEventInjectionResult injectKeyDownNoRepeat(const sp<InputDispatcher>& dispatcher, - int32_t displayId = ADISPLAY_ID_NONE) { +static InputEventInjectionResult injectKeyDownNoRepeat( + const std::unique_ptr<InputDispatcher>& dispatcher, int32_t displayId = ADISPLAY_ID_NONE) { return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId, InputEventInjectionSync::WAIT_FOR_RESULT, INJECT_EVENT_TIMEOUT, /* allowKeyRepeat */ false); } -static InputEventInjectionResult injectKeyUp(const sp<InputDispatcher>& dispatcher, +static InputEventInjectionResult injectKeyUp(const std::unique_ptr<InputDispatcher>& dispatcher, int32_t displayId = ADISPLAY_ID_NONE) { return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId); } @@ -1280,9 +1297,8 @@ public: mAction, mActionButton, mFlags, /* edgeFlags */ 0, AMETA_NONE, mButtonState, MotionClassification::NONE, identityTransform, /* xPrecision */ 0, /* yPrecision */ 0, mRawXCursorPosition, - mRawYCursorPosition, mDisplayOrientation, mDisplayWidth, mDisplayHeight, - mEventTime, mEventTime, mPointers.size(), pointerProperties.data(), - pointerCoords.data()); + mRawYCursorPosition, identityTransform, mEventTime, mEventTime, + mPointers.size(), pointerProperties.data(), pointerCoords.data()); return event; } @@ -1297,15 +1313,12 @@ private: int32_t mFlags{0}; float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION}; float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION}; - uint32_t mDisplayOrientation{ui::Transform::ROT_0}; - int32_t mDisplayWidth{INVALID_DISPLAY_SIZE}; - int32_t mDisplayHeight{INVALID_DISPLAY_SIZE}; std::vector<PointerBuilder> mPointers; }; static InputEventInjectionResult injectMotionEvent( - const sp<InputDispatcher>& dispatcher, const MotionEvent& event, + const std::unique_ptr<InputDispatcher>& dispatcher, const MotionEvent& event, std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT, InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT) { return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode, @@ -1314,8 +1327,8 @@ static InputEventInjectionResult injectMotionEvent( } static InputEventInjectionResult injectMotionEvent( - const sp<InputDispatcher>& dispatcher, int32_t action, int32_t source, int32_t displayId, - const PointF& position, + const std::unique_ptr<InputDispatcher>& dispatcher, int32_t action, int32_t source, + int32_t displayId, const PointF& position, const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION}, std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT, @@ -1335,13 +1348,13 @@ static InputEventInjectionResult injectMotionEvent( return injectMotionEvent(dispatcher, event, injectionTimeout, injectionMode); } -static InputEventInjectionResult injectMotionDown(const sp<InputDispatcher>& dispatcher, - int32_t source, int32_t displayId, - const PointF& location = {100, 200}) { +static InputEventInjectionResult injectMotionDown( + const std::unique_ptr<InputDispatcher>& dispatcher, int32_t source, int32_t displayId, + const PointF& location = {100, 200}) { return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location); } -static InputEventInjectionResult injectMotionUp(const sp<InputDispatcher>& dispatcher, +static InputEventInjectionResult injectMotionUp(const std::unique_ptr<InputDispatcher>& dispatcher, int32_t source, int32_t displayId, const PointF& location = {100, 200}) { return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location); @@ -1413,6 +1426,21 @@ TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) { window->consumeMotionDown(ADISPLAY_ID_DEFAULT); } +TEST_F(InputDispatcherTest, WhenDisplayNotSpecified_InjectMotionToDefaultDisplay) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + // Inject a MotionEvent to an unknown display. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_NONE)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Window should receive motion event. + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); +} + /** * Calling setInputWindows once with FLAG_NOT_TOUCH_MODAL should not cause any issues. * To ensure that window receives only events that were directly inside of it, add @@ -1480,6 +1508,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 = @@ -1745,8 +1964,121 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { 0 /*expectedFlags*/); } -using TransferFunction = - std::function<bool(sp<InputDispatcher> dispatcher, sp<IBinder>, sp<IBinder>)>; +/** + * Ensure the correct coordinate spaces are used by InputDispatcher. + * + * InputDispatcher works in the display space, so its coordinate system is relative to the display + * panel. Windows get events in the window space, and get raw coordinates in the logical display + * space. + */ +class InputDispatcherDisplayProjectionTest : public InputDispatcherTest { +public: + void SetUp() override { + InputDispatcherTest::SetUp(); + mDisplayInfos.clear(); + mWindowInfos.clear(); + } + + void addDisplayInfo(int displayId, const ui::Transform& transform) { + gui::DisplayInfo info; + info.displayId = displayId; + info.transform = transform; + mDisplayInfos.push_back(std::move(info)); + mDispatcher->onWindowInfosChanged(mWindowInfos, mDisplayInfos); + } + + void addWindow(const sp<WindowInfoHandle>& windowHandle) { + mWindowInfos.push_back(*windowHandle->getInfo()); + mDispatcher->onWindowInfosChanged(mWindowInfos, mDisplayInfos); + } + + // Set up a test scenario where the display has a scaled projection and there are two windows + // on the display. + std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupScaledDisplayScenario() { + // The display has a projection that has a scale factor of 2 and 4 in the x and y directions + // respectively. + ui::Transform displayTransform; + displayTransform.set(2, 0, 0, 4); + addDisplayInfo(ADISPLAY_ID_DEFAULT, displayTransform); + + std::shared_ptr<FakeApplicationHandle> application = + std::make_shared<FakeApplicationHandle>(); + + // Add two windows to the display. Their frames are represented in the display space. + sp<FakeWindowHandle> firstWindow = + new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT); + firstWindow->addFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + firstWindow->setFrame(Rect(0, 0, 100, 200), displayTransform); + addWindow(firstWindow); + + sp<FakeWindowHandle> secondWindow = + new FakeWindowHandle(application, mDispatcher, "Second Window", + ADISPLAY_ID_DEFAULT); + secondWindow->addFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + secondWindow->setFrame(Rect(100, 200, 200, 400), displayTransform); + addWindow(secondWindow); + return {std::move(firstWindow), std::move(secondWindow)}; + } + +private: + std::vector<gui::DisplayInfo> mDisplayInfos; + std::vector<gui::WindowInfo> mWindowInfos; +}; + +TEST_F(InputDispatcherDisplayProjectionTest, HitTestsInDisplaySpace) { + auto [firstWindow, secondWindow] = setupScaledDisplayScenario(); + // Send down to the first window. The point is represented in the display space. The point is + // selected so that if the hit test was done with the transform applied to it, then it would + // end up in the incorrect window. + NotifyMotionArgs downMotionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {PointF{75, 55}}); + mDispatcher->notifyMotion(&downMotionArgs); + + firstWindow->consumeMotionDown(); + secondWindow->assertNoEvents(); +} + +// Ensure that when a MotionEvent is injected through the InputDispatcher::injectInputEvent() API, +// the event should be treated as being in the logical display space. +TEST_F(InputDispatcherDisplayProjectionTest, InjectionInLogicalDisplaySpace) { + auto [firstWindow, secondWindow] = setupScaledDisplayScenario(); + // Send down to the first window. The point is represented in the logical display space. The + // point is selected so that if the hit test was done in logical display space, then it would + // end up in the incorrect window. + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + PointF{75 * 2, 55 * 4}); + + firstWindow->consumeMotionDown(); + secondWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherDisplayProjectionTest, WindowGetsEventsInCorrectCoordinateSpace) { + auto [firstWindow, secondWindow] = setupScaledDisplayScenario(); + + // Send down to the second window. + NotifyMotionArgs downMotionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {PointF{150, 220}}); + mDispatcher->notifyMotion(&downMotionArgs); + + firstWindow->assertNoEvents(); + const MotionEvent* event = secondWindow->consumeMotion(); + EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event->getAction()); + + // Ensure that the events from the "getRaw" API are in logical display coordinates. + EXPECT_EQ(300, event->getRawX(0)); + EXPECT_EQ(880, event->getRawY(0)); + + // Ensure that the x and y values are in the window's coordinate space. + // The left-top of the second window is at (100, 200) in display space, which is (200, 800) in + // the logical display space. This will be the origin of the window space. + EXPECT_EQ(100, event->getX(0)); + EXPECT_EQ(80, event->getY(0)); +} + +using TransferFunction = std::function<bool(const std::unique_ptr<InputDispatcher>& dispatcher, + sp<IBinder>, sp<IBinder>)>; class TransferTouchFixture : public InputDispatcherTest, public ::testing::WithParamInterface<TransferFunction> {}; @@ -1859,12 +2191,12 @@ TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) { // for the case where there are multiple pointers split across several windows. INSTANTIATE_TEST_SUITE_P(TransferFunctionTests, TransferTouchFixture, ::testing::Values( - [&](sp<InputDispatcher> dispatcher, sp<IBinder> /*ignored*/, - sp<IBinder> destChannelToken) { + [&](const std::unique_ptr<InputDispatcher>& dispatcher, + sp<IBinder> /*ignored*/, sp<IBinder> destChannelToken) { return dispatcher->transferTouch(destChannelToken); }, - [&](sp<InputDispatcher> dispatcher, sp<IBinder> from, - sp<IBinder> to) { + [&](const std::unique_ptr<InputDispatcher>& dispatcher, + sp<IBinder> from, sp<IBinder> to) { return dispatcher->transferTouchFocus(from, to, false /*isDragAndDrop*/); })); @@ -2273,7 +2605,7 @@ TEST_F(InputDispatcherTest, SendTimeline_DoesNotCrashDispatcher) { class FakeMonitorReceiver { public: - FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name, + FakeMonitorReceiver(const std::unique_ptr<InputDispatcher>& dispatcher, const std::string name, int32_t displayId, bool isGestureMonitor = false) { base::Result<std::unique_ptr<InputChannel>> channel = dispatcher->createInputMonitor(displayId, isGestureMonitor, name, MONITOR_PID); @@ -2296,11 +2628,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 +2662,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>(); @@ -2540,7 +2933,14 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + ui::Transform transform; + transform.set({1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0, 0, 1}); + + gui::DisplayInfo displayInfo; + displayInfo.displayId = ADISPLAY_ID_DEFAULT; + displayInfo.transform = transform; + + mDispatcher->onWindowInfosChanged({*window->getInfo()}, {displayInfo}); NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, @@ -2561,8 +2961,11 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { const VerifiedMotionEvent& verifiedMotion = static_cast<const VerifiedMotionEvent&>(*verified); - EXPECT_EQ(motionArgs.pointerCoords[0].getX(), verifiedMotion.rawX); - EXPECT_EQ(motionArgs.pointerCoords[0].getY(), verifiedMotion.rawY); + const vec2 rawXY = + MotionEvent::calculateTransformedXY(motionArgs.source, transform, + motionArgs.pointerCoords[0].getXYValue()); + EXPECT_EQ(rawXY.x, verifiedMotion.rawX); + EXPECT_EQ(rawXY.y, verifiedMotion.rawY); EXPECT_EQ(motionArgs.action & AMOTION_EVENT_ACTION_MASK, verifiedMotion.actionMasked); EXPECT_EQ(motionArgs.downTime, verifiedMotion.downTimeNanos); EXPECT_EQ(motionArgs.flags & VERIFIED_MOTION_EVENT_FLAGS, verifiedMotion.flags); @@ -2918,7 +3321,7 @@ protected: virtual void SetUp() override { mFakePolicy = new FakeInputDispatcherPolicy(); mFakePolicy->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY); - mDispatcher = new InputDispatcher(mFakePolicy); + mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy); mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); ASSERT_EQ(OK, mDispatcher->start()); @@ -3221,7 +3624,8 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CanFocusWindowOnUnfocusedDisplay) class InputFilterTest : public InputDispatcherTest { protected: - void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) { + void testNotifyMotion(int32_t displayId, bool expectToBeFiltered, + const ui::Transform& transform = ui::Transform()) { NotifyMotionArgs motionArgs; motionArgs = @@ -3232,7 +3636,8 @@ protected: mDispatcher->notifyMotion(&motionArgs); ASSERT_TRUE(mDispatcher->waitForIdle()); if (expectToBeFiltered) { - mFakePolicy->assertFilterInputEventWasCalled(motionArgs); + const auto xy = transform.transform(motionArgs.pointerCoords->getXYValue()); + mFakePolicy->assertFilterInputEventWasCalled(motionArgs, xy); } else { mFakePolicy->assertFilterInputEventWasNotCalled(); } @@ -3290,6 +3695,30 @@ TEST_F(InputFilterTest, KeyEvent_InputFilter) { testNotifyKey(/*expectToBeFiltered*/ false); } +// Ensure that MotionEvents sent to the InputFilter through InputListener are converted to the +// logical display coordinate space. +TEST_F(InputFilterTest, MotionEvent_UsesLogicalDisplayCoordinates_notifyMotion) { + ui::Transform firstDisplayTransform; + firstDisplayTransform.set({1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0, 0, 1}); + ui::Transform secondDisplayTransform; + secondDisplayTransform.set({-6.6, -5.5, -4.4, -3.3, -2.2, -1.1, 0, 0, 1}); + + std::vector<gui::DisplayInfo> displayInfos(2); + displayInfos[0].displayId = ADISPLAY_ID_DEFAULT; + displayInfos[0].transform = firstDisplayTransform; + displayInfos[1].displayId = SECOND_DISPLAY_ID; + displayInfos[1].transform = secondDisplayTransform; + + mDispatcher->onWindowInfosChanged({}, displayInfos); + + // Enable InputFilter + mDispatcher->setInputFilterEnabled(true); + + // Ensure the correct transforms are used for the displays. + testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ true, firstDisplayTransform); + testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ true, secondDisplayTransform); +} + class InputFilterInjectionPolicyTest : public InputDispatcherTest { protected: virtual void SetUp() override { @@ -3354,8 +3783,7 @@ protected: DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - 0 /*INVALID_DISPLAY_SIZE*/, 0 /*INVALID_DISPLAY_SIZE*/, eventTime, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, eventTime, eventTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); @@ -3562,7 +3990,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; diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index b419d9ab3d..53b03ada3a 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; @@ -250,14 +250,35 @@ public: return mConfig.getDisplayViewportByPort(displayPort); } + void addDisplayViewport(DisplayViewport viewport) { + mViewports.push_back(std::move(viewport)); + mConfig.setDisplayViewports(mViewports); + } + void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation, bool isActive, const std::string& uniqueId, - std::optional<uint8_t> physicalPort, ViewportType viewportType) { - const DisplayViewport viewport = - createDisplayViewport(displayId, width, height, orientation, isActive, uniqueId, - physicalPort, viewportType); - mViewports.push_back(viewport); - mConfig.setDisplayViewports(mViewports); + std::optional<uint8_t> physicalPort, ViewportType type) { + const bool isRotated = + (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270); + DisplayViewport v; + v.displayId = displayId; + v.orientation = orientation; + v.logicalLeft = 0; + v.logicalTop = 0; + v.logicalRight = isRotated ? height : width; + v.logicalBottom = isRotated ? width : height; + v.physicalLeft = 0; + v.physicalTop = 0; + v.physicalRight = isRotated ? height : width; + v.physicalBottom = isRotated ? width : height; + v.deviceWidth = isRotated ? height : width; + v.deviceHeight = isRotated ? width : height; + v.isActive = isActive; + v.uniqueId = uniqueId; + v.physicalPort = physicalPort; + v.type = type; + + addDisplayViewport(v); } bool updateViewport(const DisplayViewport& viewport) { @@ -291,8 +312,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 { @@ -330,38 +351,13 @@ public: private: uint32_t mNextPointerCaptureSequenceNumber = 0; - DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height, - int32_t orientation, bool isActive, - const std::string& uniqueId, - std::optional<uint8_t> physicalPort, ViewportType type) { - bool isRotated = (orientation == DISPLAY_ORIENTATION_90 - || orientation == DISPLAY_ORIENTATION_270); - DisplayViewport v; - v.displayId = displayId; - v.orientation = orientation; - v.logicalLeft = 0; - v.logicalTop = 0; - v.logicalRight = isRotated ? height : width; - v.logicalBottom = isRotated ? width : height; - v.physicalLeft = 0; - v.physicalTop = 0; - v.physicalRight = isRotated ? height : width; - v.physicalBottom = isRotated ? width : height; - v.deviceWidth = isRotated ? height : width; - v.deviceHeight = isRotated ? width : height; - v.isActive = isActive; - v.uniqueId = uniqueId; - v.physicalPort = physicalPort; - v.type = type; - return v; - } - void getReaderConfiguration(InputReaderConfiguration* outConfig) override { *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 +869,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; @@ -1172,7 +1186,7 @@ class InstrumentedInputReader : public InputReader { public: InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub, const sp<InputReaderPolicyInterface>& policy, - const sp<InputListenerInterface>& listener) + InputListenerInterface& listener) : InputReader(eventHub, policy, listener), mFakeContext(this) {} virtual ~InstrumentedInputReader() {} @@ -1485,7 +1499,7 @@ TEST_F(InputReaderPolicyTest, Viewports_GetByPort) { class InputReaderTest : public testing::Test { protected: - sp<TestInputListener> mFakeListener; + std::unique_ptr<TestInputListener> mFakeListener; sp<FakeInputReaderPolicy> mFakePolicy; std::shared_ptr<FakeEventHub> mFakeEventHub; std::unique_ptr<InstrumentedInputReader> mReader; @@ -1493,14 +1507,14 @@ protected: void SetUp() override { mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = new FakeInputReaderPolicy(); - mFakeListener = new TestInputListener(); + mFakeListener = std::make_unique<TestInputListener>(); mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, - mFakeListener); + *mFakeListener); } void TearDown() override { - mFakeListener.clear(); + mFakeListener.reset(); mFakePolicy.clear(); } @@ -2131,16 +2145,21 @@ TEST_F(InputReaderTest, LightGetColor) { // the tests to fail. class InputReaderIntegrationTest : public testing::Test { protected: - sp<TestInputListener> mTestListener; + std::unique_ptr<TestInputListener> mTestListener; sp<FakeInputReaderPolicy> mFakePolicy; - sp<InputReaderInterface> mReader; + std::unique_ptr<InputReaderInterface> mReader; + + std::shared_ptr<FakePointerController> mFakePointerController; void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); - mTestListener = new TestInputListener(2000ms /*eventHappenedTimeout*/, - 30ms /*eventDidNotHappenTimeout*/); + mFakePointerController = std::make_shared<FakePointerController>(); + mFakePolicy->setPointerController(mFakePointerController); + mTestListener = std::make_unique<TestInputListener>(2000ms /*eventHappenedTimeout*/, + 30ms /*eventDidNotHappenTimeout*/); - mReader = new InputReader(std::make_shared<EventHub>(), mFakePolicy, mTestListener); + mReader = std::make_unique<InputReader>(std::make_shared<EventHub>(), mFakePolicy, + *mTestListener); ASSERT_EQ(mReader->start(), OK); // Since this test is run on a real device, all the input devices connected @@ -2152,7 +2171,8 @@ protected: void TearDown() override { ASSERT_EQ(mReader->stop(), OK); - mTestListener.clear(); + mReader.reset(); + mTestListener.reset(); mFakePolicy.clear(); } }; @@ -2406,16 +2426,16 @@ protected: std::shared_ptr<FakeEventHub> mFakeEventHub; sp<FakeInputReaderPolicy> mFakePolicy; - sp<TestInputListener> mFakeListener; + std::unique_ptr<TestInputListener> mFakeListener; std::unique_ptr<InstrumentedInputReader> mReader; std::shared_ptr<InputDevice> mDevice; void SetUp() override { mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = new FakeInputReaderPolicy(); - mFakeListener = new TestInputListener(); + mFakeListener = std::make_unique<TestInputListener>(); mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, - mFakeListener); + *mFakeListener); InputDeviceIdentifier identifier; identifier.name = DEVICE_NAME; identifier.location = DEVICE_LOCATION; @@ -2427,7 +2447,7 @@ protected: } void TearDown() override { - mFakeListener.clear(); + mFakeListener.reset(); mFakePolicy.clear(); } }; @@ -2679,16 +2699,16 @@ protected: std::shared_ptr<FakeEventHub> mFakeEventHub; sp<FakeInputReaderPolicy> mFakePolicy; - sp<TestInputListener> mFakeListener; + std::unique_ptr<TestInputListener> mFakeListener; std::unique_ptr<InstrumentedInputReader> mReader; std::shared_ptr<InputDevice> mDevice; virtual void SetUp(Flags<InputDeviceClass> classes) { mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = new FakeInputReaderPolicy(); - mFakeListener = new TestInputListener(); + mFakeListener = std::make_unique<TestInputListener>(); mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, - mFakeListener); + *mFakeListener); mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes); } @@ -2700,7 +2720,7 @@ protected: } void TearDown() override { - mFakeListener.clear(); + mFakeListener.reset(); mFakePolicy.clear(); sysprop::InputFlingerProperties::per_window_input_rotation( @@ -3732,6 +3752,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 +3867,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, @@ -6254,6 +6293,172 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenAbsPressureIsPresent_HoversIfItsV toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); } +// --- TouchDisplayProjectionTest --- + +class TouchDisplayProjectionTest : public SingleTouchInputMapperTest { +public: + // The values inside DisplayViewport are expected to be pre-rotated. This updates the current + // DisplayViewport to pre-rotate the values. The viewport's physical display will be set to the + // rotated equivalent of the given un-rotated physical display bounds. + void configurePhysicalDisplay(int32_t orientation, Rect naturalPhysicalDisplay) { + uint32_t inverseRotationFlags; + auto width = DISPLAY_WIDTH; + auto height = DISPLAY_HEIGHT; + switch (orientation) { + case DISPLAY_ORIENTATION_90: + inverseRotationFlags = ui::Transform::ROT_270; + std::swap(width, height); + break; + case DISPLAY_ORIENTATION_180: + inverseRotationFlags = ui::Transform::ROT_180; + break; + case DISPLAY_ORIENTATION_270: + inverseRotationFlags = ui::Transform::ROT_90; + std::swap(width, height); + break; + case DISPLAY_ORIENTATION_0: + inverseRotationFlags = ui::Transform::ROT_0; + break; + default: + FAIL() << "Invalid orientation: " << orientation; + } + + const ui::Transform rotation(inverseRotationFlags, width, height); + const Rect rotatedPhysicalDisplay = rotation.transform(naturalPhysicalDisplay); + + std::optional<DisplayViewport> internalViewport = + *mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); + DisplayViewport& v = *internalViewport; + v.displayId = DISPLAY_ID; + v.orientation = orientation; + + v.logicalLeft = 0; + v.logicalTop = 0; + v.logicalRight = 100; + v.logicalBottom = 100; + + v.physicalLeft = rotatedPhysicalDisplay.left; + v.physicalTop = rotatedPhysicalDisplay.top; + v.physicalRight = rotatedPhysicalDisplay.right; + v.physicalBottom = rotatedPhysicalDisplay.bottom; + + v.deviceWidth = width; + v.deviceHeight = height; + + v.isActive = true; + v.uniqueId = UNIQUE_ID; + v.type = ViewportType::INTERNAL; + mFakePolicy->updateViewport(v); + configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); + } + + void assertReceivedMove(const Point& point) { + NotifyMotionArgs motionArgs; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + ASSERT_EQ(size_t(1), motionArgs.pointerCount); + ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], point.x, point.y, + 1, 0, 0, 0, 0, 0, 0, 0)); + } +}; + +TEST_F(TouchDisplayProjectionTest, IgnoresTouchesOutsidePhysicalDisplay) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + + prepareButtons(); + prepareAxes(POSITION); + SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + + NotifyMotionArgs motionArgs; + + // Configure the DisplayViewport such that the logical display maps to a subsection of + // the display panel called the physical display. Here, the physical display is bounded by the + // points (10, 20) and (70, 160) inside the display space, which is of the size 400 x 800. + static const Rect kPhysicalDisplay{10, 20, 70, 160}; + static const std::array<Point, 6> kPointsOutsidePhysicalDisplay{ + {{-10, -10}, {0, 0}, {5, 100}, {50, 15}, {75, 100}, {50, 165}}}; + + for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180, + DISPLAY_ORIENTATION_270}) { + configurePhysicalDisplay(orientation, kPhysicalDisplay); + + // Touches outside the physical display should be ignored, and should not generate any + // events. Ensure touches at the following points that lie outside of the physical display + // area do not generate any events. + for (const auto& point : kPointsOutsidePhysicalDisplay) { + processDown(mapper, toRawX(point.x), toRawY(point.y)); + processSync(mapper); + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()) + << "Unexpected event generated for touch outside physical display at point: " + << point.x << ", " << point.y; + } + } +} + +TEST_F(TouchDisplayProjectionTest, EmitsTouchDownAfterEnteringPhysicalDisplay) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + + prepareButtons(); + prepareAxes(POSITION); + SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + + NotifyMotionArgs motionArgs; + + // Configure the DisplayViewport such that the logical display maps to a subsection of + // the display panel called the physical display. Here, the physical display is bounded by the + // points (10, 20) and (70, 160) inside the display space, which is of the size 400 x 800. + static const Rect kPhysicalDisplay{10, 20, 70, 160}; + + for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180, + DISPLAY_ORIENTATION_270}) { + configurePhysicalDisplay(orientation, kPhysicalDisplay); + + // Touches that start outside the physical display should be ignored until it enters the + // physical display bounds, at which point it should generate a down event. Start a touch at + // the point (5, 100), which is outside the physical display bounds. + static const Point kOutsidePoint{5, 100}; + processDown(mapper, toRawX(kOutsidePoint.x), toRawY(kOutsidePoint.y)); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + + // Move the touch into the physical display area. This should generate a pointer down. + processMove(mapper, toRawX(11), toRawY(21)); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(size_t(1), motionArgs.pointerCount); + ASSERT_NO_FATAL_FAILURE( + assertPointerCoords(motionArgs.pointerCoords[0], 11, 21, 1, 0, 0, 0, 0, 0, 0, 0)); + + // Move the touch inside the physical display area. This should generate a pointer move. + processMove(mapper, toRawX(69), toRawY(159)); + processSync(mapper); + assertReceivedMove({69, 159}); + + // Move outside the physical display area. Since the pointer is already down, this should + // now continue generating events. + processMove(mapper, toRawX(kOutsidePoint.x), toRawY(kOutsidePoint.y)); + processSync(mapper); + assertReceivedMove(kOutsidePoint); + + // Release. This should generate a pointer up. + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); + ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], kOutsidePoint.x, + kOutsidePoint.y, 1, 0, 0, 0, 0, 0, 0, 0)); + + // Ensure no more events were generated. + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + } +} + // --- MultiTouchInputMapperTest --- class MultiTouchInputMapperTest : public TouchInputMapperTest { @@ -7793,7 +7998,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 +8151,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; @@ -8588,173 +8792,6 @@ TEST_F(MultiTouchInputMapperTest_ExternalDevice, Viewports_Fallback) { ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId); } -/** - * Test touch should not work if outside of surface. - */ -class MultiTouchInputMapperTest_SurfaceRange : public MultiTouchInputMapperTest { -protected: - void halfDisplayToCenterHorizontal(int32_t orientation) { - std::optional<DisplayViewport> internalViewport = - mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); - - // Half display to (width/4, 0, width * 3/4, height) to make display has offset. - internalViewport->orientation = orientation; - if (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270) { - internalViewport->logicalLeft = 0; - internalViewport->logicalTop = 0; - internalViewport->logicalRight = DISPLAY_HEIGHT; - internalViewport->logicalBottom = DISPLAY_WIDTH / 2; - - internalViewport->physicalLeft = 0; - internalViewport->physicalTop = DISPLAY_WIDTH / 4; - internalViewport->physicalRight = DISPLAY_HEIGHT; - internalViewport->physicalBottom = DISPLAY_WIDTH * 3 / 4; - - internalViewport->deviceWidth = DISPLAY_HEIGHT; - internalViewport->deviceHeight = DISPLAY_WIDTH; - } else { - internalViewport->logicalLeft = 0; - internalViewport->logicalTop = 0; - internalViewport->logicalRight = DISPLAY_WIDTH / 2; - internalViewport->logicalBottom = DISPLAY_HEIGHT; - - internalViewport->physicalLeft = DISPLAY_WIDTH / 4; - internalViewport->physicalTop = 0; - internalViewport->physicalRight = DISPLAY_WIDTH * 3 / 4; - internalViewport->physicalBottom = DISPLAY_HEIGHT; - - internalViewport->deviceWidth = DISPLAY_WIDTH; - internalViewport->deviceHeight = DISPLAY_HEIGHT; - } - - mFakePolicy->updateViewport(internalViewport.value()); - configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); - } - - void processPositionAndVerify(MultiTouchInputMapper& mapper, int32_t xOutside, int32_t yOutside, - int32_t xInside, int32_t yInside, int32_t xExpected, - int32_t yExpected) { - // touch on outside area should not work. - processPosition(mapper, toRawX(xOutside), toRawY(yOutside)); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); - - // touch on inside area should receive the event. - NotifyMotionArgs args; - processPosition(mapper, toRawX(xInside), toRawY(yInside)); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(xExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1); - ASSERT_NEAR(yExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1); - - // Reset. - mapper.reset(ARBITRARY_TIME); - } -}; - -TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange) { - addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); - prepareAxes(POSITION); - MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); - - // Touch on center of normal display should work. - const int32_t x = DISPLAY_WIDTH / 4; - const int32_t y = DISPLAY_HEIGHT / 2; - processPosition(mapper, toRawX(x), toRawY(y)); - processSync(mapper); - NotifyMotionArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], x, y, 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 0.0f)); - // Reset. - mapper.reset(ARBITRARY_TIME); - - // Let physical display be different to device, and make surface and physical could be 1:1 in - // all four orientations. - for (int orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180, - DISPLAY_ORIENTATION_270}) { - halfDisplayToCenterHorizontal(orientation); - - const int32_t xExpected = (x + 1) - (DISPLAY_WIDTH / 4); - const int32_t yExpected = y; - processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected); - } -} - -TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_90_NotOrientationAware) { - addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); - prepareAxes(POSITION); - // Since InputReader works in the un-rotated coordinate space, only devices that are not - // orientation-aware are affected by display rotation. - addConfigurationProperty("touch.orientationAware", "0"); - MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); - - // Half display to (width/4, 0, width * 3/4, height) and rotate 90-degrees. - halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_90); - - const int32_t x = DISPLAY_WIDTH / 4; - const int32_t y = DISPLAY_HEIGHT / 2; - - // expect x/y = swap x/y then reverse x. - constexpr int32_t xExpected = DISPLAY_HEIGHT - y; - constexpr int32_t yExpected = (x + 1) - DISPLAY_WIDTH / 4; - processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected); -} - -TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_270_NotOrientationAware) { - addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); - prepareAxes(POSITION); - // Since InputReader works in the un-rotated coordinate space, only devices that are not - // orientation-aware are affected by display rotation. - addConfigurationProperty("touch.orientationAware", "0"); - MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); - - // Half display to (width/4, 0, width * 3/4, height) and rotate 270-degrees. - halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_270); - - const int32_t x = DISPLAY_WIDTH / 4; - const int32_t y = DISPLAY_HEIGHT / 2; - - // expect x/y = swap x/y then reverse y. - const int32_t xExpected = y; - const int32_t yExpected = (DISPLAY_WIDTH * 3 / 4) - (x + 1); - processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected); -} - -TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_Corner_NotOrientationAware) { - addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); - prepareAxes(POSITION); - // Since InputReader works in the un-rotated coordinate space, only devices that are not - // orientation-aware are affected by display rotation. - addConfigurationProperty("touch.orientationAware", "0"); - MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); - - const int32_t x = 0; - const int32_t y = 0; - - const int32_t xExpected = x; - const int32_t yExpected = y; - processPositionAndVerify(mapper, x - 1, y, x, y, xExpected, yExpected); - - clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_90); - // expect x/y = swap x/y then reverse x. - const int32_t xExpected90 = DISPLAY_HEIGHT - 1; - const int32_t yExpected90 = x; - processPositionAndVerify(mapper, x - 1, y, x, y, xExpected90, yExpected90); - - clearViewports(); - prepareDisplay(DISPLAY_ORIENTATION_270); - // expect x/y = swap x/y then reverse y. - const int32_t xExpected270 = y; - const int32_t yExpected270 = DISPLAY_WIDTH - 1; - processPositionAndVerify(mapper, x - 1, y, x, y, xExpected270, yExpected270); -} - TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) { // we need a pointer controller for mouse mode of touchpad (start pointer at 0,0) std::shared_ptr<FakePointerController> fakePointerController = @@ -8769,7 +8806,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 +8956,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 +8996,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>(); @@ -8986,23 +9023,23 @@ protected: std::shared_ptr<FakeEventHub> mFakeEventHub; sp<FakeInputReaderPolicy> mFakePolicy; - sp<TestInputListener> mFakeListener; + std::unique_ptr<TestInputListener> mFakeListener; std::unique_ptr<InstrumentedInputReader> mReader; std::shared_ptr<InputDevice> mDevice; virtual void SetUp(Flags<InputDeviceClass> classes) { mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = new FakeInputReaderPolicy(); - mFakeListener = new TestInputListener(); + mFakeListener = std::make_unique<TestInputListener>(); mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, - mFakeListener); + *mFakeListener); mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes); } void SetUp() override { SetUp(DEVICE_CLASSES); } void TearDown() override { - mFakeListener.clear(); + mFakeListener.reset(); mFakePolicy.clear(); } 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/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h index 0a1dc4b0b9..626cdfc8c8 100644 --- a/services/inputflinger/tests/TestInputListener.h +++ b/services/inputflinger/tests/TestInputListener.h @@ -28,12 +28,10 @@ namespace android { // --- TestInputListener --- class TestInputListener : public InputListenerInterface { -protected: - virtual ~TestInputListener(); - public: TestInputListener(std::chrono::milliseconds eventHappenedTimeout = 0ms, std::chrono::milliseconds eventDidNotHappenTimeout = 0ms); + virtual ~TestInputListener(); void assertNotifyConfigurationChangedWasCalled( NotifyConfigurationChangedArgs* outEventArgs = nullptr); 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/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index e0511bfe73..db1a1cc29f 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/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp index af86d09784..fae16f66b2 100644 --- a/services/sensorservice/SensorDirectConnection.cpp +++ b/services/sensorservice/SensorDirectConnection.cpp @@ -32,7 +32,6 @@ SensorService::SensorDirectConnection::SensorDirectConnection(const sp<SensorSer : mService(service), mUid(uid), mMem(*mem), mHalChannelHandle(halChannelHandle), mOpPackageName(opPackageName), mDestroyed(false) { - mIsRateCappedBasedOnPermission = mService->isRateCappedBasedOnPermission(mOpPackageName); mUserId = multiuser_get_user_id(mUid); ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection"); } @@ -197,8 +196,8 @@ int32_t SensorService::SensorDirectConnection::configureChannel(int handle, int if (mService->isSensorInCappedSet(s.getType())) { // Back up the rates that the app is allowed to have if the mic toggle is off // This is used in the uncapRates() function. - if (!mIsRateCappedBasedOnPermission || - requestedRateLevel <= SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) { + if ((requestedRateLevel <= SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) || + !isRateCappedBasedOnPermission()) { mMicRateBackup[handle] = requestedRateLevel; } else { mMicRateBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL; diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h index a3f348b668..d39a073f98 100644 --- a/services/sensorservice/SensorDirectConnection.h +++ b/services/sensorservice/SensorDirectConnection.h @@ -17,6 +17,7 @@ #ifndef ANDROID_SENSOR_DIRECT_CONNECTION_H #define ANDROID_SENSOR_DIRECT_CONNECTION_H +#include <optional> #include <stdint.h> #include <sys/types.h> @@ -100,10 +101,19 @@ private: std::unordered_map<int, int> mActivatedBackup; std::unordered_map<int, int> mMicRateBackup; - std::atomic_bool mIsRateCappedBasedOnPermission; mutable Mutex mDestroyLock; bool mDestroyed; userid_t mUserId; + + std::optional<bool> mIsRateCappedBasedOnPermission; + + bool isRateCappedBasedOnPermission() { + if (!mIsRateCappedBasedOnPermission.has_value()) { + mIsRateCappedBasedOnPermission = + mService->isRateCappedBasedOnPermission(mOpPackageName); + } + return mIsRateCappedBasedOnPermission.value(); + } }; } // namepsace android diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp index 9ce8d9bb18..6948895835 100644 --- a/services/sensorservice/SensorEventConnection.cpp +++ b/services/sensorservice/SensorEventConnection.cpp @@ -44,7 +44,6 @@ SensorService::SensorEventConnection::SensorEventConnection( mCacheSize(0), mMaxCacheSize(0), mTimeOfLastEventDrop(0), mEventsDropped(0), mPackageName(packageName), mOpPackageName(opPackageName), mAttributionTag(attributionTag), mTargetSdk(kTargetSdkUnknown), mDestroyed(false) { - mIsRateCappedBasedOnPermission = mService->isRateCappedBasedOnPermission(mOpPackageName); mUserId = multiuser_get_user_id(mUid); mChannel = new BitTube(mService->mSocketBufferSize); #if DEBUG_CONNECTIONS @@ -706,8 +705,8 @@ status_t SensorService::SensorEventConnection::enableDisable( err = mService->enable(this, handle, samplingPeriodNs, maxBatchReportLatencyNs, reservedFlags, mOpPackageName); if (err == OK && isSensorCapped) { - if (!mIsRateCappedBasedOnPermission || - requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) { + if ((requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) || + !isRateCappedBasedOnPermission()) { mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs; } else { mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS; @@ -745,8 +744,8 @@ status_t SensorService::SensorEventConnection::setEventRate(int handle, nsecs_t } status_t ret = mService->setEventRate(this, handle, samplingPeriodNs, mOpPackageName); if (ret == OK && isSensorCapped) { - if (!mIsRateCappedBasedOnPermission || - requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) { + if ((requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) || + !isRateCappedBasedOnPermission()) { mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs; } else { mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS; @@ -867,7 +866,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/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h index 909053be50..6a98a40686 100644 --- a/services/sensorservice/SensorEventConnection.h +++ b/services/sensorservice/SensorEventConnection.h @@ -18,6 +18,7 @@ #define ANDROID_SENSOR_EVENT_CONNECTION_H #include <atomic> +#include <optional> #include <stdint.h> #include <sys/types.h> #include <unordered_map> @@ -148,7 +149,6 @@ private: sp<SensorService> const mService; sp<BitTube> mChannel; uid_t mUid; - std::atomic_bool mIsRateCappedBasedOnPermission; mutable Mutex mConnectionLock; // Number of events from wake up sensors which are still pending and haven't been delivered to // the corresponding application. It is incremented by one unit for each write to the socket. @@ -201,6 +201,16 @@ private: // Mapping of sensor handles to its rate before being capped by the mic toggle. std::unordered_map<int, nsecs_t> mMicSamplingPeriodBackup; userid_t mUserId; + + std::optional<bool> mIsRateCappedBasedOnPermission; + + bool isRateCappedBasedOnPermission() { + if (!mIsRateCappedBasedOnPermission.has_value()) { + mIsRateCappedBasedOnPermission + = mService->isRateCappedBasedOnPermission(mOpPackageName); + } + return mIsRateCappedBasedOnPermission.value(); + } }; } // namepsace android diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 726fe8ea84..8c3a24fe7c 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; } @@ -2190,10 +2190,10 @@ bool SensorService::isSensorInCappedSet(int sensorType) { status_t SensorService::adjustSamplingPeriodBasedOnMicAndPermission(nsecs_t* requestedPeriodNs, const String16& opPackageName) { uid_t uid = IPCThreadState::self()->getCallingUid(); - bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName); if (*requestedPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) { return OK; } + bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName); if (shouldCapBasedOnPermission) { *requestedPeriodNs = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS; if (isPackageDebuggable(opPackageName)) { @@ -2211,11 +2211,10 @@ status_t SensorService::adjustSamplingPeriodBasedOnMicAndPermission(nsecs_t* req status_t SensorService::adjustRateLevelBasedOnMicAndPermission(int* requestedRateLevel, const String16& opPackageName) { uid_t uid = IPCThreadState::self()->getCallingUid(); - bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName); - if (*requestedRateLevel <= SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) { return OK; } + bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName); if (shouldCapBasedOnPermission) { *requestedRateLevel = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL; if (isPackageDebuggable(opPackageName)) { 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..8de43e0fe6 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) { @@ -374,13 +346,13 @@ TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate } } // namespace -bool BufferLayer::onPostComposition(const DisplayDevice* display, +void BufferLayer::onPostComposition(const DisplayDevice* display, const std::shared_ptr<FenceTime>& glDoneFence, const std::shared_ptr<FenceTime>& presentFence, const CompositorTiming& compositorTiming) { // mFrameLatencyNeeded is true when a new frame was latched for the // composition. - if (!mBufferInfo.mFrameLatencyNeeded) return false; + if (!mBufferInfo.mFrameLatencyNeeded) return; // Update mFrameEventHistory. { @@ -454,7 +426,6 @@ bool BufferLayer::onPostComposition(const DisplayDevice* display, mFrameTracker.advanceFrame(); mBufferInfo.mFrameLatencyNeeded = false; - return true; } void BufferLayer::gatherBufferInfo() { @@ -648,14 +619,6 @@ bool BufferLayer::needsFilteringForScreenshots(const DisplayDevice* display, return sourceCrop.getHeight() != frameHeight || sourceCrop.getWidth() != frameWidth; } -uint64_t BufferLayer::getHeadFrameNumber(nsecs_t expectedPresentTime) const { - if (hasFrameUpdate()) { - return getFrameNumber(expectedPresentTime); - } else { - return mCurrentFrameNumber; - } -} - Rect BufferLayer::getBufferSize(const State& s) const { // If we have a sideband stream, or we are scaling the buffer then return the layer size since // we cannot determine the buffer size. diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h index 760c8b9f3c..61e7ac5d05 100644 --- a/services/surfaceflinger/BufferLayer.h +++ b/services/surfaceflinger/BufferLayer.h @@ -77,7 +77,7 @@ public: bool isHdrY410() const override; - bool onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& glDoneFence, + void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& glDoneFence, const std::shared_ptr<FenceTime>& presentFence, const CompositorTiming&) override; @@ -164,8 +164,6 @@ protected: void updateCloneBufferInfo() override; uint64_t mPreviousFrameNumber = 0; - uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const override; - void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override; // Transform hint provided to the producer. This must be accessed holding @@ -189,8 +187,6 @@ protected: private: virtual bool fenceHasSignaled() const = 0; virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0; - virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0; - // Latch sideband stream and returns true if the dirty region should be updated. virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0; diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp index 96b22478ab..c79fa1104c 100644 --- a/services/surfaceflinger/BufferLayerConsumer.cpp +++ b/services/surfaceflinger/BufferLayerConsumer.cpp @@ -438,7 +438,7 @@ void BufferLayerConsumer::onDisconnect() { } void BufferLayerConsumer::onSidebandStreamChanged() { - FrameAvailableListener* unsafeFrameAvailableListener = nullptr; + [[maybe_unused]] FrameAvailableListener* unsafeFrameAvailableListener = nullptr; { Mutex::Autolock lock(mFrameAvailableMutex); unsafeFrameAvailableListener = mFrameAvailableListener.unsafe_get(); diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp index 99e470dfe6..f98681e1ce 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -17,7 +17,6 @@ // 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 "BufferQueueLayer" @@ -66,16 +65,6 @@ void BufferQueueLayer::setTransformHint(ui::Transform::RotationFlags displayTran mConsumer->setTransformHint(mTransformHint); } -std::vector<OccupancyTracker::Segment> BufferQueueLayer::getOccupancyHistory(bool forceFlush) { - std::vector<OccupancyTracker::Segment> history; - status_t result = mConsumer->getOccupancyHistory(forceFlush, &history); - if (result != NO_ERROR) { - ALOGW("[%s] Failed to obtain occupancy history (%d)", getDebugName(), result); - return {}; - } - return history; -} - void BufferQueueLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) { if (!mConsumer->releasePendingBuffer()) { return; @@ -153,54 +142,21 @@ 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; } -uint64_t BufferQueueLayer::getFrameNumber(nsecs_t expectedPresentTime) const { - Mutex::Autolock lock(mQueueItemLock); - uint64_t frameNumber = mQueueItems[0].item.mFrameNumber; - - // The head of the queue will be dropped if there are signaled and timely frames behind it - if (isRemovedFromCurrentState()) { - expectedPresentTime = 0; - } - - for (int i = 1; i < mQueueItems.size(); i++) { - const bool fenceSignaled = - mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING; - if (!fenceSignaled) { - break; - } - - // We don't drop frames without explicit timestamps - if (mQueueItems[i].item.mIsAutoTimestamp) { - break; - } - - const nsecs_t desiredPresent = mQueueItems[i].item.mTimestamp; - if (desiredPresent < expectedPresentTime - BufferQueueConsumer::MAX_REASONABLE_NSEC || - desiredPresent > expectedPresentTime) { - break; - } - - frameNumber = mQueueItems[i].item.mFrameNumber; - } - - return frameNumber; -} - 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(); @@ -243,7 +199,7 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t uint64_t lastSignaledFrameNumber = mLastFrameNumberReceived; { Mutex::Autolock lock(mQueueItemLock); - for (int i = 0; i < mQueueItems.size(); i++) { + for (size_t i = 0; i < mQueueItems.size(); i++) { bool fenceSignaled = mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING; if (!fenceSignaled) { @@ -269,13 +225,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 +263,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 +272,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 +293,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(); } @@ -625,4 +585,4 @@ void BufferQueueLayer::ContentsChangedListener::abandon() { } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion -Wextra" +#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h index b3b7948935..a3bd725cfe 100644 --- a/services/surfaceflinger/BufferQueueLayer.h +++ b/services/surfaceflinger/BufferQueueLayer.h @@ -44,8 +44,6 @@ public: void onLayerDisplayed(const sp<Fence>& releaseFence) override; - std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override; - // If a buffer was replaced this frame, release the former buffer void releasePendingBuffer(nsecs_t dequeueReadyTime) override; @@ -89,7 +87,6 @@ protected: }; private: - uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override; bool latchSidebandStream(bool& recomputeVisibleRegions) override; void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override; diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index cd531d6afc..4eeaba154f 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" @@ -137,6 +132,20 @@ status_t BufferStateLayer::addReleaseFence(const sp<CallbackHandle>& ch, // Interface implementation for Layer // ----------------------------------------------------------------------- void BufferStateLayer::onLayerDisplayed(const sp<Fence>& releaseFence) { + + // If a layer has been displayed again we may need to clear + // the mLastClientComposition fence that we use for early release in setBuffer + // (as we now have a new fence which won't pass through the client composition path in some cases + // e.g. screenshot). We expect one call to onLayerDisplayed after receiving the GL comp fence + // from a single composition cycle, and want to clear on the second call + // (which we track with mLastClientCompositionDisplayed) + if (mLastClientCompositionDisplayed) { + mLastClientCompositionFence = nullptr; + mLastClientCompositionDisplayed = false; + } else if (mLastClientCompositionFence) { + mLastClientCompositionDisplayed = true; + } + if (!releaseFence->isValid()) { return; } @@ -219,7 +228,7 @@ void BufferStateLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) { JankData(surfaceFrame->getToken(), surfaceFrame->getJankType().value())); } - mFlinger->getTransactionCallbackInvoker().finalizePendingCallbackHandles( + mFlinger->getTransactionCallbackInvoker().addCallbackHandles( mDrawingState.callbackHandles, jankData); mDrawingState.callbackHandles = {}; @@ -411,15 +420,55 @@ bool BufferStateLayer::addFrameEvent(const sp<Fence>& acquireFence, nsecs_t post return true; } -bool BufferStateLayer::setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& buffer, - const sp<Fence>& acquireFence, nsecs_t postTime, +std::shared_ptr<renderengine::ExternalTexture> BufferStateLayer::getBufferFromBufferData( + const BufferData& bufferData) { + bool cacheIdChanged = bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged); + bool bufferSizeExceedsLimit = false; + std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr; + if (cacheIdChanged && bufferData.buffer != nullptr) { + bufferSizeExceedsLimit = + mFlinger->exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(), + bufferData.buffer->getHeight()); + if (!bufferSizeExceedsLimit) { + ClientCache::getInstance().add(bufferData.cachedBuffer, bufferData.buffer); + buffer = ClientCache::getInstance().get(bufferData.cachedBuffer); + } + } else if (cacheIdChanged) { + buffer = ClientCache::getInstance().get(bufferData.cachedBuffer); + } else if (bufferData.buffer != nullptr) { + bufferSizeExceedsLimit = + mFlinger->exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(), + bufferData.buffer->getHeight()); + if (!bufferSizeExceedsLimit) { + buffer = std::make_shared< + renderengine::ExternalTexture>(bufferData.buffer, mFlinger->getRenderEngine(), + renderengine::ExternalTexture::Usage::READABLE); + } + } + ALOGE_IF(bufferSizeExceedsLimit, + "Attempted to create an ExternalTexture for layer %s that exceeds render target size " + "limit.", + getDebugName()); + return buffer; +} + +bool BufferStateLayer::setBuffer(const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp, - const client_cache_t& clientCacheId, uint64_t frameNumber, - std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info, - const sp<ITransactionCompletedListener>& releaseBufferListener, - const sp<IBinder>& releaseBufferEndpoint) { + std::optional<nsecs_t> dequeueTime, + const FrameTimelineInfo& info) { ATRACE_CALL(); + const std::shared_ptr<renderengine::ExternalTexture>& buffer = + getBufferFromBufferData(bufferData); + if (!buffer) { + return false; + } + + const bool frameNumberChanged = + bufferData.flags.test(BufferData::BufferDataChange::frameNumberChanged); + const uint64_t frameNumber = + frameNumberChanged ? bufferData.frameNumber : mDrawingState.frameNumber + 1; + if (mDrawingState.buffer) { mReleasePreviousBuffer = true; if (mDrawingState.buffer != mBufferInfo.mBuffer || @@ -439,13 +488,28 @@ bool BufferStateLayer::setBuffer(const std::shared_ptr<renderengine::ExternalTex addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX); mDrawingState.bufferSurfaceFrameTX.reset(); } + } else if (mLastClientCompositionFence != nullptr) { + callReleaseBufferCallback(mDrawingState.releaseBufferListener, + mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber, + mLastClientCompositionFence, mTransformHint, + mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate( + mOwnerUid)); + mLastClientCompositionFence = nullptr; } } mDrawingState.frameNumber = frameNumber; - mDrawingState.releaseBufferListener = releaseBufferListener; + mDrawingState.releaseBufferListener = bufferData.releaseBufferListener; mDrawingState.buffer = buffer; - mDrawingState.clientCacheId = clientCacheId; + mDrawingState.clientCacheId = bufferData.cachedBuffer; + + mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged) + ? bufferData.acquireFence + : Fence::NO_FENCE; + mDrawingState.acquireFenceTime = std::make_unique<FenceTime>(mDrawingState.acquireFence); + // The acquire fences of BufferStateLayers have already signaled before they are set + mCallbackHandleAcquireTime = mDrawingState.acquireFenceTime->getSignalTime(); + mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); @@ -467,7 +531,7 @@ bool BufferStateLayer::setBuffer(const std::shared_ptr<renderengine::ExternalTex mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerHistory::LayerUpdateType::Buffer); - addFrameEvent(acquireFence, postTime, isAutoTimestamp ? 0 : desiredPresentTime); + addFrameEvent(mDrawingState.acquireFence, postTime, isAutoTimestamp ? 0 : desiredPresentTime); setFrameTimelineVsyncForBufferTransaction(info, postTime); @@ -482,20 +546,7 @@ bool BufferStateLayer::setBuffer(const std::shared_ptr<renderengine::ExternalTex mDrawingState.width = mDrawingState.buffer->getBuffer()->getWidth(); mDrawingState.height = mDrawingState.buffer->getBuffer()->getHeight(); - mDrawingState.releaseBufferEndpoint = releaseBufferEndpoint; - - return true; -} - -bool BufferStateLayer::setAcquireFence(const sp<Fence>& fence) { - mDrawingState.acquireFence = fence; - mDrawingState.acquireFenceTime = std::make_unique<FenceTime>(fence); - - // The acquire fences of BufferStateLayers have already signaled before they are set - mCallbackHandleAcquireTime = mDrawingState.acquireFenceTime->getSignalTime(); - - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); + mDrawingState.releaseBufferEndpoint = bufferData.releaseBufferEndpoint; return true; } @@ -569,10 +620,6 @@ bool BufferStateLayer::setTransactionCompletedListeners( handle->acquireTime = mCallbackHandleAcquireTime; handle->frameNumber = mDrawingState.frameNumber; - // Notify the transaction completed thread that there is a pending latched callback - // handle - mFlinger->getTransactionCallbackInvoker().registerPendingCallbackHandle(handle); - // Store so latched time and release fence can be set mDrawingState.callbackHandles.push_back(handle); @@ -596,7 +643,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 +665,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 { @@ -664,36 +711,6 @@ bool BufferStateLayer::onPreComposition(nsecs_t refreshStartTime) { return BufferLayer::onPreComposition(refreshStartTime); } -uint64_t BufferStateLayer::getFrameNumber(nsecs_t /*expectedPresentTime*/) const { - return mDrawingState.frameNumber; -} - -/** - * This is the frameNumber used for deferred transaction signalling. We need to use this because - * of cases where we defer a transaction for a surface to itself. In the BLAST world this - * may not make a huge amount of sense (Why not just merge the Buffer transaction with the - * deferred transaction?) but this is an important legacy use case, for example moving - * a window at the same time it draws makes use of this kind of technique. So anyway - * imagine we have something like this: - * - * Transaction { // containing - * Buffer -> frameNumber = 2 - * DeferTransactionUntil -> frameNumber = 2 - * Random other stuff - * } - * Now imagine mFrameNumber returned mDrawingState.frameNumber (or mCurrentFrameNumber). - * Prior to doTransaction SurfaceFlinger will call notifyAvailableFrames, but because we - * haven't swapped mDrawingState to mDrawingState yet we will think the sync point - * is not ready. So we will return false from applyPendingState and not swap - * current state to drawing state. But because we don't swap current state - * to drawing state the number will never update and we will be stuck. This way - * we can see we need to return the frame number for the buffer we are about - * to apply. - */ -uint64_t BufferStateLayer::getHeadFrameNumber(nsecs_t /* expectedPresentTime */) const { - return mDrawingState.frameNumber; -} - void BufferStateLayer::setAutoRefresh(bool autoRefresh) { if (!mAutoRefresh.exchange(autoRefresh)) { mFlinger->signalLayerUpdate(); @@ -702,9 +719,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; @@ -770,7 +787,7 @@ status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nse std::deque<sp<CallbackHandle>> remainingHandles; mFlinger->getTransactionCallbackInvoker() - .finalizeOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles); + .addOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles); mDrawingState.callbackHandles = remainingHandles; mDrawingStateModified = false; @@ -817,7 +834,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 +845,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 +854,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 +951,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 +1011,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..87b68ea71b 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -55,13 +55,9 @@ public: bool setTransform(uint32_t transform) override; bool setTransformToDisplayInverse(bool transformToDisplayInverse) override; bool setCrop(const Rect& crop) override; - bool setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& buffer, - const sp<Fence>& acquireFence, nsecs_t postTime, nsecs_t desiredPresentTime, - bool isAutoTimestamp, const client_cache_t& clientCacheId, uint64_t frameNumber, - std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info, - const sp<ITransactionCompletedListener>& transactionListener, - const sp<IBinder>& releaseBufferEndpoint) override; - bool setAcquireFence(const sp<Fence>& fence) override; + bool setBuffer(const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime, + bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime, + const FrameTimelineInfo& info) override; bool setDataspace(ui::Dataspace dataspace) override; bool setHdrMetadata(const HdrMetadata& hdrMetadata) override; bool setSurfaceDamageRegion(const Region& surfaceDamage) override; @@ -105,7 +101,6 @@ public: protected: void gatherBufferInfo() override; - uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const; void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame); ui::Transform getInputTransform() const override; Rect getInputBounds() const override; @@ -122,8 +117,6 @@ private: status_t addReleaseFence(const sp<CallbackHandle>& ch, const sp<Fence>& releaseFence); - uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override; - bool latchSidebandStream(bool& recomputeVisibleRegions) override; bool hasFrameUpdate() const override; @@ -143,6 +136,9 @@ private: bool bufferNeedsFiltering() const override; + std::shared_ptr<renderengine::ExternalTexture> getBufferFromBufferData( + const BufferData& bufferData); + sp<Fence> mPreviousReleaseFence; ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID; uint64_t mPreviousReleasedFrameNumber = 0; @@ -178,19 +174,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 +198,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..e7b8995703 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) {} @@ -82,10 +82,13 @@ bool ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& bu return false; } - status_t err = token->linkToDeath(mDeathRecipient); - if (err != NO_ERROR) { - ALOGE("failed to cache buffer: could not link to death"); - return false; + // Only call linkToDeath if not a local binder + if (token->localBinder() == nullptr) { + status_t err = token->linkToDeath(mDeathRecipient); + if (err != NO_ERROR) { + ALOGE("failed to cache buffer: could not link to death"); + return false; + } } auto [itr, success] = mBuffers.emplace(processToken, @@ -212,16 +215,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..f7b71cf9fe 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; @@ -165,6 +161,7 @@ public: // Whether the layer should be rendered with rounded corners. virtual bool hasRoundedCorners() const = 0; + virtual void setWasClientComposed(const sp<Fence>&) {} }; // TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can @@ -177,11 +174,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 +198,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..73770b7779 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,8 @@ 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, + std::vector<LayerFE*> &outLayerRef) = 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..844876a997 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,8 @@ 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, + std::vector<LayerFE*> &outLayerFEs) 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..7b0d028c11 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>&)); @@ -114,7 +115,7 @@ public: MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences()); MOCK_METHOD3(generateClientCompositionRequests, - std::vector<LayerFE::LayerSettings>(bool, Region&, ui::Dataspace)); + std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace, std::vector<compositionengine::LayerFE*>&)); 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..048d7c2b4a 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,14 +1063,12 @@ 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*> clientCompositionLayersFE; std::vector<LayerFE::LayerSettings> clientCompositionLayers = generateClientCompositionRequests(supportsProtectedContent, - clientCompositionDisplay.clearRegion, - clientCompositionDisplay.outputDataspace); + clientCompositionDisplay.outputDataspace, + clientCompositionLayersFE); appendRegionFlashRequests(debugRegion, clientCompositionLayers); // Check if the client composition requests were rendered into the provided graphic buffer. If @@ -1096,7 +1079,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 +1112,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 +1125,32 @@ 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())))); + } + + if (clientCompositionLayersFE.size() > 0) { + sp<Fence> clientCompFence = new Fence(dup(drawFence.get())); + for (auto clientComposedLayer : clientCompositionLayersFE) { + clientComposedLayer->setWasClientComposed(clientCompFence); + } } - 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*>& outLayerFEs) { 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 +1212,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, @@ -1234,6 +1223,7 @@ std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( } } + outLayerFEs.push_back(&layerFE); clientCompositionLayers.insert(clientCompositionLayers.end(), std::make_move_iterator(results.begin()), std::make_move_iterator(results.end())); @@ -1330,7 +1320,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..e958549569 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}; @@ -381,12 +381,7 @@ void OutputLayer::writeOutputDependentGeometryStateToHWC(HWC2::Layer* hwcLayer, if (outputDependentState.overrideInfo.buffer != nullptr) { displayFrame = outputDependentState.overrideInfo.displayFrame; - sourceCrop = - FloatRect(0.f, 0.f, - static_cast<float>(outputDependentState.overrideInfo.buffer->getBuffer() - ->getWidth()), - static_cast<float>(outputDependentState.overrideInfo.buffer->getBuffer() - ->getHeight())); + sourceCrop = displayFrame.toFloatRect(); } ALOGV("Writing display frame [%d, %d, %d, %d]", displayFrame.left, displayFrame.top, @@ -628,7 +623,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); @@ -737,7 +732,7 @@ std::vector<LayerFE::LayerSettings> OutputLayer::getOverrideCompositionList() co // framebuffer space of the override buffer to layer space. const ProjectionSpace& layerSpace = getOutput().getState().layerStackSpace; const ui::Transform transform = getState().overrideInfo.displaySpace.getTransform(layerSpace); - const Rect boundaries = transform.transform(getState().overrideInfo.displayFrame); + const Rect boundaries = transform.transform(getState().overrideInfo.displaySpace.getContent()); LayerFE::LayerSettings settings; settings.geometry = renderengine::Geometry{ 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/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp index ad5e93168d..2272099c77 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp @@ -315,7 +315,7 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers state.overrideInfo = { .buffer = mNewCachedSet->getBuffer(), .acquireFence = mNewCachedSet->getDrawFence(), - .displayFrame = mNewCachedSet->getTextureBounds(), + .displayFrame = mNewCachedSet->getBounds(), .dataspace = mNewCachedSet->getOutputDataspace(), .displaySpace = mNewCachedSet->getOutputSpace(), .damageRegion = Region::INVALID_REGION, @@ -355,7 +355,7 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers state.overrideInfo = { .buffer = currentLayerIter->getBuffer(), .acquireFence = currentLayerIter->getDrawFence(), - .displayFrame = currentLayerIter->getTextureBounds(), + .displayFrame = currentLayerIter->getBounds(), .dataspace = currentLayerIter->getOutputDataspace(), .displaySpace = currentLayerIter->getOutputSpace(), .damageRegion = Region(), 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..fbc20898ec 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); @@ -876,7 +876,7 @@ const half4 OutputLayerWriteStateToHWCTest::kColor{81.f / 255.f, 82.f / 255.f, 8 84.f / 255.f}; const Rect OutputLayerWriteStateToHWCTest::kDisplayFrame{1001, 1002, 1003, 10044}; const Rect OutputLayerWriteStateToHWCTest::kOverrideDisplayFrame{1002, 1003, 1004, 20044}; -const FloatRect OutputLayerWriteStateToHWCTest::kOverrideSourceCrop{0.f, 0.f, 4.f, 5.f}; +const FloatRect OutputLayerWriteStateToHWCTest::kOverrideSourceCrop{1002, 1003, 1004, 20044}; const Region OutputLayerWriteStateToHWCTest::kOutputSpaceVisibleRegion{ Rect{1005, 1006, 1007, 1008}}; const Region OutputLayerWriteStateToHWCTest::kOverrideVisibleRegion{Rect{1006, 1007, 1008, 1009}}; @@ -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..8f0028c399 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()); @@ -2993,7 +2974,7 @@ struct OutputComposeSurfacesTest : public testing::Test { // mock implementations. MOCK_CONST_METHOD0(getSkipColorTransform, bool()); MOCK_METHOD3(generateClientCompositionRequests, - std::vector<LayerFE::LayerSettings>(bool, Region&, ui::Dataspace)); + std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace, std::vector<LayerFE*>&)); 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(); } @@ -3474,8 +3484,15 @@ struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeS 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)); @@ -3652,11 +3675,11 @@ TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreNotEx 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 { + std::vector<LayerFE::LayerSettings> generateClientCompositionRequestsHelper( + bool supportsProtectedContent, ui::Dataspace dataspace) { + std::vector<LayerFE*> ignore; return impl::Output::generateClientCompositionRequests(supportsProtectedContent, - clearRegion, dataspace); + dataspace, ignore); } }; @@ -3691,12 +3714,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 +3763,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); + auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, + kDisplayDataspace); EXPECT_EQ(0u, requests.size()); - EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, requiresVisibleRegionAfterViewportClip) { @@ -3752,11 +3773,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); + auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, + kDisplayDataspace); EXPECT_EQ(0u, requests.size()); - EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, gathersClientCompositionRequests) { @@ -3771,16 +3790,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); + auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, + 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 +3827,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); + auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, + 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 +3857,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); + auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, + 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 +3880,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); + auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, + 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 +3904,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 +3921,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqu false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -3936,16 +3939,14 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqu EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings)))) .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings}))); - auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, + 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 +3955,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 +3971,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -3985,7 +3982,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4001,8 +3997,8 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); static_cast<void>( - mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace)); + mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, + kDisplayDataspace)); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, @@ -4010,14 +4006,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 +4022,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4041,7 +4033,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4057,8 +4048,8 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); static_cast<void>( - mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace)); + mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, + kDisplayDataspace)); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, @@ -4066,14 +4057,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 +4074,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, true, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4098,7 +4085,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, true, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4114,22 +4100,19 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); static_cast<void>( - mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace)); + mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, + 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 +4124,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ true, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4153,7 +4135,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ true, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4169,20 +4150,18 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); static_cast<void>( - mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace)); + mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, + 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 +4173,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ true, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4206,7 +4184,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ true, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4221,8 +4198,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings)))) .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); - static_cast<void>(mOutput.generateClientCompositionRequests(true /* supportsProtectedContent */, - accumClearRegion, + static_cast<void>(mOutput.generateClientCompositionRequestsHelper(true /* supportsProtectedContent */, kDisplayDataspace)); } @@ -4343,11 +4319,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 +4346,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 +4368,6 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq false, /* needs filtering */ true, /* secure */ true, /* supports protected content */ - accumClearRegion, kPortraitViewport, kOutputDataspace, true /* realContentIsVisible */, @@ -4409,8 +4381,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.generateClientCompositionRequestsHelper(supportsProtectedContent, kOutputDataspace); ASSERT_EQ(2u, requests.size()); EXPECT_EQ(leftLayer.mLayerSettings, requests[0]); EXPECT_EQ(rightLayer.mLayerSettings, requests[1]); @@ -4423,13 +4395,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 */, @@ -4448,8 +4418,8 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings)))) .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mShadowSettings}))); - auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, + kDisplayDataspace); ASSERT_EQ(1u, requests.size()); EXPECT_EQ(mShadowSettings, requests[0]); @@ -4469,13 +4439,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 */, @@ -4489,8 +4457,8 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, .WillOnce(Return(std::vector<LayerFE::LayerSettings>( {mShadowSettings, mLayers[2].mLayerSettings}))); - auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */, + 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/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 27146ab79c..e21b0daa96 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -283,8 +283,21 @@ Error Display::getConnectionType(ui::DisplayConnectionType* outType) const { return Error::NONE; } +bool Display::hasCapability(hal::DisplayCapability capability) const { + std::scoped_lock lock(mDisplayCapabilitiesMutex); + if (mDisplayCapabilities) { + return mDisplayCapabilities->count(capability) > 0; + } + + ALOGW("Can't query capability %d." + " Display Capabilities were not queried from HWC yet", + static_cast<int>(capability)); + + return false; +} + Error Display::supportsDoze(bool* outSupport) const { - *outSupport = mDisplayCapabilities.count(DisplayCapability::DOZE) > 0; + *outSupport = hasCapability(DisplayCapability::DOZE); return Error::NONE; } @@ -447,17 +460,21 @@ Error Display::setPowerMode(PowerMode mode) auto error = static_cast<Error>(mComposer.getDisplayCapabilities(mId, &tmpCapabilities)); if (error == Error::NONE) { + std::scoped_lock lock(mDisplayCapabilitiesMutex); + mDisplayCapabilities.emplace(); for (auto capability : tmpCapabilities) { - mDisplayCapabilities.emplace(static_cast<DisplayCapability>(capability)); + mDisplayCapabilities->emplace(static_cast<DisplayCapability>(capability)); } } else if (error == Error::UNSUPPORTED) { + std::scoped_lock lock(mDisplayCapabilitiesMutex); + mDisplayCapabilities.emplace(); if (mCapabilities.count(Capability::SKIP_CLIENT_COLOR_TRANSFORM)) { - mDisplayCapabilities.emplace(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM); + mDisplayCapabilities->emplace(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM); } bool dozeSupport = false; error = static_cast<Error>(mComposer.getDozeSupport(mId, &dozeSupport)); if (error == Error::NONE && dozeSupport) { - mDisplayCapabilities.emplace(DisplayCapability::DOZE); + mDisplayCapabilities->emplace(DisplayCapability::DOZE); } } }); diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 871465d717..a65efb2931 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -17,6 +17,7 @@ #pragma once #include <android-base/expected.h> +#include <android-base/thread_annotations.h> #include <gui/HdrMetadata.h> #include <math/mat4.h> #include <ui/HdrCapabilities.h> @@ -79,7 +80,7 @@ public: virtual hal::HWDisplayId getId() const = 0; virtual bool isConnected() const = 0; virtual void setConnected(bool connected) = 0; // For use by Device only - virtual const std::unordered_set<hal::DisplayCapability>& getCapabilities() const = 0; + virtual bool hasCapability(hal::DisplayCapability) const = 0; virtual bool isVsyncPeriodSwitchSupported() const = 0; virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0; @@ -175,7 +176,7 @@ public: hal::DisplayRequest* outDisplayRequests, std::unordered_map<HWC2::Layer*, hal::LayerRequest>* outLayerRequests) override; hal::Error getConnectionType(ui::DisplayConnectionType*) const override; - hal::Error supportsDoze(bool* outSupport) const override; + hal::Error supportsDoze(bool* outSupport) const override EXCLUDES(mDisplayCapabilitiesMutex); hal::Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override; hal::Error getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat, hal::Dataspace* outDataspace, @@ -214,9 +215,7 @@ public: hal::HWDisplayId getId() const override { return mId; } bool isConnected() const override { return mIsConnected; } void setConnected(bool connected) override; // For use by Device only - const std::unordered_set<hal::DisplayCapability>& getCapabilities() const override { - return mDisplayCapabilities; - }; + bool hasCapability(hal::DisplayCapability) const override EXCLUDES(mDisplayCapabilitiesMutex); bool isVsyncPeriodSwitchSupported() const override; void onLayerDestroyed(hal::HWLayerId layerId) override; @@ -243,8 +242,10 @@ private: using Layers = std::unordered_map<hal::HWLayerId, std::weak_ptr<HWC2::impl::Layer>>; Layers mLayers; + mutable std::mutex mDisplayCapabilitiesMutex; std::once_flag mDisplayCapabilityQueryFlag; - std::unordered_set<hal::DisplayCapability> mDisplayCapabilities; + std::optional<std::unordered_set<hal::DisplayCapability>> mDisplayCapabilities + GUARDED_BY(mDisplayCapabilitiesMutex); }; } // namespace impl diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index a790b4c11e..c63ba0e055 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -183,7 +183,7 @@ bool HWComposer::hasCapability(hal::Capability capability) const { bool HWComposer::hasDisplayCapability(HalDisplayId displayId, hal::DisplayCapability capability) const { RETURN_IF_INVALID_DISPLAY(displayId, false); - return mDisplayData.at(displayId).hwcDisplay->getCapabilities().count(capability) > 0; + return mDisplayData.at(displayId).hwcDisplay->hasCapability(capability); } std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hal::HWDisplayId hwcDisplayId, @@ -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..7602e6d1fe --- /dev/null +++ b/services/surfaceflinger/FlagManager.cpp @@ -0,0 +1,100 @@ +/* + * 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()); + base::StringAppendF(&result, "use_adpf_cpu_hint: %s\n", use_adpf_cpu_hint() ? "true" : "false"); +} + +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); +} + +bool FlagManager::use_adpf_cpu_hint() const { + std::optional<bool> sysPropVal = std::nullopt; + return getValue("AdpfFeature__adpf_cpu_hint", sysPropVal, false); +} + +} // namespace android diff --git a/services/surfaceflinger/FlagManager.h b/services/surfaceflinger/FlagManager.h new file mode 100644 index 0000000000..24d83a2153 --- /dev/null +++ b/services/surfaceflinger/FlagManager.h @@ -0,0 +1,46 @@ +/* + * 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; + + bool use_adpf_cpu_hint() 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 985729582e..57837d43f9 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; @@ -393,7 +393,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; @@ -405,9 +404,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; @@ -429,20 +426,6 @@ void Layer::prepareBasicGeometryCompositionState() { void Layer::prepareGeometryCompositionState() { const auto& drawingState{getDrawingState()}; - - int type = drawingState.metadata.getInt32(METADATA_WINDOW_TYPE, 0); - int appId = drawingState.metadata.getInt32(METADATA_OWNER_UID, 0); - sp<Layer> parent = mDrawingParent.promote(); - if (parent.get()) { - auto& parentState = parent->getDrawingState(); - const int parentType = parentState.metadata.getInt32(METADATA_WINDOW_TYPE, 0); - const int parentAppId = parentState.metadata.getInt32(METADATA_OWNER_UID, 0); - if (parentType > 0 && parentAppId > 0) { - type = parentType; - appId = parentAppId; - } - } - auto* compositionState = editCompositionState(); compositionState->geomBufferSize = getBufferSize(drawingState); @@ -730,14 +713,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) { @@ -1007,7 +990,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; @@ -1054,12 +1037,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) { @@ -1373,7 +1355,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; @@ -2111,7 +2093,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(), @@ -2158,7 +2140,7 @@ void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet if (traceFlags & SurfaceTracing::TRACE_INPUT) { WindowInfo info; if (useDrawing) { - info = fillInputInfo({nullptr}); + info = fillInputInfo(ui::Transform(), /* displayIsSecure */ true); } else { info = state.inputInfo; } @@ -2187,7 +2169,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 @@ -2207,13 +2189,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(); @@ -2360,47 +2342,21 @@ void Layer::handleDropInputMode(gui::WindowInfo& info) const { } } -WindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) { +WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform, bool displayIsSecure) { 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(); + info.displayId = getLayerStack().id; - // 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.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 @@ -2415,17 +2371,19 @@ WindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) { fillTouchOcclusionMode(info); handleDropInputMode(info); + // If the window will be blacked out on a display because the display does not have the secure + // flag and the layer has the secure flag set, then drop input. + if (!displayIsSecure && isSecure()) { + info.inputFeatures |= WindowInfo::Feature::DROP_INPUT; + } + 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 @@ -2436,9 +2394,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); } } @@ -2634,14 +2591,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 b107f8e9d7..8209c51ecb 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -152,12 +152,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]; @@ -408,8 +403,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); @@ -421,17 +416,11 @@ public: // Used only to set BufferStateLayer state virtual bool setTransform(uint32_t /*transform*/) { return false; }; virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; }; - virtual bool setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& /*buffer*/, - const sp<Fence>& /*acquireFence*/, nsecs_t /*postTime*/, - nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/, - const client_cache_t& /*clientCacheId*/, uint64_t /* frameNumber */, - std::optional<nsecs_t> /* dequeueTime */, - const FrameTimelineInfo& /*info*/, - const sp<ITransactionCompletedListener>& /* releaseBufferListener */, - const sp<IBinder>& /* releaseBufferEndpoint */) { + virtual bool setBuffer(const BufferData&, nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/, + bool /*isAutoTimestamp*/, std::optional<nsecs_t> /* dequeueTime */, + const FrameTimelineInfo& /*info*/) { return false; }; - virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; }; virtual bool setDataspace(ui::Dataspace /*dataspace*/) { return false; }; virtual bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/) { return false; }; virtual bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/) { return false; }; @@ -532,18 +521,14 @@ public: virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; } - virtual uint64_t getHeadFrameNumber(nsecs_t /* expectedPresentTime */) const { return 0; } - /* * called after composition. * returns true if the layer latched a new buffer this frame. */ - virtual bool onPostComposition(const DisplayDevice*, + virtual void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& /*glDoneFence*/, const std::shared_ptr<FenceTime>& /*presentFence*/, - const CompositorTiming&) { - return false; - } + const CompositorTiming&) {} // If a buffer was replaced this frame, release the former buffer virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { } @@ -604,10 +589,6 @@ public: } virtual FrameRate getFrameRateForLayerTree() const; - virtual std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool /*forceFlush*/) { - return {}; - } - virtual bool getTransformToDisplayInverse() const { return false; } // Returns how rounded corners should be drawn for this layer. @@ -636,6 +617,11 @@ public: std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList( compositionengine::LayerFE::ClientCompositionTargetSettings&) override; void onLayerDisplayed(const sp<Fence>& releaseFence) override; + + void setWasClientComposed(const sp<Fence>& fence) override { + mLastClientCompositionFence = fence; + } + const char* getDebugName() const override; bool setShadowRadius(float shadowRadius); @@ -648,13 +634,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; @@ -687,6 +672,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*); @@ -703,8 +696,6 @@ public: gui::WindowInfo::Type getWindowType() const { return mWindowType; } - bool getPrimaryDisplayOnly() const; - void updateMirrorInfo(); /* @@ -854,7 +845,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, bool displayIsSecure); + /** * Returns whether this layer has an explicitly set input-info. */ @@ -1035,6 +1027,8 @@ protected: mutable bool mDrawingStateModified = false; + sp<Fence> mLastClientCompositionFence; + bool mLastClientCompositionDisplayed = false; private: virtual void setTransformHint(ui::Transform::RotationFlags) {} @@ -1077,8 +1071,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..ec81e6317c 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -25,92 +25,77 @@ #include "Client.h" #include "Layer.h" +#include <SkBlendMode.h> +#include <SkPaint.h> +#include <SkRect.h> +#include <SkSurface.h> #include <gui/IProducerListener.h> +#include <gui/SurfaceComposerClient.h> +#include <gui/SurfaceControl.h> #undef LOG_TAG #define LOG_TAG "RefreshRateOverlay" 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 +105,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); } @@ -186,35 +192,48 @@ RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger, uint32_t lowFps, } bool RefreshRateOverlay::createLayer() { - int32_t layerId; - const status_t ret = - mFlinger.createLayer(String8("RefreshRateOverlay"), mClient, - SevenSegmentDrawer::getWidth(), SevenSegmentDrawer::getHeight(), - PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceBufferState, LayerMetadata(), - &mIBinder, &mGbp, nullptr, &layerId); - if (ret) { + mSurfaceControl = + SurfaceComposerClient::getDefault() + ->createSurface(String8("RefreshRateOverlay"), SevenSegmentDrawer::getWidth(), + SevenSegmentDrawer::getHeight(), PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState); + + if (!mSurfaceControl) { ALOGE("failed to create buffer state layer"); return false; } - mLayer = mClient->getLayerUser(mIBinder); - mLayer->setFrameRate(Layer::FrameRate(Fps(0.0f), Layer::FrameRateCompatibility::NoVote)); - mLayer->setIsAtRoot(true); - - // setting Layer's Z requires resorting layersSortedByZ - ssize_t idx = mFlinger.mDrawingState.layersSortedByZ.indexOf(mLayer); - if (mLayer->setLayer(INT32_MAX - 2) && idx >= 0) { - mFlinger.mDrawingState.layersSortedByZ.removeAt(idx); - mFlinger.mDrawingState.layersSortedByZ.add(mLayer); - } + SurfaceComposerClient::Transaction t; + t.setFrameRate(mSurfaceControl, 0.0f, + static_cast<int8_t>(Layer::FrameRateCompatibility::NoVote), + static_cast<int8_t>(scheduler::Seamlessness::OnlySeamless)); + t.setLayer(mSurfaceControl, INT32_MAX - 2); + t.apply(); return true; } -const std::vector<std::shared_ptr<renderengine::ExternalTexture>>& -RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) { - if (mBufferCache.find(fps) == mBufferCache.end()) { +const std::vector<sp<GraphicBuffer>>& RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) { + ui::Transform::RotationFlags transformHint = + static_cast<ui::Transform::RotationFlags>(mSurfaceControl->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; + } + }(); + + SurfaceComposerClient::Transaction t; + t.setTransform(mSurfaceControl, transform); + t.apply(); + + 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,25 +241,18 @@ 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); - 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> { - return std::make_shared< - renderengine::ExternalTexture>(buffer, - mFlinger.getRenderEngine(), - renderengine::ExternalTexture:: - Usage::READABLE); - }); - mBufferCache.emplace(fps, textures); + 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); + mBufferCache[transformHint].emplace(fps, buffers); } - return mBufferCache[fps]; + return mBufferCache[transformHint][fps]; } void RefreshRateOverlay::setViewport(ui::Size viewport) { @@ -250,30 +262,26 @@ void RefreshRateOverlay::setViewport(ui::Size viewport) { Rect frame((3 * width) >> 4, height >> 5); frame.offsetBy(width >> 5, height >> 4); - layer_state_t::matrix22_t matrix; - matrix.dsdx = frame.getWidth() / static_cast<float>(SevenSegmentDrawer::getWidth()); - matrix.dtdx = 0; - matrix.dtdy = 0; - matrix.dsdy = frame.getHeight() / static_cast<float>(SevenSegmentDrawer::getHeight()); - mLayer->setMatrix(matrix, true); - mLayer->setPosition(frame.left, frame.top); - mFlinger.mTransactionFlags.fetch_or(eTransactionMask); + SurfaceComposerClient::Transaction t; + t.setMatrix(mSurfaceControl, + frame.getWidth() / static_cast<float>(SevenSegmentDrawer::getWidth()), 0, 0, + frame.getHeight() / static_cast<float>(SevenSegmentDrawer::getHeight())); + t.setPosition(mSurfaceControl, frame.left, frame.top); + t.apply(); } -void RefreshRateOverlay::setLayerStack(uint32_t stack) { - mLayer->setLayerStack(stack); - mFlinger.mTransactionFlags.fetch_or(eTransactionMask); +void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) { + SurfaceComposerClient::Transaction t; + t.setLayerStack(mSurfaceControl, stack); + t.apply(); } void RefreshRateOverlay::changeRefreshRate(const Fps& fps) { mCurrentFps = fps.getIntValue(); auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame]; - mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {}, - mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */), - std::nullopt /* dequeueTime */, FrameTimelineInfo{}, - nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */); - - mFlinger.mTransactionFlags.fetch_or(eTransactionMask); + SurfaceComposerClient::Transaction t; + t.setBuffer(mSurfaceControl, buffer); + t.apply(); } void RefreshRateOverlay::onInvalidate() { @@ -282,12 +290,9 @@ void RefreshRateOverlay::onInvalidate() { const auto& buffers = getOrCreateBuffers(*mCurrentFps); mFrame = (mFrame + 1) % buffers.size(); auto buffer = buffers[mFrame]; - mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {}, - mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */), - std::nullopt /* dequeueTime */, FrameTimelineInfo{}, - nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */); - - mFlinger.mTransactionFlags.fetch_or(eTransactionMask); + SurfaceComposerClient::Transaction t; + t.setBuffer(mSurfaceControl, buffer); + t.apply(); } } // namespace android diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h index f9baa898dc..354510a9d7 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" @@ -34,12 +37,13 @@ class IBinder; class IGraphicBufferProducer; class Layer; class SurfaceFlinger; +class SurfaceControl; 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 +51,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; @@ -71,28 +71,30 @@ private: }; bool createLayer(); - const std::vector<std::shared_ptr<renderengine::ExternalTexture>>& getOrCreateBuffers( - uint32_t fps); + + const std::vector<sp<GraphicBuffer>>& getOrCreateBuffers(uint32_t fps); SurfaceFlinger& mFlinger; const sp<Client> mClient; - sp<Layer> mLayer; 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<sp<GraphicBuffer>>>> 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; // Interpolate the colors between these values. const uint32_t mLowFps; const uint32_t mHighFps; + + sp<SurfaceControl> mSurfaceControl; }; } // namespace android 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 ee1d7301c1..c9f00a0376 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -134,42 +134,37 @@ bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer, return true; } -float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer, - const RefreshRate& refreshRate, - bool isSeamlessSwitch) const { - if (!isVoteAllowed(layer, refreshRate)) { - return 0; - } - - // Slightly prefer seamless switches. - constexpr float kSeamedSwitchPenalty = 0.95f; - const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty; - - // If the layer wants Max, give higher score to the higher refresh rate - if (layer.vote == LayerVoteType::Max) { - const auto ratio = refreshRate.getFps().getValue() / - mAppRequestRefreshRates.back()->getFps().getValue(); - // use ratio^2 to get a lower score the more we get further from peak - return ratio * ratio; - } +float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked( + const LayerRequirement& layer, const RefreshRate& refreshRate) const { + constexpr float kScoreForFractionalPairs = .8f; const auto displayPeriod = refreshRate.getVsyncPeriod(); 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; + } + // Calculate how many display vsyncs we need to present a single frame for this // layer const auto [displayFramesQuotient, displayFramesRemainder] = @@ -177,7 +172,7 @@ float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& laye static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1 if (displayFramesRemainder == 0) { // Layer desired refresh rate matches the display rate. - return 1.0f * seamlessness; + return 1.0f; } if (displayFramesQuotient == 0) { @@ -195,7 +190,29 @@ float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& laye iter++; } - return (1.0f / iter) * seamlessness; + return (1.0f / iter); + } + + return 0; +} + +float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer, + const RefreshRate& refreshRate, + bool isSeamlessSwitch) const { + if (!isVoteAllowed(layer, refreshRate)) { + return 0; + } + + // Slightly prefer seamless switches. + constexpr float kSeamedSwitchPenalty = 0.95f; + const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty; + + // If the layer wants Max, give higher score to the higher refresh rate + if (layer.vote == LayerVoteType::Max) { + const auto ratio = refreshRate.getFps().getValue() / + mAppRequestRefreshRates.back()->getFps().getValue(); + // use ratio^2 to get a lower score the more we get further from peak + return ratio * ratio; } if (layer.vote == LayerVoteType::ExplicitExact) { @@ -210,7 +227,18 @@ float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& laye return divider == 1; } - return 0; + // If the layer frame rate is a divider of the refresh rate it should score + // the highest score. + if (getFrameRateDivider(refreshRate.getFps(), layer.desiredRefreshRate) > 0) { + return 1.0f * seamlessness; + } + + // The layer frame rate is not a divider of the refresh rate, + // there is a small penalty attached to the score to favor the frame rates + // the exactly matches the display refresh rate or a multiple. + constexpr float kNonExactMatchingPenalty = 0.99f; + return calculateNonExactMatchingLayerScoreLocked(layer, refreshRate) * seamlessness * + kNonExactMatchingPenalty; } struct RefreshRateScore { @@ -319,18 +347,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); @@ -342,7 +380,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 @@ -359,8 +399,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, @@ -398,10 +436,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(), @@ -422,7 +457,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; } @@ -440,9 +475,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; } @@ -452,7 +487,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) { @@ -583,7 +618,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) { @@ -592,7 +627,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; } @@ -635,10 +670,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; } } @@ -946,6 +981,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 2addc83225..3abf83d018 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -344,6 +344,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. // @@ -417,7 +421,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. @@ -434,6 +442,9 @@ private: float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&, bool isSeamlessSwitch) const REQUIRES(mLock); + float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&, + const RefreshRate&) const REQUIRES(mLock); + void updateDisplayModes(const DisplayModes& mode, DisplayModeId currentModeId) EXCLUDES(mLock); void initializeIdleTimer(); diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 1a5f0f44ba..12e741bbc2 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -663,7 +663,7 @@ void Scheduler::resetIdleTimer() { mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ false); } -void Scheduler::notifyTouchEvent() { +void Scheduler::onTouchHint() { if (mTouchTimer) { mTouchTimer->reset(); @@ -870,7 +870,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); @@ -883,21 +883,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 2f97328824..3f3debe33b 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; @@ -135,8 +138,8 @@ public: void resetIdleTimer(); - // Function that resets the touch timer. - void notifyTouchEvent(); + // Indicates that touch interaction is taking place. + void onTouchHint(); void setDisplayPowerState(bool normal); @@ -202,6 +205,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 82bd398278..81f20edca1 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; @@ -332,7 +334,6 @@ uint32_t SurfaceFlinger::maxGraphicsWidth; uint32_t SurfaceFlinger::maxGraphicsHeight; bool SurfaceFlinger::hasWideColorDisplay; ui::Rotation SurfaceFlinger::internalDisplayOrientation = ui::ROTATION_0; -bool SurfaceFlinger::useColorManagement; bool SurfaceFlinger::useContextPriority; Dataspace SurfaceFlinger::defaultCompositionDataspace = Dataspace::V0_SRGB; ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888; @@ -396,11 +397,6 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI maxGraphicsHeight = std::max(max_graphics_height(0), 0); hasWideColorDisplay = has_wide_color_display(false); - - // Android 12 and beyond, color management in display pipeline is turned on - // by default. - useColorManagement = use_color_management(true); - mDefaultCompositionDataspace = static_cast<ui::Dataspace>(default_composition_dataspace(Dataspace::V0_SRGB)); mWideColorGamutCompositionDataspace = static_cast<ui::Dataspace>(wcg_composition_dataspace( @@ -450,10 +446,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"); @@ -650,17 +643,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); } } @@ -670,12 +666,7 @@ std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() con status_t SurfaceFlinger::getPrimaryPhysicalDisplayId(PhysicalDisplayId* id) const { Mutex::Autolock lock(mStateLock); - const auto display = getInternalDisplayIdLocked(); - if (!display) { - return NAME_NOT_FOUND; - } - - *id = *display; + *id = getInternalDisplayIdLocked(); return NO_ERROR; } @@ -721,6 +712,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(); @@ -838,10 +830,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; @@ -1010,6 +1002,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(); @@ -1069,18 +1066,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; } @@ -1107,8 +1104,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()); @@ -1304,15 +1301,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()); } @@ -1336,34 +1333,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; } @@ -1683,7 +1687,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; @@ -1700,16 +1704,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() { @@ -1727,7 +1741,15 @@ nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const { void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp, std::optional<hal::VsyncPeriodNanos> vsyncPeriod) { - ATRACE_CALL(); + const std::string tracePeriod = [vsyncPeriod]() { + if (ATRACE_ENABLED() && vsyncPeriod) { + std::stringstream ss; + ss << "(" << *vsyncPeriod << ")"; + return ss.str(); + } + return std::string(); + }(); + ATRACE_FORMAT("onComposerHalVsync%s", tracePeriod.c_str()); Mutex::Autolock lock(mStateLock); const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId); @@ -1780,8 +1802,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 @@ -1812,7 +1834,7 @@ void SurfaceFlinger::onComposerHalSeamlessPossible(hal::HWDisplayId) { void SurfaceFlinger::onComposerHalRefresh(hal::HWDisplayId) { Mutex::Autolock lock(mStateLock); - repaintEverythingForHWC(); + scheduleRefresh(FrameHint::kNone); } void SurfaceFlinger::setVsyncEnabled(bool enabled) { @@ -1973,17 +1995,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) || @@ -1993,8 +2011,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"); @@ -2016,7 +2035,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 @@ -2038,25 +2056,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() { @@ -2080,7 +2096,6 @@ void SurfaceFlinger::onMessageRefresh() { refreshArgs.layersWithQueuedFrames.push_back(layerFE); } - refreshArgs.repaintEverything = mRepaintEverything.exchange(false); refreshArgs.outputColorSetting = useColorManagement ? mDisplayColorSetting : compositionengine::OutputColorSetting::kUnmanaged; @@ -2088,7 +2103,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(); @@ -2097,11 +2112,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); @@ -2110,8 +2125,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(); @@ -2170,10 +2183,11 @@ void SurfaceFlinger::onMessageRefresh() { bool SurfaceFlinger::handleMessageInvalidate() { ATRACE_CALL(); + // Send on commit callbacks + mTransactionCallbackInvoker.sendCallbacks(); + bool refreshNeeded = handlePageFlip(); - // Send on commit callbacks - mTransactionCallbackInvoker.sendCallbacks(); if (mVisibleRegionsDirty) { computeLayerBounds(); @@ -2294,13 +2308,9 @@ void SurfaceFlinger::postComposition() { } for (const auto& layer: mLayersWithQueuedFrames) { - const bool frameLatched = - layer->onPostComposition(display, glCompositionDoneFenceTime, - mPreviousPresentFences[0].fenceTime, compositorTiming); + layer->onPostComposition(display, glCompositionDoneFenceTime, + mPreviousPresentFences[0].fenceTime, compositorTiming); layer->releasePendingBuffer(/*dequeueReadyTime*/ now); - if (frameLatched) { - recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false)); - } } std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>> @@ -2333,7 +2343,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 || @@ -2364,6 +2374,7 @@ void SurfaceFlinger::postComposition() { mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0].fence); mTransactionCallbackInvoker.sendCallbacks(); + mTransactionCallbackInvoker.clearCompletedTransactions(); if (display && display->isInternal() && display->getPowerMode() == hal::PowerMode::ON && mPreviousPresentFences[0].fenceTime->isValid()) { @@ -2490,29 +2501,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, @@ -2749,6 +2757,7 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( display->setProjection(state.orientation, state.layerStackSpaceRect, state.orientedDisplaySpaceRect); display->setDisplayName(state.displayName); + display->setFlags(state.flags); return display; } @@ -2779,14 +2788,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()); @@ -2892,7 +2899,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); } } @@ -2972,8 +2979,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(); @@ -2987,18 +2994,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. @@ -3009,30 +3007,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; } } @@ -3046,20 +3043,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. @@ -3081,7 +3068,9 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) { }); } - commitTransaction(); + doCommitTransactions(); + signalSynchronousTransactions(CountDownLatch::eSyncTransaction); + mAnimTransactionPending = false; } void SurfaceFlinger::updateInputFlinger() { @@ -3113,17 +3102,35 @@ 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; - // 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 DisplayDevice* display = ON_MAIN_THREAD(getDisplayWithInputByLayer(layer)).get(); + ui::Transform displayTransform = ui::Transform(); + + if (enablePerWindowInputRotation()) { + // When calculating the screen bounds we ignore the transparent region since it may + // result in an unwanted offset. + const auto it = displayTransforms.find(display); + if (it != displayTransforms.end()) { + displayTransform = it->second; + } + } + const bool displayIsSecure = !display || display->isSecure(); + windowInfos.push_back(layer->fillInputInfo(displayTransform, displayIsSecure)); }); - mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, + mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, displayInfos, mInputWindowCommands.syncInputWindows); } @@ -3227,19 +3234,12 @@ 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) { - recordBufferingStats(l->getName(), l->getOccupancyHistory(true)); - // Ensure any buffers set to display on any children are released. if (l->isRemovedFromCurrentState()) { l->latchAndReleaseBuffer(); @@ -3278,11 +3278,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(); + } }); } } @@ -3290,7 +3289,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); } } @@ -3318,14 +3317,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); @@ -3333,7 +3332,7 @@ bool SurfaceFlinger::handlePageFlip() { ATRACE_NAME("!layer->shouldPresentNow()"); layer->useEmptyDamage(); } - } else { + } else { layer->useEmptyDamage(); } }); @@ -3386,10 +3385,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, @@ -3436,23 +3431,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; } @@ -3600,9 +3595,10 @@ bool SurfaceFlinger::transactionIsReadyToBeApplied( for (const ComposerState& state : states) { const layer_state_t& s = state.state; - const bool acquireFenceChanged = (s.what & layer_state_t::eAcquireFenceChanged); - if (acquireFenceChanged && s.acquireFence && !enableLatchUnsignaled && - s.acquireFence->getStatus() == Fence::Status::Unsignaled) { + const bool acquireFenceChanged = + s.bufferData.flags.test(BufferData::BufferDataChange::fenceChanged); + if (acquireFenceChanged && s.bufferData.acquireFence && !enableLatchUnsignaled && + s.bufferData.acquireFence->getStatus() == Fence::Status::Unsignaled) { ATRACE_NAME("fence unsignaled"); return false; } @@ -3765,8 +3761,7 @@ void SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin // that listeners with SurfaceControls will start registration during setClientStateLocked // below. for (const auto& listener : listenerCallbacks) { - mTransactionCallbackInvoker.startRegistration(listener); - mTransactionCallbackInvoker.endRegistration(listener); + mTransactionCallbackInvoker.addEmptyTransaction(listener); } std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> listenerCallbacksWithSurfaces; @@ -3784,10 +3779,6 @@ void SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin } } - for (const auto& listenerCallback : listenerCallbacksWithSurfaces) { - mTransactionCallbackInvoker.endRegistration(listenerCallback); - } - // If the state doesn't require a traversal and there are callbacks, send them now if (!(clientStateFlags & eTraversalNeeded) && hasListenerCallbacks) { mTransactionCallbackInvoker.sendCallbacks(); @@ -3917,14 +3908,12 @@ uint32_t SurfaceFlinger::setClientStateLocked( ListenerCallbacks onCommitCallbacks = listener.filter(CallbackId::Type::ON_COMMIT); if (!onCommitCallbacks.callbackIds.empty()) { - mTransactionCallbackInvoker.startRegistration(onCommitCallbacks); filteredListeners.push_back(onCommitCallbacks); outListenerCallbacks.insert(onCommitCallbacks); } ListenerCallbacks onCompleteCallbacks = listener.filter(CallbackId::Type::ON_COMPLETE); if (!onCompleteCallbacks.callbackIds.empty()) { - mTransactionCallbackInvoker.startRegistration(onCompleteCallbacks); filteredListeners.push_back(onCompleteCallbacks); outListenerCallbacks.insert(onCompleteCallbacks); } @@ -4099,9 +4088,6 @@ uint32_t SurfaceFlinger::setClientStateLocked( if (what & layer_state_t::eCropChanged) { if (layer->setCrop(s.crop)) flags |= eTraversalNeeded; } - if (what & layer_state_t::eAcquireFenceChanged) { - if (layer->setAcquireFence(s.acquireFence)) flags |= eTraversalNeeded; - } if (what & layer_state_t::eDataspaceChanged) { if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded; } @@ -4202,7 +4188,7 @@ uint32_t SurfaceFlinger::setClientStateLocked( mInputInfoChanged = true; } } else { - ALOGE("Attempt to update InputPolicyFlags without permission ACCESS_SURFACE_FLINGER"); + ALOGE("Attempt to update DropInputMode without permission ACCESS_SURFACE_FLINGER"); } } // This has to happen after we reparent children because when we reparent to null we remove @@ -4228,43 +4214,11 @@ uint32_t SurfaceFlinger::setClientStateLocked( callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface)); } } - bool bufferChanged = what & layer_state_t::eBufferChanged; - bool cacheIdChanged = what & layer_state_t::eCachedBufferChanged; - bool bufferSizeExceedsLimit = false; - std::shared_ptr<renderengine::ExternalTexture> buffer; - if (bufferChanged && cacheIdChanged && s.buffer != nullptr) { - bufferSizeExceedsLimit = - exceedsMaxRenderTargetSize(s.buffer->getWidth(), s.buffer->getHeight()); - if (!bufferSizeExceedsLimit) { - ClientCache::getInstance().add(s.cachedBuffer, s.buffer); - buffer = ClientCache::getInstance().get(s.cachedBuffer); - } - } else if (cacheIdChanged) { - buffer = ClientCache::getInstance().get(s.cachedBuffer); - } else if (bufferChanged && s.buffer != nullptr) { - bufferSizeExceedsLimit = - exceedsMaxRenderTargetSize(s.buffer->getWidth(), s.buffer->getHeight()); - if (!bufferSizeExceedsLimit) { - buffer = std::make_shared< - renderengine::ExternalTexture>(s.buffer, getRenderEngine(), - renderengine::ExternalTexture::Usage::READABLE); - } - } - ALOGE_IF(bufferSizeExceedsLimit, - "Attempted to create an ExternalTexture for layer %s that exceeds render target size " - "limit.", - layer->getDebugName()); - if (buffer) { - const bool frameNumberChanged = what & layer_state_t::eFrameNumberChanged; - const uint64_t frameNumber = frameNumberChanged - ? s.frameNumber - : layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1; - - if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp, - s.cachedBuffer, frameNumber, dequeueBufferTimestamp, frameTimelineInfo, - s.releaseBufferListener, s.releaseBufferEndpoint)) { - flags |= eTraversalNeeded; - } + + if (what & layer_state_t::eBufferChanged && + layer->setBuffer(s.bufferData, postTime, desiredPresentTime, isAutoTimestamp, + dequeueBufferTimestamp, frameTimelineInfo)) { + flags |= eTraversalNeeded; } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) { layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime); } @@ -4521,7 +4475,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(); @@ -4552,23 +4506,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) { @@ -4613,7 +4566,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) { @@ -4855,24 +4808,6 @@ void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const { bucketTimeSec, percent); } -void SurfaceFlinger::recordBufferingStats(const std::string& layerName, - std::vector<OccupancyTracker::Segment>&& history) { - Mutex::Autolock lock(getBE().mBufferingStatsMutex); - auto& stats = getBE().mBufferingStats[layerName]; - for (const auto& segment : history) { - if (!segment.usedThirdBuffer) { - stats.twoBufferTime += segment.totalTime; - } - if (segment.occupancyAverage < 1.0f) { - stats.doubleBufferedTime += segment.totalTime; - } else if (segment.occupancyAverage < 2.0f) { - stats.tripleBufferedTime += segment.totalTime; - } - ++stats.numSegments; - stats.totalTime += segment.totalTime; - } -} - void SurfaceFlinger::dumpFrameEventsLocked(std::string& result) { result.append("Layer frame timestamps:\n"); // Traverse all layers to dump frame-events for each layer @@ -4880,38 +4815,6 @@ void SurfaceFlinger::dumpFrameEventsLocked(std::string& result) { [&] (Layer* layer) { layer->dumpFrameEvents(result); }); } -void SurfaceFlinger::dumpBufferingStats(std::string& result) const { - result.append("Buffering stats:\n"); - result.append(" [Layer name] <Active time> <Two buffer> " - "<Double buffered> <Triple buffered>\n"); - Mutex::Autolock lock(getBE().mBufferingStatsMutex); - typedef std::tuple<std::string, float, float, float> BufferTuple; - std::map<float, BufferTuple, std::greater<float>> sorted; - for (const auto& statsPair : getBE().mBufferingStats) { - const char* name = statsPair.first.c_str(); - const SurfaceFlingerBE::BufferingStats& stats = statsPair.second; - if (stats.numSegments == 0) { - continue; - } - float activeTime = ns2ms(stats.totalTime) / 1000.0f; - float twoBufferRatio = static_cast<float>(stats.twoBufferTime) / - stats.totalTime; - float doubleBufferRatio = static_cast<float>( - stats.doubleBufferedTime) / stats.totalTime; - float tripleBufferRatio = static_cast<float>( - stats.tripleBufferedTime) / stats.totalTime; - sorted.insert({activeTime, {name, twoBufferRatio, - doubleBufferRatio, tripleBufferRatio}}); - } - for (const auto& sortedPair : sorted) { - float activeTime = sortedPair.first; - const BufferTuple& values = sortedPair.second; - StringAppendF(&result, " [%s] %.2f %.3f %.3f %.3f\n", std::get<0>(values).c_str(), - activeTime, std::get<1>(values), std::get<2>(values), std::get<3>(values)); - } - result.append("\n"); -} - void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const { for (const auto& [token, display] : mDisplays) { const auto displayId = PhysicalDisplayId::tryCast(display->getId()); @@ -5006,7 +4909,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(), [&]() { @@ -5103,8 +5006,6 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co StringAppendF(&result, "HWC missed frame count: %u\n", mHwcFrameMissedCount.load()); StringAppendF(&result, "GPU missed frame count: %u\n\n", mGpuFrameMissedCount.load()); - dumpBufferingStats(result); - /* * Dump the visible layer list */ @@ -5227,7 +5128,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); @@ -5237,6 +5138,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"); } @@ -5437,9 +5343,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); @@ -5456,47 +5361,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; @@ -5551,12 +5452,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; @@ -5610,8 +5514,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. @@ -5742,37 +5645,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) { @@ -5789,12 +5680,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 @@ -5835,14 +5724,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); @@ -5859,8 +5745,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r if (error != OK) { return error; } - invalidateHwcGeometry(); - repaintEverything(); + scheduleRepaint(); return NO_ERROR; } } @@ -5868,17 +5753,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); @@ -6102,7 +5976,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; @@ -6110,21 +5984,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 = @@ -6209,7 +6076,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; } @@ -6424,7 +6295,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; @@ -6436,7 +6306,6 @@ status_t SurfaceFlinger::renderScreenImplLocked( renderArea.needsFiltering(), renderArea.isSecure(), useProtected, - clearRegion, layerStackSpaceRect, clientCompositionDisplay.outputDataspace, true, /* realContentIsVisible */ @@ -6474,16 +6343,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)); @@ -6496,7 +6366,7 @@ status_t SurfaceFlinger::renderScreenImplLocked( // Always switch back to unprotected context. getRenderEngine().useProtectedContext(false); - return NO_ERROR; + return status; } void SurfaceFlinger::windowInfosReported() { @@ -6523,12 +6393,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()) { @@ -6887,33 +6757,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 dba2bd6b48..1f0e42ddf3 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -32,7 +32,6 @@ #include <gui/ISurfaceComposerClient.h> #include <gui/ITransactionCompletedListener.h> #include <gui/LayerState.h> -#include <gui/OccupancyTracker.h> #include <layerproto/LayerProtoHeader.h> #include <math/mat4.h> #include <renderengine/LayerSettings.h> @@ -89,6 +88,7 @@ namespace android { class Client; class EventThread; +class FlagManager; class FpsReporter; class TunnelModeEnabledReporter; class HdrLayerInfoReporter; @@ -154,22 +154,6 @@ struct SurfaceFlingerBE { nsecs_t mFrameBuckets[NUM_BUCKETS] = {}; nsecs_t mTotalTime = 0; std::atomic<nsecs_t> mLastSwapTime = 0; - - // Double- vs. triple-buffering stats - struct BufferingStats { - size_t numSegments = 0; - nsecs_t totalTime = 0; - - // "Two buffer" means that a third buffer was never used, whereas - // "double-buffered" means that on average the segment only used two - // buffers (though it may have used a third for some part of the - // segment) - nsecs_t twoBufferTime = 0; - nsecs_t doubleBufferedTime = 0; - nsecs_t tripleBufferedTime = 0; - }; - mutable Mutex mBufferingStatsMutex; - std::unordered_map<std::string, BufferingStats> mBufferingStats; }; class SurfaceFlinger : public BnSurfaceComposer, @@ -243,7 +227,7 @@ public: static ui::Rotation internalDisplayOrientation; // Indicate if device wants color management on its display. - static bool useColorManagement; + static const constexpr bool useColorManagement = true; static bool useContextPriority; @@ -285,8 +269,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 +336,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); @@ -359,6 +346,10 @@ protected: return static_cast<bool>(findDisplay(p)); } + bool exceedsMaxRenderTargetSize(uint32_t width, uint32_t height) const { + return width > mMaxRenderTargetSize || height > mMaxRenderTargetSize; + } + private: friend class BufferLayer; friend class BufferQueueLayer; @@ -630,12 +621,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*) @@ -748,8 +737,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 +753,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 +784,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 +798,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 +829,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, @@ -933,11 +924,7 @@ private: void readPersistentProperties(); - bool exceedsMaxRenderTargetSize(uint32_t width, uint32_t height) const { - 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 +1015,6 @@ private: /* * Compositing */ - void invalidateHwcGeometry(); - void postComposition(); void getCompositorTiming(CompositorTiming* compositorTiming); void updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime, @@ -1113,15 +1098,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. @@ -1152,10 +1141,6 @@ private: void dumpStaticScreenStats(std::string& result) const; // Not const because each Layer needs to query Fences and cache timestamps. void dumpFrameEventsLocked(std::string& result); - - void recordBufferingStats(const std::string& layerName, - std::vector<OccupancyTracker::Segment>&& history); - void dumpBufferingStats(std::string& result) const; void dumpDisplayIdentificationData(std::string& result) const REQUIRES(mStateLock); void dumpRawDisplayIdentificationData(const DumpArgs&, std::string& result) const; void dumpWideColorInfo(std::string& result) const REQUIRES(mStateLock); @@ -1202,8 +1187,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 +1229,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 +1257,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 +1291,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 +1494,8 @@ private: wp<IBinder> mActiveDisplayToken GUARDED_BY(mStateLock); const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker; + + std::unique_ptr<FlagManager> mFlagManager; }; } // namespace android diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp index e15eae8c36..a8117f7f57 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.cpp +++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp @@ -195,17 +195,6 @@ SurfaceFlingerProperties::primary_display_orientation_values primary_display_ori return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_0; } -bool use_color_management(bool defaultValue) { - auto tmpuseColorManagement = SurfaceFlingerProperties::use_color_management(); - auto tmpHasHDRDisplayVal = has_HDR_display(defaultValue); - auto tmpHasWideColorDisplayVal = has_wide_color_display(defaultValue); - - auto tmpuseColorManagementVal = tmpuseColorManagement.has_value() ? *tmpuseColorManagement : - defaultValue; - - return tmpuseColorManagementVal || tmpHasHDRDisplayVal || tmpHasWideColorDisplayVal; -} - int64_t default_composition_dataspace(Dataspace defaultValue) { auto temp = SurfaceFlingerProperties::default_composition_dataspace(); if (temp.has_value()) { diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h index 039d316a95..ed182608fb 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.h +++ b/services/surfaceflinger/SurfaceFlingerProperties.h @@ -59,8 +59,6 @@ bool start_graphics_allocator_service(bool defaultValue); SurfaceFlingerProperties::primary_display_orientation_values primary_display_orientation( SurfaceFlingerProperties::primary_display_orientation_values defaultValue); -bool use_color_management(bool defaultValue); - int64_t default_composition_dataspace( android::hardware::graphics::common::V1_2::Dataspace defaultValue); 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/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp index 7c1f21f9e4..bf2038b277 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.cpp +++ b/services/surfaceflinger/TimeStats/TimeStats.cpp @@ -564,7 +564,7 @@ bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName layerRecords += record.second.stats.size(); } - return mTimeStats.stats.size() < MAX_NUM_LAYER_STATS; + return layerRecords < MAX_NUM_LAYER_STATS; } void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp index 6af69f0ef2..4b12a2640b 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.cpp +++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp @@ -49,121 +49,25 @@ static bool containsOnCommitCallbacks(const std::vector<CallbackId>& callbacks) return !callbacks.empty() && callbacks.front().type == CallbackId::Type::ON_COMMIT; } -TransactionCallbackInvoker::~TransactionCallbackInvoker() { - { - std::lock_guard lock(mMutex); - for (const auto& [listener, transactionStats] : mCompletedTransactions) { - listener->unlinkToDeath(mDeathRecipient); - } - } -} - -status_t TransactionCallbackInvoker::startRegistration(const ListenerCallbacks& listenerCallbacks) { - std::lock_guard lock(mMutex); - - auto [itr, inserted] = mRegisteringTransactions.insert(listenerCallbacks); +void TransactionCallbackInvoker::addEmptyTransaction(const ListenerCallbacks& listenerCallbacks) { auto& [listener, callbackIds] = listenerCallbacks; - - if (inserted) { - if (mCompletedTransactions.count(listener) == 0) { - status_t err = listener->linkToDeath(mDeathRecipient); - if (err != NO_ERROR) { - ALOGE("cannot add callback because linkToDeath failed, err: %d", err); - return err; - } - } - auto& transactionStatsDeque = mCompletedTransactions[listener]; - transactionStatsDeque.emplace_back(callbackIds); - } - - return NO_ERROR; -} - -status_t TransactionCallbackInvoker::endRegistration(const ListenerCallbacks& listenerCallbacks) { - std::lock_guard lock(mMutex); - - auto itr = mRegisteringTransactions.find(listenerCallbacks); - if (itr == mRegisteringTransactions.end()) { - ALOGE("cannot end a registration that does not exist"); - return BAD_VALUE; - } - - mRegisteringTransactions.erase(itr); - - return NO_ERROR; -} - -bool TransactionCallbackInvoker::isRegisteringTransaction( - const sp<IBinder>& transactionListener, const std::vector<CallbackId>& callbackIds) { - ListenerCallbacks listenerCallbacks(transactionListener, callbackIds); - - auto itr = mRegisteringTransactions.find(listenerCallbacks); - return itr != mRegisteringTransactions.end(); -} - -status_t TransactionCallbackInvoker::registerPendingCallbackHandle( - const sp<CallbackHandle>& handle) { - std::lock_guard lock(mMutex); - - // If we can't find the transaction stats something has gone wrong. The client should call - // startRegistration before trying to register a pending callback handle. - TransactionStats* transactionStats; - status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats); - if (err != NO_ERROR) { - ALOGE("cannot find transaction stats"); - return err; - } - - mPendingTransactions[handle->listener][handle->callbackIds]++; - return NO_ERROR; -} - -status_t TransactionCallbackInvoker::finalizeCallbackHandle(const sp<CallbackHandle>& handle, - const std::vector<JankData>& jankData) { - auto listener = mPendingTransactions.find(handle->listener); - if (listener != mPendingTransactions.end()) { - auto& pendingCallbacks = listener->second; - auto pendingCallback = pendingCallbacks.find(handle->callbackIds); - - if (pendingCallback != pendingCallbacks.end()) { - auto& pendingCount = pendingCallback->second; - - // Decrease the pending count for this listener - if (--pendingCount == 0) { - pendingCallbacks.erase(pendingCallback); - } - } else { - ALOGW("there are more latched callbacks than there were registered callbacks"); - } - if (listener->second.size() == 0) { - mPendingTransactions.erase(listener); - } - } else { - ALOGW("cannot find listener in mPendingTransactions"); - } - - status_t err = addCallbackHandle(handle, jankData); - if (err != NO_ERROR) { - ALOGE("could not add callback handle"); - return err; - } - return NO_ERROR; + auto& transactionStatsDeque = mCompletedTransactions[listener]; + transactionStatsDeque.emplace_back(callbackIds); } -status_t TransactionCallbackInvoker::finalizeOnCommitCallbackHandles( +status_t TransactionCallbackInvoker::addOnCommitCallbackHandles( const std::deque<sp<CallbackHandle>>& handles, std::deque<sp<CallbackHandle>>& outRemainingHandles) { if (handles.empty()) { return NO_ERROR; } - std::lock_guard lock(mMutex); const std::vector<JankData>& jankData = std::vector<JankData>(); for (const auto& handle : handles) { if (!containsOnCommitCallbacks(handle->callbackIds)) { outRemainingHandles.push_back(handle); continue; } - status_t err = finalizeCallbackHandle(handle, jankData); + status_t err = addCallbackHandle(handle, jankData); if (err != NO_ERROR) { return err; } @@ -172,14 +76,13 @@ status_t TransactionCallbackInvoker::finalizeOnCommitCallbackHandles( return NO_ERROR; } -status_t TransactionCallbackInvoker::finalizePendingCallbackHandles( +status_t TransactionCallbackInvoker::addCallbackHandles( const std::deque<sp<CallbackHandle>>& handles, const std::vector<JankData>& jankData) { if (handles.empty()) { return NO_ERROR; } - std::lock_guard lock(mMutex); for (const auto& handle : handles) { - status_t err = finalizeCallbackHandle(handle, jankData); + status_t err = addCallbackHandle(handle, jankData); if (err != NO_ERROR) { return err; } @@ -190,8 +93,6 @@ status_t TransactionCallbackInvoker::finalizePendingCallbackHandles( status_t TransactionCallbackInvoker::registerUnpresentedCallbackHandle( const sp<CallbackHandle>& handle) { - std::lock_guard lock(mMutex); - return addCallbackHandle(handle, std::vector<JankData>()); } @@ -208,9 +109,8 @@ status_t TransactionCallbackInvoker::findTransactionStats( return NO_ERROR; } } - - ALOGE("could not find transaction stats"); - return BAD_VALUE; + *outTransactionStats = &transactionStatsDeque.emplace_back(callbackIds); + return NO_ERROR; } status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& handle, @@ -244,13 +144,10 @@ status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& } void TransactionCallbackInvoker::addPresentFence(const sp<Fence>& presentFence) { - std::lock_guard<std::mutex> lock(mMutex); mPresentFence = presentFence; } void TransactionCallbackInvoker::sendCallbacks() { - std::lock_guard lock(mMutex); - // For each listener auto completedTransactionsItr = mCompletedTransactions.begin(); while (completedTransactionsItr != mCompletedTransactions.end()) { @@ -263,27 +160,9 @@ void TransactionCallbackInvoker::sendCallbacks() { while (transactionStatsItr != transactionStatsDeque.end()) { auto& transactionStats = *transactionStatsItr; - // If this transaction is still registering, it is not safe to send a callback - // because there could be surface controls that haven't been added to - // transaction stats or mPendingTransactions. - if (isRegisteringTransaction(listener, transactionStats.callbackIds)) { - break; - } - - // If we are still waiting on the callback handles for this transaction, stop - // here because all transaction callbacks for the same listener must come in order - auto pendingTransactions = mPendingTransactions.find(listener); - if (pendingTransactions != mPendingTransactions.end() && - pendingTransactions->second.count(transactionStats.callbackIds) != 0) { - break; - } - // If the transaction has been latched if (transactionStats.latchTime >= 0 && !containsOnCommitCallbacks(transactionStats.callbackIds)) { - if (!mPresentFence) { - break; - } transactionStats.presentFence = mPresentFence; } @@ -303,20 +182,9 @@ void TransactionCallbackInvoker::sendCallbacks() { // we get pointers that compare unequal in the SF process. interface_cast<ITransactionCompletedListener>(listenerStats.listener) ->onTransactionCompleted(listenerStats); - if (transactionStatsDeque.empty()) { - listener->unlinkToDeath(mDeathRecipient); - completedTransactionsItr = - mCompletedTransactions.erase(completedTransactionsItr); - } else { - completedTransactionsItr++; - } - } else { - completedTransactionsItr = - mCompletedTransactions.erase(completedTransactionsItr); } - } else { - completedTransactionsItr++; } + completedTransactionsItr++; } if (mPresentFence) { diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h index 6f4d812ec5..71ca6e5360 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.h +++ b/services/surfaceflinger/TransactionCallbackInvoker.h @@ -56,79 +56,38 @@ public: class TransactionCallbackInvoker { public: - ~TransactionCallbackInvoker(); - - // Adds listener and callbackIds in case there are no SurfaceControls that are supposed - // to be included in the callback. This functions should be call before attempting to register - // any callback handles. - status_t startRegistration(const ListenerCallbacks& listenerCallbacks); - // Ends the registration. After this is called, no more CallbackHandles will be registered. - // It is safe to send a callback if the Transaction doesn't have any Pending callback handles. - status_t endRegistration(const ListenerCallbacks& listenerCallbacks); - - // Informs the TransactionCallbackInvoker that there is a Transaction with a CallbackHandle - // that needs to be latched and presented this frame. This function should be called once the - // layer has received the CallbackHandle so the TransactionCallbackInvoker knows not to send - // a callback for that Listener/Transaction pair until that CallbackHandle has been latched and - // presented. - status_t registerPendingCallbackHandle(const sp<CallbackHandle>& handle); - // Notifies the TransactionCallbackInvoker that a pending CallbackHandle has been presented. - status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles, + status_t addCallbackHandles(const std::deque<sp<CallbackHandle>>& handles, const std::vector<JankData>& jankData); - status_t finalizeOnCommitCallbackHandles(const std::deque<sp<CallbackHandle>>& handles, + status_t addOnCommitCallbackHandles(const std::deque<sp<CallbackHandle>>& handles, std::deque<sp<CallbackHandle>>& outRemainingHandles); // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and // presented this frame. status_t registerUnpresentedCallbackHandle(const sp<CallbackHandle>& handle); + void addEmptyTransaction(const ListenerCallbacks& listenerCallbacks); void addPresentFence(const sp<Fence>& presentFence); void sendCallbacks(); + void clearCompletedTransactions() { + mCompletedTransactions.clear(); + } -private: + status_t addCallbackHandle(const sp<CallbackHandle>& handle, + const std::vector<JankData>& jankData); - bool isRegisteringTransaction(const sp<IBinder>& transactionListener, - const std::vector<CallbackId>& callbackIds) REQUIRES(mMutex); +private: status_t findTransactionStats(const sp<IBinder>& listener, const std::vector<CallbackId>& callbackIds, - TransactionStats** outTransactionStats) REQUIRES(mMutex); + TransactionStats** outTransactionStats); + - status_t addCallbackHandle(const sp<CallbackHandle>& handle, - const std::vector<JankData>& jankData) REQUIRES(mMutex); - - status_t finalizeCallbackHandle(const sp<CallbackHandle>& handle, - const std::vector<JankData>& jankData) REQUIRES(mMutex); - - class CallbackDeathRecipient : public IBinder::DeathRecipient { - public: - // This function is a no-op. isBinderAlive needs a linked DeathRecipient to work. - // Death recipients needs a binderDied function. - // - // (isBinderAlive checks if BpBinder's mAlive is 0. mAlive is only set to 0 in sendObituary. - // sendObituary is only called if linkToDeath was called with a DeathRecipient.) - void binderDied(const wp<IBinder>& /*who*/) override {} - }; - sp<CallbackDeathRecipient> mDeathRecipient = - new CallbackDeathRecipient(); - - std::mutex mMutex; - std::condition_variable_any mConditionVariable; - - std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> mRegisteringTransactions - GUARDED_BY(mMutex); - - std::unordered_map< - sp<IBinder>, - std::unordered_map<std::vector<CallbackId>, uint32_t /*count*/, CallbackIdsHash>, - IListenerHash> - mPendingTransactions GUARDED_BY(mMutex); std::unordered_map<sp<IBinder>, std::deque<TransactionStats>, IListenerHash> - mCompletedTransactions GUARDED_BY(mMutex); + mCompletedTransactions; - sp<Fence> mPresentFence GUARDED_BY(mMutex); + sp<Fence> mPresentFence; }; } // namespace android 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/Android.bp b/services/surfaceflinger/tests/Android.bp index 32ad8732ad..7b86229556 100644 --- a/services/surfaceflinger/tests/Android.bp +++ b/services/surfaceflinger/tests/Android.bp @@ -60,7 +60,7 @@ cc_test { "android.hardware.graphics.composer@2.1", ], shared_libs: [ - "android.hardware.graphics.common-V2-ndk", + "android.hardware.graphics.common-V3-ndk", "android.hardware.graphics.common@1.2", "libandroid", "libbase", 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..94e1e0c95c 100644 --- a/services/surfaceflinger/tests/IPC_test.cpp +++ b/services/surfaceflinger/tests/IPC_test.cpp @@ -159,10 +159,9 @@ 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) + .setBuffer(mSurfaceControl, gb, fence) .show(mSurfaceControl) .addTransactionCompletedCallback(mCallbackHelper.function, mCallbackHelper.getContext()); @@ -232,7 +231,7 @@ public: mDisplayHeight = mode.resolution.getHeight(); Transaction setupTransaction; - setupTransaction.setDisplayLayerStack(mPrimaryDisplay, 0); + setupTransaction.setDisplayLayerStack(mPrimaryDisplay, ui::DEFAULT_LAYER_STACK); setupTransaction.apply(); } @@ -310,10 +309,9 @@ 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) + .setBuffer(sc, gb, fence) .show(sc) .addTransactionCompletedCallback(helper1.function, helper1.getContext()); diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp index 965aac301d..e8759e503b 100644 --- a/services/surfaceflinger/tests/LayerCallback_test.cpp +++ b/services/surfaceflinger/tests/LayerCallback_test.cpp @@ -66,8 +66,7 @@ public: return err; } - transaction.setBuffer(layer, buffer); - transaction.setAcquireFence(layer, fence); + transaction.setBuffer(layer, buffer, fence); } if (setBackgroundColor) { diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp index c8eeac66e9..0e2bc3df0e 100644 --- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp @@ -1353,7 +1353,7 @@ TEST_P(LayerRenderTypeTransactionTest, DISABLED_SetFenceBasic_BufferState) { return; } - Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply(); + Transaction().setBuffer(layer, buffer, fence).apply(); status_t status = fence->wait(1000); ASSERT_NE(static_cast<status_t>(Fence::Status::Unsignaled), status); @@ -1375,7 +1375,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetFenceNull_BufferState) { sp<Fence> fence = Fence::NO_FENCE; - Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply(); + Transaction().setBuffer(layer, buffer, fence).apply(); auto shot = getScreenCapture(); shot->expectColor(Rect(0, 0, 32, 32), Color::RED); 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 ab1d5896ae..e5872c10e5 100644 --- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp @@ -732,7 +732,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/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp index c4d42fad36..3847a51b7c 100644 --- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp +++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp @@ -85,9 +85,7 @@ public: sp<Fence> fence, CallbackHelper& callback, const ReleaseCallbackId& id, ReleaseBufferCallbackHelper& releaseCallback) { Transaction t; - t.setFrameNumber(layer, id.framenumber); - t.setBuffer(layer, buffer, id, releaseCallback.getCallback()); - t.setAcquireFence(layer, fence); + t.setBuffer(layer, buffer, fence, id.framenumber, id, releaseCallback.getCallback()); t.addTransactionCompletedCallback(callback.function, callback.getContext()); t.apply(); } @@ -302,8 +300,8 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_FrameDropping) { nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count(); Transaction t; - t.setBuffer(layer, firstBuffer, firstBufferCallbackId, releaseCallback->getCallback()); - t.setAcquireFence(layer, Fence::NO_FENCE); + t.setBuffer(layer, firstBuffer, std::nullopt, std::nullopt, firstBufferCallbackId, + releaseCallback->getCallback()); t.addTransactionCompletedCallback(transactionCallback.function, transactionCallback.getContext()); t.setDesiredPresentTime(time); @@ -318,8 +316,8 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_FrameDropping) { // Dropping frames in transaction queue emits a callback sp<GraphicBuffer> secondBuffer = getBuffer(); ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber()); - t.setBuffer(layer, secondBuffer, secondBufferCallbackId, releaseCallback->getCallback()); - t.setAcquireFence(layer, Fence::NO_FENCE); + t.setBuffer(layer, secondBuffer, std::nullopt, std::nullopt, secondBufferCallbackId, + releaseCallback->getCallback()); t.addTransactionCompletedCallback(transactionCallback.function, transactionCallback.getContext()); t.setDesiredPresentTime(time); @@ -361,10 +359,8 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Different_Processes) { ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber()); Transaction transaction1; - transaction1.setFrameNumber(layer, secondBufferCallbackId.framenumber); - transaction1.setBuffer(layer, secondBuffer, secondBufferCallbackId, - releaseCallback->getCallback()); - transaction1.setAcquireFence(layer, Fence::NO_FENCE); + transaction1.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber, + secondBufferCallbackId, releaseCallback->getCallback()); transaction1.addTransactionCompletedCallback(callback1.function, callback1.getContext()); // Set a different TransactionCompletedListener to mimic a second process 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..6cb3052053 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -38,7 +38,6 @@ DisplayTransactionTest::DisplayTransactionTest() { // Default to no wide color display support configured mFlinger.mutableHasWideColorDisplay() = false; - mFlinger.mutableUseColorManagement() = false; mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged; mFlinger.setCreateBufferQueueFunction([](auto, auto, auto) { @@ -122,7 +121,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 +138,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..7746e73e7a 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -233,7 +233,7 @@ struct HwcDisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> { // 2) HalVirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC. // 3) GpuVirtualDisplayIdType for virtual display without HWC backing. template <typename DisplayIdType, int width, int height, Critical critical, Async async, - Secure secure, Primary primary, int grallocUsage> + Secure secure, Primary primary, int grallocUsage, int displayFlags> struct DisplayVariant { using DISPLAY_ID = DisplayIdGetter<DisplayIdType>; using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>; @@ -261,17 +261,14 @@ struct DisplayVariant { // Whether the display is primary static constexpr Primary PRIMARY = primary; + static constexpr int DISPLAY_FLAGS = displayFlags; + static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) { auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder(); ceDisplayArgs.setId(DISPLAY_ID::get()) .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 +276,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 +385,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) @@ -475,16 +471,19 @@ struct HwcDisplayVariant { constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB; +constexpr int PHYSICAL_DISPLAY_FLAGS = 0x1; + template <typename PhysicalDisplay, int width, int height, Critical critical> struct PhysicalDisplayVariant : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical, Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY, - GRALLOC_USAGE_PHYSICAL_DISPLAY>, - HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL, - DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, - critical, Async::FALSE, Secure::TRUE, - PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>, - PhysicalDisplay> {}; + GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>, + HwcDisplayVariant< + PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL, + DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical, + Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY, + GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>, + PhysicalDisplay> {}; template <bool hasIdentificationData> struct PrimaryDisplay { @@ -526,13 +525,16 @@ using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 120 // A virtual display not supported by the HWC. constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0; +constexpr int VIRTUAL_DISPLAY_FLAGS = 0x0; + template <int width, int height, Secure secure> struct NonHwcVirtualDisplayVariant : DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE, secure, - Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY> { - using Base = - DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE, - secure, Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>; + Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY, + VIRTUAL_DISPLAY_FLAGS> { + using Base = DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, + Async::TRUE, secure, Primary::FALSE, + GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS>; static void injectHwcDisplay(DisplayTransactionTest*) {} @@ -575,13 +577,16 @@ constexpr uint32_t GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY = GRALLOC_USAGE_HW_COMPOSER template <int width, int height, Secure secure> struct HwcVirtualDisplayVariant : DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, Async::TRUE, - secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>, - HwcDisplayVariant<HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL, - DisplayVariant<HalVirtualDisplayIdType<42>, width, height, - Critical::FALSE, Async::TRUE, secure, Primary::FALSE, - GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> { + secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY, + VIRTUAL_DISPLAY_FLAGS>, + HwcDisplayVariant< + HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL, + DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, + Async::TRUE, secure, Primary::FALSE, + GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS>> { using Base = DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, - Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>; + Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER, + VIRTUAL_DISPLAY_FLAGS>; using Self = HwcVirtualDisplayVariant<width, height, secure>; static std::shared_ptr<compositionengine::Display> injectCompositionDisplay( @@ -671,7 +676,6 @@ struct WideColorNotSupportedVariant { static constexpr bool WIDE_COLOR_SUPPORTED = false; static void injectConfigChange(DisplayTransactionTest* test) { - test->mFlinger.mutableUseColorManagement() = true; test->mFlinger.mutableHasWideColorDisplay() = true; } @@ -690,7 +694,6 @@ struct WideColorSupportNotConfiguredVariant { static void injectConfigChange(DisplayTransactionTest* test) { test->mFlinger.mutableHasWideColorDisplay() = false; - test->mFlinger.mutableUseColorManagement() = false; test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged; } 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..a8e3e5ee6d 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), @@ -158,7 +175,6 @@ protected: RefreshRate mExpected30Config = {mConfig30, RefreshRate::ConstructorTag(0)}; RefreshRate mExpected120Config = {mConfig120, RefreshRate::ConstructorTag(0)}; -private: DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod, ui::Size resolution = ui::Size()); }; @@ -181,7 +197,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 +348,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 +1255,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 +2155,67 @@ 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})); +} + +// b/190578904 +TEST_F(RefreshRateConfigsTest, getBestRefreshRate_deviceWithCloseRefreshRates) { + constexpr int kMinRefreshRate = 10; + constexpr int kMaxRefreshRate = 240; + + DisplayModes displayModes; + for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) { + constexpr int32_t kGroup = 0; + const auto refreshRate = Fps(static_cast<float>(fps)); + displayModes.push_back( + createDisplayMode(DisplayModeId(fps), kGroup, refreshRate.getPeriodNsecs())); + } + + const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false}; + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(displayModes, + /*currentConfigId=*/displayModes[0]->getId()); + + auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}}; + const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) { + layers[0].desiredRefreshRate = fps; + layers[0].vote = vote; + EXPECT_EQ(fps.getIntValue(), + refreshRateConfigs->getBestRefreshRate(layers, globalSignals) + .getFps() + .getIntValue()) + << "Failed for " << RefreshRateConfigs::layerVoteTypeString(vote); + }; + + for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) { + const auto refreshRate = Fps(static_cast<float>(fps)); + testRefreshRate(refreshRate, LayerVoteType::Heuristic); + testRefreshRate(refreshRate, LayerVoteType::ExplicitDefault); + testRefreshRate(refreshRate, LayerVoteType::ExplicitExactOrMultiple); + testRefreshRate(refreshRate, LayerVoteType::ExplicitExact); + } +} + TEST_F(RefreshRateConfigsTest, testComparisonOperator) { EXPECT_TRUE(mExpected60Config < mExpected90Config); EXPECT_FALSE(mExpected60Config < mExpected60Config); @@ -2123,6 +2311,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/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp index 35033ea545..e388a6f40f 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp @@ -21,7 +21,6 @@ #include <gtest/gtest.h> #include <gui/LayerMetadata.h> -#include "BufferQueueLayer.h" #include "BufferStateLayer.h" #include "EffectLayer.h" #include "Layer.h" @@ -60,7 +59,6 @@ protected: static constexpr int32_t PRIORITY_UNSET = -1; void setupScheduler(); - sp<BufferQueueLayer> createBufferQueueLayer(); sp<BufferStateLayer> createBufferStateLayer(); sp<EffectLayer> createEffectLayer(); @@ -90,12 +88,6 @@ RefreshRateSelectionTest::~RefreshRateSelectionTest() { ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } -sp<BufferQueueLayer> RefreshRateSelectionTest::createBufferQueueLayer() { - sp<Client> client; - LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT, - LAYER_FLAGS, LayerMetadata()); - return new BufferQueueLayer(args); -} sp<BufferStateLayer> RefreshRateSelectionTest::createBufferStateLayer() { sp<Client> client; @@ -149,46 +141,6 @@ namespace { /* ------------------------------------------------------------------------ * Test cases */ -TEST_F(RefreshRateSelectionTest, testPriorityOnBufferQueueLayers) { - mParent = createBufferQueueLayer(); - mChild = createBufferQueueLayer(); - setParent(mChild.get(), mParent.get()); - mGrandChild = createBufferQueueLayer(); - setParent(mGrandChild.get(), mChild.get()); - - ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority()); - ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority()); - ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority()); - - // Child has its own priority. - mGrandChild->setFrameRateSelectionPriority(1); - commitTransaction(mGrandChild.get()); - ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority()); - ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority()); - ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority()); - - // Child inherits from his parent. - mChild->setFrameRateSelectionPriority(1); - commitTransaction(mChild.get()); - mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET); - commitTransaction(mGrandChild.get()); - - ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority()); - ASSERT_EQ(1, mChild->getFrameRateSelectionPriority()); - ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority()); - - // Grandchild inherits from his grand parent. - mParent->setFrameRateSelectionPriority(1); - commitTransaction(mParent.get()); - mChild->setFrameRateSelectionPriority(PRIORITY_UNSET); - commitTransaction(mChild.get()); - mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET); - commitTransaction(mGrandChild.get()); - ASSERT_EQ(1, mParent->getFrameRateSelectionPriority()); - ASSERT_EQ(1, mChild->getFrameRateSelectionPriority()); - ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority()); -} - TEST_F(RefreshRateSelectionTest, testPriorityOnBufferStateLayers) { mParent = createBufferStateLayer(); mChild = createBufferStateLayer(); 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/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp index a4e9d20b75..84fa1e290a 100644 --- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp +++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp @@ -24,7 +24,6 @@ // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" -#include "BufferQueueLayer.h" #include "BufferStateLayer.h" #include "EffectLayer.h" #include "Layer.h" @@ -65,17 +64,6 @@ protected: static constexpr uint32_t LAYER_FLAGS = 0; }; -class BufferQueueLayerFactory : public LayerFactory { -public: - std::string name() override { return "BufferQueueLayer"; } - sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override { - sp<Client> client; - LayerCreationArgs args(flinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT, - LAYER_FLAGS, LayerMetadata()); - return new BufferQueueLayer(args); - } -}; - class BufferStateLayerFactory : public LayerFactory { public: std::string name() override { return "BufferStateLayer"; } @@ -417,8 +405,7 @@ TEST_P(SetFrameRateTest, SetAndGetParentNotInTree) { } INSTANTIATE_TEST_SUITE_P(PerLayerType, SetFrameRateTest, - testing::Values(std::make_shared<BufferQueueLayerFactory>(), - std::make_shared<BufferStateLayerFactory>(), + testing::Values(std::make_shared<BufferStateLayerFactory>(), std::make_shared<EffectLayerFactory>()), PrintToStringParamName); 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..38dceb9570 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp @@ -34,7 +34,6 @@ struct WideColorP3ColorimetricSupportedVariant { static constexpr bool WIDE_COLOR_SUPPORTED = true; static void injectConfigChange(DisplayTransactionTest* test) { - test->mFlinger.mutableUseColorManagement() = true; test->mFlinger.mutableHasWideColorDisplay() = true; test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged; } @@ -257,6 +256,7 @@ void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { } state.isSecure = static_cast<bool>(Case::Display::SECURE); + state.flags = Case::Display::DISPLAY_FLAGS; auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state, displaySurface, producer); @@ -279,6 +279,8 @@ void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport()); EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS, device->getSupportedPerFrameMetadata()); + EXPECT_EQ(Case::Display::DISPLAY_FLAGS & DisplayDevice::eReceivesInput, + device->receivesInput()); if constexpr (Case::Display::CONNECTION_TYPE::value) { EXPECT_EQ(1, device->getSupportedModes().size()); @@ -292,7 +294,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..7072439cea 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); } @@ -419,14 +419,13 @@ public: */ auto& mutableHasWideColorDisplay() { return SurfaceFlinger::hasWideColorDisplay; } - auto& mutableUseColorManagement() { return SurfaceFlinger::useColorManagement; } auto& mutableCurrentState() { return mFlinger->mCurrentState; } auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; } 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 +438,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 +598,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 +642,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 +761,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/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp index a749ece835..bd6a7805ec 100644 --- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp @@ -46,6 +46,7 @@ public: ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); setupScheduler(); mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); + mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); } ~TransactionFrameTracerTest() { @@ -92,21 +93,17 @@ public: } TestableSurfaceFlinger mFlinger; - renderengine::mock::RenderEngine mRenderEngine; + renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); FenceToFenceTimeMap fenceFactory; - client_cache_t mClientCache; void BLASTTransactionSendsFrameTracerEvents() { sp<BufferStateLayer> layer = createBufferStateLayer(); sp<Fence> fence(new Fence()); - const auto buffer = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); + const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); int32_t layerId = layer->getSequence(); - uint64_t bufferId = buffer->getBuffer()->getId(); + uint64_t bufferId = buffer->getId(); uint64_t frameNumber = 5; nsecs_t dequeueTime = 10; nsecs_t postTime = 20; @@ -117,9 +114,14 @@ public: EXPECT_CALL(*mFlinger.getFrameTracer(), traceTimestamp(layerId, bufferId, frameNumber, postTime, FrameTracer::FrameEvent::QUEUE, /*duration*/ 0)); - layer->setBuffer(buffer, fence, postTime, /*desiredPresentTime*/ 30, false, mClientCache, - frameNumber, dequeueTime, FrameTimelineInfo{}, - nullptr /* releaseBufferCallback */, nullptr /* releaseBufferEndpoint*/); + BufferData bufferData; + bufferData.buffer = buffer; + bufferData.acquireFence = fence; + bufferData.frameNumber = frameNumber; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, postTime, /*desiredPresentTime*/ 30, false, dequeueTime, + FrameTimelineInfo{}); commitTransaction(layer.get()); bool computeVisisbleRegions; diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp index 2a7921f661..2b5b7b5a5b 100644 --- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp @@ -46,6 +46,7 @@ public: ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); setupScheduler(); mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); + mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); } ~TransactionSurfaceFrameTest() { @@ -92,10 +93,9 @@ public: } TestableSurfaceFlinger mFlinger; - renderengine::mock::RenderEngine mRenderEngine; + renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); FenceToFenceTimeMap fenceFactory; - client_cache_t mClientCache; void PresentedSurfaceFrameForBufferlessTransaction() { sp<BufferStateLayer> layer = createBufferStateLayer(); @@ -114,13 +114,15 @@ public: sp<BufferStateLayer> layer = createBufferStateLayer(); sp<Fence> fence(new Fence()); auto acquireFence = fenceFactory.createFenceTimeForTest(fence); - const auto buffer = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); - layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + BufferData bufferData; + bufferData.buffer = buffer; + bufferData.acquireFence = fence; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); acquireFence->signalForTest(12); commitTransaction(layer.get()); @@ -143,27 +145,30 @@ public: sp<Fence> fence1(new Fence()); auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1); - const auto buffer1 = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); - layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + const auto buffer1 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + BufferData bufferData; + bufferData.buffer = buffer1; + bufferData.acquireFence = fence1; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; sp<Fence> fence2(new Fence()); auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2); - const auto buffer2 = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); + const auto buffer2 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); nsecs_t start = systemTime(); - layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + bufferData.buffer = buffer2; + bufferData.acquireFence = fence2; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); nsecs_t end = systemTime(); acquireFence2->signalForTest(12); @@ -198,13 +203,15 @@ public: sp<Fence> fence(new Fence()); auto acquireFence = fenceFactory.createFenceTimeForTest(fence); - const auto buffer = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); - layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + BufferData bufferData; + bufferData.buffer = buffer; + bufferData.acquireFence = fence; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); acquireFence->signalForTest(12); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); @@ -227,13 +234,15 @@ public: sp<BufferStateLayer> layer = createBufferStateLayer(); sp<Fence> fence(new Fence()); auto acquireFence = fenceFactory.createFenceTimeForTest(fence); - const auto buffer = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); - layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + BufferData bufferData; + bufferData.buffer = buffer; + bufferData.acquireFence = fence; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); @@ -260,13 +269,15 @@ public: sp<Fence> fence(new Fence()); auto acquireFence = fenceFactory.createFenceTimeForTest(fence); - const auto buffer = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); - layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 3, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + BufferData bufferData; + bufferData.buffer = buffer; + bufferData.acquireFence = fence; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 3, /*inputEventId*/ 0}); EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX; @@ -299,25 +310,28 @@ public: sp<Fence> fence1(new Fence()); auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1); - const auto buffer1 = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); - layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + const auto buffer1 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + BufferData bufferData; + bufferData.buffer = buffer1; + bufferData.acquireFence = fence1; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; sp<Fence> fence2(new Fence()); auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2); - const auto buffer2 = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); - layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + const auto buffer2 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + bufferData.buffer = buffer2; + bufferData.acquireFence = fence2; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); acquireFence2->signalForTest(12); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); @@ -342,27 +356,30 @@ public: sp<Fence> fence1(new Fence()); auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1); - const auto buffer1 = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); - layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + const auto buffer1 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + BufferData bufferData; + bufferData.buffer = buffer1; + bufferData.acquireFence = fence1; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX; sp<Fence> fence2(new Fence()); auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2); - const auto buffer2 = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); + const auto buffer2 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); auto dropStartTime1 = systemTime(); - layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0}, - nullptr /* releaseBufferCallback */, nullptr /* releaseBufferEndpoint */); + bufferData.buffer = buffer2; + bufferData.acquireFence = fence2; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0}); auto dropEndTime1 = systemTime(); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); @@ -370,14 +387,15 @@ public: sp<Fence> fence3(new Fence()); auto acquireFence3 = fenceFactory.createFenceTimeForTest(fence3); - const auto buffer3 = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, 0), - mRenderEngine, false); + const auto buffer3 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); auto dropStartTime2 = systemTime(); - layer->setBuffer(buffer3, fence3, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 2, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + bufferData.buffer = buffer3; + bufferData.acquireFence = fence3; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 2, /*inputEventId*/ 0}); auto dropEndTime2 = systemTime(); acquireFence3->signalForTest(12); @@ -413,16 +431,16 @@ public: uint32_t surfaceFramesPendingClassification = 0; std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> bufferlessSurfaceFrames; for (int i = 0; i < 10; i += 2) { - sp<Fence> fence1(new Fence()); - const auto buffer1 = std::make_shared< - renderengine::ExternalTexture>(new GraphicBuffer(1, 1, - HAL_PIXEL_FORMAT_RGBA_8888, 1, - 0), - mRenderEngine, false); - layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, - nullptr /* releaseBufferCallback */, - nullptr /* releaseBufferEndpoint */); + sp<Fence> fence(new Fence()); + const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0); + BufferData bufferData; + bufferData.buffer = buffer; + bufferData.acquireFence = fence; + bufferData.frameNumber = 1; + bufferData.flags |= BufferData::BufferDataChange::fenceChanged; + bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged; + layer->setBuffer(bufferData, 10, 20, false, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 2, /*inputEventId*/ 0}, 10); 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/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h index c3919d9310..fe1544ec18 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h @@ -30,8 +30,7 @@ public: MOCK_METHOD(hal::HWDisplayId, getId, (), (const, override)); MOCK_METHOD(bool, isConnected, (), (const, override)); MOCK_METHOD(void, setConnected, (bool), (override)); - MOCK_METHOD(const std::unordered_set<hal::DisplayCapability> &, getCapabilities, (), - (const, override)); + MOCK_METHOD(bool, hasCapability, (hal::DisplayCapability), (const, override)); MOCK_METHOD(bool, isVsyncPeriodSwitchSupported, (), (const, override)); MOCK_METHOD(void, onLayerDestroyed, (hal::HWLayerId), (override)); 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. |