diff options
87 files changed, 2574 insertions, 1771 deletions
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/include/android/input.h b/include/android/input.h index bb98beb41a..f03facb929 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/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/small_map.h b/include/ftl/small_map.h index 84c15ebdca..bcaba82934 100644 --- a/include/ftl/small_map.h +++ b/include/ftl/small_map.h @@ -19,6 +19,7 @@ #include <ftl/initializer_list.h> #include <ftl/small_vector.h> +#include <algorithm> #include <functional> #include <optional> #include <type_traits> @@ -28,7 +29,10 @@ namespace android::ftl { // Associative container with unique, unordered keys. Unlike std::unordered_map, key-value pairs are // stored in contiguous storage for cache efficiency. The map is allocated statically until its size -// exceeds N, at which point mappings are relocated to dynamic memory. +// exceeds N, at which point mappings are relocated to dynamic memory. The try_emplace operation has +// a non-standard analogue try_replace that destructively emplaces. The API also defines an in-place +// counterpart to insert_or_assign: emplace_or_replace. Lookup is done not via a subscript operator, +// but immutable getters that can optionally transform the value. // // SmallMap<K, V, 0> unconditionally allocates on the heap. // @@ -43,16 +47,19 @@ namespace android::ftl { // assert(!map.dynamic()); // // assert(map.contains(123)); -// assert(map.find(42, [](const std::string& s) { return s.size(); }) == 3u); +// assert(map.get(42, [](const std::string& s) { return s.size(); }) == 3u); // -// const auto opt = map.find(-1); +// const auto opt = map.get(-1); // assert(opt); // // std::string& ref = *opt; // assert(ref.empty()); // ref = "xyz"; // -// assert(map == SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc"))); +// map.emplace_or_replace(0, "vanilla", 2u, 3u); +// assert(map.dynamic()); +// +// assert(map == SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc"))); // template <typename K, typename V, std::size_t N> class SmallMap final { @@ -80,12 +87,7 @@ class SmallMap final { // The syntax for listing pairs is as follows: // // ftl::SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?'); - // // static_assert(std::is_same_v<decltype(map), ftl::SmallMap<int, std::string, 3>>); - // assert(map.size() == 3u); - // assert(map.contains(-1) && map.find(-1)->get().empty()); - // assert(map.contains(42) && map.find(42)->get() == "???"); - // assert(map.contains(123) && map.find(123)->get() == "abc"); // // The types of the key and value are deduced if the first pair contains exactly two arguments: // @@ -95,7 +97,7 @@ class SmallMap final { template <typename U, std::size_t... Sizes, typename... Types> SmallMap(InitializerList<U, std::index_sequence<Sizes...>, Types...>&& list) : map_(std::move(list)) { - // TODO: Enforce unique keys. + deduplicate(); } size_type max_size() const { return map_.max_size(); } @@ -115,27 +117,27 @@ class SmallMap final { // Returns whether a mapping exists for the given key. bool contains(const key_type& key) const { - return find(key, [](const mapped_type&) {}); + return get(key, [](const mapped_type&) {}); } // Returns a reference to the value for the given key, or std::nullopt if the key was not found. // // ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); // - // const auto opt = map.find('c'); + // const auto opt = map.get('c'); // assert(opt == 'C'); // // char d = 'd'; - // const auto ref = map.find('d').value_or(std::ref(d)); + // const auto ref = map.get('d').value_or(std::ref(d)); // ref.get() = 'D'; // assert(d == 'D'); // - auto find(const key_type& key) const -> std::optional<std::reference_wrapper<const mapped_type>> { - return find(key, [](const mapped_type& v) { return std::cref(v); }); + auto get(const key_type& key) const -> std::optional<std::reference_wrapper<const mapped_type>> { + return get(key, [](const mapped_type& v) { return std::cref(v); }); } - auto find(const key_type& key) -> std::optional<std::reference_wrapper<mapped_type>> { - return find(key, [](mapped_type& v) { return std::ref(v); }); + auto get(const key_type& key) -> std::optional<std::reference_wrapper<mapped_type>> { + return get(key, [](mapped_type& v) { return std::ref(v); }); } // Returns the result R of a unary operation F on (a constant or mutable reference to) the value @@ -144,11 +146,11 @@ class SmallMap final { // // ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); // - // assert(map.find('c', [](char c) { return std::toupper(c); }) == 'Z'); - // assert(map.find('c', [](char& c) { c = std::toupper(c); })); + // assert(map.get('c', [](char c) { return std::toupper(c); }) == 'Z'); + // assert(map.get('c', [](char& c) { c = std::toupper(c); })); // template <typename F, typename R = std::invoke_result_t<F, const mapped_type&>> - auto find(const key_type& key, F f) const + auto get(const key_type& key, F f) const -> std::conditional_t<std::is_void_v<R>, bool, std::optional<R>> { for (auto& [k, v] : *this) { if (k == key) { @@ -165,12 +167,96 @@ class SmallMap final { } template <typename F> - auto find(const key_type& key, F f) { - return std::as_const(*this).find( + auto get(const key_type& key, F f) { + return std::as_const(*this).get( key, [&f](const mapped_type& v) { return f(const_cast<mapped_type&>(v)); }); } + // Returns an iterator to an existing mapping for the given key, or the end() iterator otherwise. + const_iterator find(const key_type& key) const { return const_cast<SmallMap&>(*this).find(key); } + iterator find(const key_type& key) { return find(key, begin()); } + + // Inserts a mapping unless it exists. Returns an iterator to the inserted or existing mapping, + // and whether the mapping was inserted. + // + // On emplace, if the map reaches its static or dynamic capacity, then all iterators are + // invalidated. Otherwise, only the end() iterator is invalidated. + // + template <typename... Args> + std::pair<iterator, bool> try_emplace(const key_type& key, Args&&... args) { + if (const auto it = find(key); it != end()) { + return {it, false}; + } + + auto& ref = map_.emplace_back(std::piecewise_construct, std::forward_as_tuple(key), + std::forward_as_tuple(std::forward<Args>(args)...)); + return {&ref, true}; + } + + // Replaces a mapping if it exists, and returns an iterator to it. Returns the end() iterator + // otherwise. + // + // The value is replaced via move constructor, so type V does not need to define copy/move + // assignment, e.g. its data members may be const. + // + // The arguments may directly or indirectly refer to the mapping being replaced. + // + // Iterators to the replaced mapping point to its replacement, and others remain valid. + // + template <typename... Args> + iterator try_replace(const key_type& key, Args&&... args) { + const auto it = find(key); + if (it == end()) return it; + map_.replace(it, std::piecewise_construct, std::forward_as_tuple(key), + std::forward_as_tuple(std::forward<Args>(args)...)); + return it; + } + + // In-place counterpart of std::unordered_map's insert_or_assign. Returns true on emplace, or + // false on replace. + // + // The value is emplaced and replaced via move constructor, so type V does not need to define + // copy/move assignment, e.g. its data members may be const. + // + // On emplace, if the map reaches its static or dynamic capacity, then all iterators are + // invalidated. Otherwise, only the end() iterator is invalidated. On replace, iterators + // to the replaced mapping point to its replacement, and others remain valid. + // + template <typename... Args> + std::pair<iterator, bool> emplace_or_replace(const key_type& key, Args&&... args) { + const auto [it, ok] = try_emplace(key, std::forward<Args>(args)...); + if (ok) return {it, ok}; + map_.replace(it, std::piecewise_construct, std::forward_as_tuple(key), + std::forward_as_tuple(std::forward<Args>(args)...)); + return {it, ok}; + } + + // Removes a mapping if it exists, and returns whether it did. + // + // The last() and end() iterators, as well as those to the erased mapping, are invalidated. + // + bool erase(const key_type& key) { return erase(key, begin()); } + private: + iterator find(const key_type& key, iterator first) { + return std::find_if(first, end(), [&key](const auto& pair) { return pair.first == key; }); + } + + bool erase(const key_type& key, iterator first) { + const auto it = find(key, first); + if (it == end()) return false; + map_.unstable_erase(it); + return true; + } + + void deduplicate() { + for (auto it = begin(); it != end();) { + if (const auto key = it->first; ++it != end()) { + while (erase(key, it)); + } + } + } + Map map_; }; @@ -186,7 +272,7 @@ bool operator==(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) { for (const auto& [k, v] : lhs) { const auto& lv = v; - if (!rhs.find(k, [&lv](const auto& rv) { return lv == rv; }).value_or(false)) { + if (!rhs.get(k, [&lv](const auto& rv) { return lv == rv; }).value_or(false)) { return false; } } diff --git a/include/ftl/small_vector.h b/include/ftl/small_vector.h index cb0ae359eb..0341435813 100644 --- a/include/ftl/small_vector.h +++ b/include/ftl/small_vector.h @@ -348,7 +348,7 @@ class SmallVector<T, 0> final : ArrayTraits<T>, using Impl::pop_back; void unstable_erase(iterator it) { - if (it != last()) std::iter_swap(it, last()); + if (it != last()) replace(it, std::move(back())); pop_back(); } diff --git a/include/ftl/string.h b/include/ftl/string.h new file mode 100644 index 0000000000..2d96b06a2f --- /dev/null +++ b/include/ftl/string.h @@ -0,0 +1,101 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cassert> +#include <charconv> +#include <limits> +#include <string> +#include <string_view> +#include <type_traits> + +namespace android::ftl { + +enum class Radix { kBin = 2, kDec = 10, kHex = 16 }; + +template <typename T> +struct to_chars_length { + static_assert(std::is_integral_v<T>); + // Maximum binary digits, plus minus sign and radix prefix. + static constexpr std::size_t value = std::numeric_limits<std::make_unsigned_t<T>>::digits + 3; +}; + +template <typename T> +constexpr std::size_t to_chars_length_v = to_chars_length<T>::value; + +template <typename T = std::int64_t> +using to_chars_buffer_t = char[to_chars_length_v<T>]; + +// Lightweight (not allocating nor sprintf-based) alternative to std::to_string for integers, with +// optional radix. See also ftl::to_string below. +// +// ftl::to_chars_buffer_t<> buffer; +// +// assert(ftl::to_chars(buffer, 123u) == "123"); +// assert(ftl::to_chars(buffer, -42, ftl::Radix::kBin) == "-0b101010"); +// assert(ftl::to_chars(buffer, 0xcafe, ftl::Radix::kHex) == "0xcafe"); +// assert(ftl::to_chars(buffer, '*', ftl::Radix::kHex) == "0x2a"); +// +template <typename T, std::size_t N> +std::string_view to_chars(char (&buffer)[N], T v, Radix radix = Radix::kDec) { + static_assert(N >= to_chars_length_v<T>); + + auto begin = buffer + 2; + const auto [end, err] = std::to_chars(begin, buffer + N, v, static_cast<int>(radix)); + assert(err == std::errc()); + + if (radix == Radix::kDec) { + // TODO: Replace with {begin, end} in C++20. + return {begin, static_cast<std::size_t>(end - begin)}; + } + + const auto prefix = radix == Radix::kBin ? 'b' : 'x'; + if constexpr (std::is_unsigned_v<T>) { + buffer[0] = '0'; + buffer[1] = prefix; + } else { + if (*begin == '-') { + *buffer = '-'; + } else { + --begin; + } + + *begin-- = prefix; + *begin = '0'; + } + + // TODO: Replace with {buffer, end} in C++20. + return {buffer, static_cast<std::size_t>(end - buffer)}; +} + +// Lightweight (not sprintf-based) alternative to std::to_string for integers, with optional radix. +// +// assert(ftl::to_string(123u) == "123"); +// assert(ftl::to_string(-42, ftl::Radix::kBin) == "-0b101010"); +// assert(ftl::to_string(0xcafe, ftl::Radix::kHex) == "0xcafe"); +// assert(ftl::to_string('*', ftl::Radix::kHex) == "0x2a"); +// +template <typename T> +inline std::string to_string(T v, Radix radix = Radix::kDec) { + to_chars_buffer_t<T> buffer; + return std::string(to_chars(buffer, v, radix)); +} + +std::string to_string(bool) = delete; +std::string to_string(bool, Radix) = delete; + +} // namespace android::ftl diff --git a/include/input/Input.h b/include/input/Input.h index cd110e8c39..2e424097e1 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. @@ -888,6 +886,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 +969,7 @@ public: virtual FocusEvent* createFocusEvent() = 0; virtual CaptureEvent* createCaptureEvent() = 0; virtual DragEvent* createDragEvent() = 0; + virtual TouchModeEvent* createTouchModeEvent() = 0; }; /* @@ -968,6 +986,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 +994,7 @@ private: FocusEvent mFocusEvent; CaptureEvent mCaptureEvent; DragEvent mDragEvent; + TouchModeEvent mTouchModeEvent; }; /* @@ -990,6 +1010,7 @@ public: virtual FocusEvent* createFocusEvent() override; virtual CaptureEvent* createCaptureEvent() override; virtual DragEvent* createDragEvent() override; + virtual TouchModeEvent* createTouchModeEvent() override; void recycle(InputEvent* event); @@ -1001,6 +1022,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; }; } // namespace android diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index a790b5637f..9a150eb0e2 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -72,6 +72,7 @@ struct InputMessage { CAPTURE, DRAG, TIMELINE, + TOUCH_MODE, }; struct Header { @@ -206,6 +207,15 @@ struct InputMessage { inline size_t size() const { return sizeof(Timeline); } } timeline; + + struct TouchMode { + int32_t eventId; + // The following 2 fields take up 4 bytes total + bool isInTouchMode; + uint8_t empty[3]; + + inline size_t size() const { return sizeof(TouchMode); } + } touchMode; } __attribute__((aligned(8))) body; bool isValid(size_t actualSize) const; @@ -388,6 +398,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 +677,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/libs/ftl/Android.bp b/libs/ftl/Android.bp index 2524c5f6d2..3026921044 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -14,12 +14,14 @@ cc_test { address: true, }, srcs: [ + "cast_test.cpp", "Flags_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/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/small_map_test.cpp b/libs/ftl/small_map_test.cpp index 323b9f91e7..2e81022f38 100644 --- a/libs/ftl/small_map_test.cpp +++ b/libs/ftl/small_map_test.cpp @@ -18,6 +18,9 @@ #include <gtest/gtest.h> #include <cctype> +#include <string> + +using namespace std::string_literals; namespace android::test { @@ -35,16 +38,19 @@ TEST(SmallMap, Example) { EXPECT_TRUE(map.contains(123)); - EXPECT_EQ(map.find(42, [](const std::string& s) { return s.size(); }), 3u); + EXPECT_EQ(map.get(42, [](const std::string& s) { return s.size(); }), 3u); - const auto opt = map.find(-1); + const auto opt = map.get(-1); ASSERT_TRUE(opt); std::string& ref = *opt; EXPECT_TRUE(ref.empty()); ref = "xyz"; - EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc"))); + map.emplace_or_replace(0, "vanilla", 2u, 3u); + EXPECT_TRUE(map.dynamic()); + + EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc"))); } TEST(SmallMap, Construct) { @@ -90,42 +96,253 @@ TEST(SmallMap, Construct) { } } +TEST(SmallMap, UniqueKeys) { + { + // Duplicate mappings are discarded. + const SmallMap map = ftl::init::map<int, float>(1)(2)(3)(2)(3)(1)(3)(2)(1); + + EXPECT_EQ(map.size(), 3u); + EXPECT_EQ(map.max_size(), 9u); + + using Map = decltype(map); + EXPECT_EQ(map, Map(ftl::init::map(1, 0.f)(2, 0.f)(3, 0.f))); + } + { + // Duplicate mappings may be reordered. + const SmallMap map = ftl::init::map('a', 'A')( + 'b', 'B')('b')('b')('c', 'C')('a')('d')('c')('e', 'E')('d', 'D')('a')('f', 'F'); + + EXPECT_EQ(map.size(), 6u); + EXPECT_EQ(map.max_size(), 12u); + + using Map = decltype(map); + EXPECT_EQ(map, Map(ftl::init::map('a', 'A')('b', 'B')('c', 'C')('d', 'D')('e', 'E')('f', 'F'))); + } +} + TEST(SmallMap, Find) { { // Constant reference. - const ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); + const SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); - const auto opt = map.find('b'); + const auto opt = map.get('b'); EXPECT_EQ(opt, 'B'); const char d = 'D'; - const auto ref = map.find('d').value_or(std::cref(d)); + const auto ref = map.get('d').value_or(std::cref(d)); EXPECT_EQ(ref.get(), 'D'); } { // Mutable reference. - ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); + SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); - const auto opt = map.find('c'); + const auto opt = map.get('c'); EXPECT_EQ(opt, 'C'); char d = 'd'; - const auto ref = map.find('d').value_or(std::ref(d)); + const auto ref = map.get('d').value_or(std::ref(d)); ref.get() = 'D'; EXPECT_EQ(d, 'D'); } { // Constant unary operation. - const ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); - EXPECT_EQ(map.find('c', [](char c) { return std::toupper(c); }), 'Z'); + const SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); + EXPECT_EQ(map.get('c', [](char c) { return std::toupper(c); }), 'Z'); } { // Mutable unary operation. - ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); - EXPECT_TRUE(map.find('c', [](char& c) { c = std::toupper(c); })); + SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); + EXPECT_TRUE(map.get('c', [](char& c) { c = std::toupper(c); })); EXPECT_EQ(map, SmallMap(ftl::init::map('c', 'Z')('b', 'y')('a', 'x'))); } } +TEST(SmallMap, TryEmplace) { + SmallMap<int, std::string, 3> map; + using Pair = decltype(map)::value_type; + + { + const auto [it, ok] = map.try_emplace(123, "abc"); + ASSERT_TRUE(ok); + EXPECT_EQ(*it, Pair(123, "abc"s)); + } + { + const auto [it, ok] = map.try_emplace(42, 3u, '?'); + ASSERT_TRUE(ok); + EXPECT_EQ(*it, Pair(42, "???"s)); + } + { + const auto [it, ok] = map.try_emplace(-1); + ASSERT_TRUE(ok); + EXPECT_EQ(*it, Pair(-1, std::string())); + EXPECT_FALSE(map.dynamic()); + } + { + // Insertion fails if mapping exists. + const auto [it, ok] = map.try_emplace(42, "!!!"); + EXPECT_FALSE(ok); + EXPECT_EQ(*it, Pair(42, "???")); + EXPECT_FALSE(map.dynamic()); + } + { + // Insertion at capacity promotes the map. + const auto [it, ok] = map.try_emplace(999, "xyz"); + ASSERT_TRUE(ok); + EXPECT_EQ(*it, Pair(999, "xyz")); + EXPECT_TRUE(map.dynamic()); + } + + EXPECT_EQ(map, SmallMap(ftl::init::map(-1, ""s)(42, "???"s)(123, "abc"s)(999, "xyz"s))); +} + +namespace { + +// The mapped type does not require a copy/move assignment operator. +struct String { + template <typename... Args> + String(Args... args) : str(args...) {} + const std::string str; + + bool operator==(const String& other) const { return other.str == str; } +}; + +} // namespace + +TEST(SmallMap, TryReplace) { + SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B"); + using Pair = decltype(map)::value_type; + + { + // Replacing fails unless mapping exists. + const auto it = map.try_replace(3, "c"); + EXPECT_EQ(it, map.end()); + } + { + // Replacement arguments can refer to the replaced mapping. + const auto ref = map.get(2, [](const auto& s) { return s.str[0]; }); + ASSERT_TRUE(ref); + + // Construct std::string from one character. + const auto it = map.try_replace(2, 1u, static_cast<char>(std::tolower(*ref))); + ASSERT_NE(it, map.end()); + EXPECT_EQ(*it, Pair(2, "b")); + } + + EXPECT_FALSE(map.dynamic()); + EXPECT_TRUE(map.try_emplace(3, "abc").second); + EXPECT_TRUE(map.try_emplace(4, "d").second); + EXPECT_TRUE(map.dynamic()); + + { + // Replacing fails unless mapping exists. + const auto it = map.try_replace(5, "e"); + EXPECT_EQ(it, map.end()); + } + { + // Replacement arguments can refer to the replaced mapping. + const auto ref = map.get(3); + ASSERT_TRUE(ref); + + // Construct std::string from substring. + const auto it = map.try_replace(3, ref->get().str, 2u, 1u); + ASSERT_NE(it, map.end()); + EXPECT_EQ(*it, Pair(3, "c")); + } + + EXPECT_EQ(map, SmallMap(ftl::init::map(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s))); +} + +TEST(SmallMap, EmplaceOrReplace) { + SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B"); + using Pair = decltype(map)::value_type; + + { + // New mapping is emplaced. + const auto [it, emplace] = map.emplace_or_replace(3, "c"); + EXPECT_TRUE(emplace); + EXPECT_EQ(*it, Pair(3, "c")); + } + { + // Replacement arguments can refer to the replaced mapping. + const auto ref = map.get(2, [](const auto& s) { return s.str[0]; }); + ASSERT_TRUE(ref); + + // Construct std::string from one character. + const auto [it, emplace] = map.emplace_or_replace(2, 1u, static_cast<char>(std::tolower(*ref))); + EXPECT_FALSE(emplace); + EXPECT_EQ(*it, Pair(2, "b")); + } + + EXPECT_FALSE(map.dynamic()); + EXPECT_FALSE(map.emplace_or_replace(3, "abc").second); // Replace. + EXPECT_TRUE(map.emplace_or_replace(4, "d").second); // Emplace. + EXPECT_TRUE(map.dynamic()); + + { + // New mapping is emplaced. + const auto [it, emplace] = map.emplace_or_replace(5, "e"); + EXPECT_TRUE(emplace); + EXPECT_EQ(*it, Pair(5, "e")); + } + { + // Replacement arguments can refer to the replaced mapping. + const auto ref = map.get(3); + ASSERT_TRUE(ref); + + // Construct std::string from substring. + const auto [it, emplace] = map.emplace_or_replace(3, ref->get().str, 2u, 1u); + EXPECT_FALSE(emplace); + EXPECT_EQ(*it, Pair(3, "c")); + } + + EXPECT_EQ(map, SmallMap(ftl::init::map(5, "e"s)(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s))); +} + +TEST(SmallMap, Erase) { + { + SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3')(4, '4'); + EXPECT_FALSE(map.dynamic()); + + EXPECT_FALSE(map.erase(0)); // Key not found. + + EXPECT_TRUE(map.erase(2)); + EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4'))); + + EXPECT_TRUE(map.erase(1)); + EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4'))); + + EXPECT_TRUE(map.erase(4)); + EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3'))); + + EXPECT_TRUE(map.erase(3)); + EXPECT_FALSE(map.erase(3)); // Key not found. + + EXPECT_TRUE(map.empty()); + EXPECT_FALSE(map.dynamic()); + } + { + SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3'); + map.try_emplace(4, '4'); + EXPECT_TRUE(map.dynamic()); + + EXPECT_FALSE(map.erase(0)); // Key not found. + + EXPECT_TRUE(map.erase(2)); + EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4'))); + + EXPECT_TRUE(map.erase(1)); + EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4'))); + + EXPECT_TRUE(map.erase(4)); + EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3'))); + + EXPECT_TRUE(map.erase(3)); + EXPECT_FALSE(map.erase(3)); // Key not found. + + EXPECT_TRUE(map.empty()); + EXPECT_TRUE(map.dynamic()); + } +} + } // namespace android::test diff --git a/libs/ftl/string_test.cpp b/libs/ftl/string_test.cpp new file mode 100644 index 0000000000..f3d85c8319 --- /dev/null +++ b/libs/ftl/string_test.cpp @@ -0,0 +1,187 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ftl/string.h> +#include <gtest/gtest.h> + +#include <algorithm> +#include <cstdint> +#include <iterator> +#include <limits> +#include <sstream> +#include <type_traits> + +namespace android::test { + +// Keep in sync with example usage in header file. +TEST(String, ToChars) { + ftl::to_chars_buffer_t<> buffer; + + EXPECT_EQ(ftl::to_chars(buffer, 123u), "123"); + EXPECT_EQ(ftl::to_chars(buffer, -42, ftl::Radix::kBin), "-0b101010"); + EXPECT_EQ(ftl::to_chars(buffer, 0xcafe, ftl::Radix::kHex), "0xcafe"); + EXPECT_EQ(ftl::to_chars(buffer, '*', ftl::Radix::kHex), "0x2a"); +} + +namespace { + +template <typename F, typename T> +void ToCharsTest() { + constexpr auto kRadix = F::kRadix; + + using Limits = std::numeric_limits<T>; + constexpr auto kMin = Limits::min(); + constexpr auto kMax = Limits::max(); + constexpr auto kNeg = static_cast<T>(-42); + constexpr auto kPos = static_cast<T>(123); + + ftl::to_chars_buffer_t<T> buffer; + + EXPECT_EQ(ftl::to_chars(buffer, kMin, kRadix), F{}(kMin)); + EXPECT_EQ(ftl::to_chars(buffer, kMax, kRadix), F{}(kMax)); + EXPECT_EQ(ftl::to_chars(buffer, kNeg, kRadix), F{}(kNeg)); + EXPECT_EQ(ftl::to_chars(buffer, kPos, kRadix), F{}(kPos)); +} + +template <typename...> +struct Types {}; + +template <typename F, typename Types> +struct ToCharsTests; + +template <typename F, typename T, typename... Ts> +struct ToCharsTests<F, Types<T, Ts...>> { + static void test() { + ToCharsTest<F, T>(); + ToCharsTests<F, Types<Ts...>>::test(); + } +}; + +template <typename F> +struct ToCharsTests<F, Types<>> { + static void test() {} +}; + +template <typename T, typename U = std::make_unsigned_t<T>> +U to_unsigned(std::ostream& stream, T v) { + if (std::is_same_v<T, U>) return v; + + if (v < 0) { + stream << '-'; + return std::numeric_limits<U>::max() - static_cast<U>(v) + 1; + } else { + return static_cast<U>(v); + } +} + +struct Bin { + static constexpr auto kRadix = ftl::Radix::kBin; + + template <typename T> + std::string operator()(T v) const { + std::ostringstream stream; + auto u = to_unsigned(stream, v); + stream << "0b"; + + if (u == 0) { + stream << 0; + } else { + std::ostringstream digits; + do { + digits << (u & 1); + } while (u >>= 1); + + const auto str = digits.str(); + std::copy(str.rbegin(), str.rend(), std::ostream_iterator<char>(stream)); + } + + return stream.str(); + } +}; + +struct Dec { + static constexpr auto kRadix = ftl::Radix::kDec; + + template <typename T> + std::string operator()(T v) const { + return std::to_string(v); + } +}; + +struct Hex { + static constexpr auto kRadix = ftl::Radix::kHex; + + template <typename T> + std::string operator()(T v) const { + std::ostringstream stream; + const auto u = to_unsigned(stream, v); + stream << "0x" << std::hex << std::nouppercase; + stream << (sizeof(T) == 1 ? static_cast<unsigned>(u) : u); + return stream.str(); + } +}; + +using IntegerTypes = + Types<char, unsigned char, signed char, std::uint8_t, std::uint16_t, std::uint32_t, + std::uint64_t, std::int8_t, std::int16_t, std::int32_t, std::int64_t>; + +} // namespace + +TEST(String, ToCharsBin) { + ToCharsTests<Bin, IntegerTypes>::test(); + + { + const std::uint8_t x = 0b1111'1111; + ftl::to_chars_buffer_t<decltype(x)> buffer; + EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kBin), "0b11111111"); + } + { + const std::int16_t x = -0b1000'0000'0000'0000; + ftl::to_chars_buffer_t<decltype(x)> buffer; + EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kBin), "-0b1000000000000000"); + } +} + +TEST(String, ToCharsDec) { + ToCharsTests<Dec, IntegerTypes>::test(); + + { + const std::uint32_t x = UINT32_MAX; + ftl::to_chars_buffer_t<decltype(x)> buffer; + EXPECT_EQ(ftl::to_chars(buffer, x), "4294967295"); + } + { + const std::int32_t x = INT32_MIN; + ftl::to_chars_buffer_t<decltype(x)> buffer; + EXPECT_EQ(ftl::to_chars(buffer, x), "-2147483648"); + } +} + +TEST(String, ToCharsHex) { + ToCharsTests<Hex, IntegerTypes>::test(); + + { + const std::uint16_t x = 0xfade; + ftl::to_chars_buffer_t<decltype(x)> buffer; + EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kHex), "0xfade"); + } + { + ftl::to_chars_buffer_t<> buffer; + EXPECT_EQ(ftl::to_chars(buffer, INT64_MIN, ftl::Radix::kHex), "-0x8000000000000000"); + } +} + +} // namespace android::test diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index d54de4999c..7f0cac5d4f 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -343,80 +343,6 @@ void* GraphicsEnv::loadLibrary(std::string name) { return nullptr; } -bool GraphicsEnv::checkAngleRules(void* so) { - auto manufacturer = base::GetProperty("ro.product.manufacturer", "UNSET"); - auto model = base::GetProperty("ro.product.model", "UNSET"); - - auto ANGLEGetFeatureSupportUtilAPIVersion = - (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so, - "ANGLEGetFeatureSupportUtilAPIVersion"); - - if (!ANGLEGetFeatureSupportUtilAPIVersion) { - ALOGW("Cannot find ANGLEGetFeatureSupportUtilAPIVersion function"); - return false; - } - - // Negotiate the interface version by requesting most recent known to the platform - unsigned int versionToUse = CURRENT_ANGLE_API_VERSION; - if (!(ANGLEGetFeatureSupportUtilAPIVersion)(&versionToUse)) { - ALOGW("Cannot use ANGLE feature-support library, it is older than supported by EGL, " - "requested version %u", - versionToUse); - return false; - } - - // Add and remove versions below as needed - bool useAngle = false; - switch (versionToUse) { - case 2: { - ALOGV("Using version %d of ANGLE feature-support library", versionToUse); - void* rulesHandle = nullptr; - int rulesVersion = 0; - void* systemInfoHandle = nullptr; - - // Get the symbols for the feature-support-utility library: -#define GET_SYMBOL(symbol) \ - fp##symbol symbol = (fp##symbol)dlsym(so, #symbol); \ - if (!symbol) { \ - ALOGW("Cannot find " #symbol " in ANGLE feature-support library"); \ - break; \ - } - GET_SYMBOL(ANGLEAndroidParseRulesString); - GET_SYMBOL(ANGLEGetSystemInfo); - GET_SYMBOL(ANGLEAddDeviceInfoToSystemInfo); - GET_SYMBOL(ANGLEShouldBeUsedForApplication); - GET_SYMBOL(ANGLEFreeRulesHandle); - GET_SYMBOL(ANGLEFreeSystemInfoHandle); - - // Parse the rules, obtain the SystemInfo, and evaluate the - // application against the rules: - if (!(ANGLEAndroidParseRulesString)(mRulesBuffer.data(), &rulesHandle, &rulesVersion)) { - ALOGW("ANGLE feature-support library cannot parse rules file"); - break; - } - if (!(ANGLEGetSystemInfo)(&systemInfoHandle)) { - ALOGW("ANGLE feature-support library cannot obtain SystemInfo"); - break; - } - if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer.c_str(), model.c_str(), - systemInfoHandle)) { - ALOGW("ANGLE feature-support library cannot add device info to SystemInfo"); - break; - } - useAngle = (ANGLEShouldBeUsedForApplication)(rulesHandle, rulesVersion, - systemInfoHandle, mAngleAppName.c_str()); - (ANGLEFreeRulesHandle)(rulesHandle); - (ANGLEFreeSystemInfoHandle)(systemInfoHandle); - } break; - - default: - ALOGW("Version %u of ANGLE feature-support library is NOT supported.", versionToUse); - } - - ALOGV("Close temporarily-loaded ANGLE opt-in/out logic"); - return useAngle; -} - bool GraphicsEnv::shouldUseAngle(std::string appName) { if (appName != mAngleAppName) { // Make sure we are checking the app we were init'ed for @@ -444,31 +370,20 @@ void GraphicsEnv::updateUseAngle() { const char* ANGLE_PREFER_ANGLE = "angle"; const char* ANGLE_PREFER_NATIVE = "native"; + mUseAngle = NO; if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) { ALOGV("User set \"Developer Options\" to force the use of ANGLE"); mUseAngle = YES; } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) { ALOGV("User set \"Developer Options\" to force the use of Native"); - mUseAngle = NO; } else { - // The "Developer Options" value wasn't set to force the use of ANGLE. Need to temporarily - // load ANGLE and call the updatable opt-in/out logic: - void* featureSo = loadLibrary("feature_support"); - if (featureSo) { - ALOGV("loaded ANGLE's opt-in/out logic from namespace"); - mUseAngle = checkAngleRules(featureSo) ? YES : NO; - dlclose(featureSo); - featureSo = nullptr; - } else { - ALOGV("Could not load the ANGLE opt-in/out logic, cannot use ANGLE."); - } + ALOGV("User set invalid \"Developer Options\": '%s'", mAngleDeveloperOptIn.c_str()); } } void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName, const std::string developerOptIn, - const std::vector<std::string> eglFeatures, const int rulesFd, - const long rulesOffset, const long rulesLength) { + const std::vector<std::string> eglFeatures) { if (mUseAngle != UNKNOWN) { // We've already figured out an answer for this app, so just return. ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(), @@ -485,22 +400,6 @@ void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName ALOGV("setting ANGLE application opt-in to '%s'", developerOptIn.c_str()); mAngleDeveloperOptIn = developerOptIn; - lseek(rulesFd, rulesOffset, SEEK_SET); - mRulesBuffer = std::vector<char>(rulesLength + 1); - ssize_t numBytesRead = read(rulesFd, mRulesBuffer.data(), rulesLength); - if (numBytesRead < 0) { - ALOGE("Cannot read rules file: numBytesRead = %zd", numBytesRead); - numBytesRead = 0; - } else if (numBytesRead == 0) { - ALOGW("Empty rules file"); - } - if (numBytesRead != rulesLength) { - ALOGW("Did not read all of the necessary bytes from the rules file." - "expected: %ld, got: %zd", - rulesLength, numBytesRead); - } - mRulesBuffer[numBytesRead] = '\0'; - // Update the current status of whether we should use ANGLE or not updateUseAngle(); } diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index 900fc49b59..56d1139f57 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -97,8 +97,7 @@ public: // in the search path must have a '!' after the zip filename, e.g. // /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn, - const std::vector<std::string> eglFeatures, const int rulesFd, - const long rulesOffset, const long rulesLength); + const std::vector<std::string> eglFeatures); // Get the ANGLE driver namespace. android_namespace_t* getAngleNamespace(); // Get the app name for ANGLE debug message. @@ -129,8 +128,6 @@ private: // Load requested ANGLE library. void* loadLibrary(std::string name); - // Check ANGLE support with the rules. - bool checkAngleRules(void* so); // Update whether ANGLE should be used. void updateUseAngle(); // Link updatable driver namespace with llndk and vndk-sp libs. @@ -159,8 +156,6 @@ private: std::string mAngleDeveloperOptIn; // ANGLE EGL features; std::vector<std::string> mAngleEglFeatures; - // ANGLE rules. - std::vector<char> mRulesBuffer; // Use ANGLE flag. UseAngle mUseAngle = UNKNOWN; // Vulkan debug layers libs. diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 2a980bd118..3bf63062f6 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -124,11 +124,11 @@ public: return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply); } - status_t captureDisplay(uint64_t displayOrLayerStack, + status_t captureDisplay(DisplayId displayId, const sp<IScreenCaptureListener>& captureListener) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - SAFE_PARCEL(data.writeUint64, displayOrLayerStack); + SAFE_PARCEL(data.writeUint64, displayId.value); SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener)); return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, data, &reply); @@ -282,9 +282,14 @@ public: NO_ERROR) { std::vector<uint64_t> rawIds; if (reply.readUint64Vector(&rawIds) == NO_ERROR) { - std::vector<PhysicalDisplayId> displayIds(rawIds.size()); - std::transform(rawIds.begin(), rawIds.end(), displayIds.begin(), - [](uint64_t rawId) { return PhysicalDisplayId(rawId); }); + std::vector<PhysicalDisplayId> displayIds; + displayIds.reserve(rawIds.size()); + + for (const uint64_t rawId : rawIds) { + if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(rawId)) { + displayIds.push_back(*id); + } + } return displayIds; } } @@ -1344,12 +1349,15 @@ status_t BnSurfaceComposer::onTransact( } case CAPTURE_DISPLAY_BY_ID: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - uint64_t displayOrLayerStack = 0; + uint64_t value; + SAFE_PARCEL(data.readUint64, &value); + const auto id = DisplayId::fromValue(value); + if (!id) return BAD_VALUE; + sp<IScreenCaptureListener> captureListener; - SAFE_PARCEL(data.readUint64, &displayOrLayerStack); SAFE_PARCEL(data.readStrongBinder, &captureListener); - return captureDisplay(displayOrLayerStack, captureListener); + return captureDisplay(*id, captureListener); } case CAPTURE_LAYERS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); @@ -1416,9 +1424,9 @@ status_t BnSurfaceComposer::onTransact( } case GET_PHYSICAL_DISPLAY_TOKEN: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - PhysicalDisplayId displayId(data.readUint64()); - sp<IBinder> display = getPhysicalDisplayToken(displayId); - reply->writeStrongBinder(display); + const auto id = DisplayId::fromValue<PhysicalDisplayId>(data.readUint64()); + if (!id) return BAD_VALUE; + reply->writeStrongBinder(getPhysicalDisplayToken(*id)); return NO_ERROR; } case GET_DISPLAY_STATE: { diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 837e5b35f7..23895c0c9f 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -560,10 +560,13 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eDestinationFrameChanged; destinationFrame = other.destinationFrame; } + if (other.what & eProducerDisconnect) { + what |= eProducerDisconnect; + } if ((other.what & what) != other.what) { ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " - "other.what=0x%" PRIu64 " what=0x%" PRIu64, - other.what, what); + "other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64, + other.what, what, (other.what & what) ^ other.what); } } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index ce9a1f7918..f620f5ae07 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -2175,12 +2175,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/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index ad7bcb7d12..b7cd082da0 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -116,6 +116,11 @@ public: using EventRegistrationFlags = Flags<EventRegistration>; + template <typename T> + struct SpHash { + size_t operator()(const sp<T>& k) const { return std::hash<T*>()(k.get()); } + }; + /* * Create a connection with SurfaceFlinger. */ @@ -239,24 +244,17 @@ public: * The subregion can be optionally rotated. It will also be scaled to * match the size of the output buffer. */ - virtual status_t captureDisplay(const DisplayCaptureArgs& args, - const sp<IScreenCaptureListener>& captureListener) = 0; + virtual status_t captureDisplay(const DisplayCaptureArgs&, + const sp<IScreenCaptureListener>&) = 0; - virtual status_t captureDisplay(uint64_t displayOrLayerStack, - const sp<IScreenCaptureListener>& captureListener) = 0; - - template <class AA> - struct SpHash { - size_t operator()(const sp<AA>& k) const { return std::hash<AA*>()(k.get()); } - }; + virtual status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) = 0; /** * Capture a subtree of the layer hierarchy, potentially ignoring the root node. * This requires READ_FRAME_BUFFER permission. This function will fail if there * is a secure window on screen */ - virtual status_t captureLayers(const LayerCaptureArgs& args, - const sp<IScreenCaptureListener>& captureListener) = 0; + virtual status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) = 0; /* Clears the frame statistics for animations. * diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 8993891398..869cef6cdc 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -638,12 +638,9 @@ private: class ScreenshotClient { public: - static status_t captureDisplay(const DisplayCaptureArgs& captureArgs, - const sp<IScreenCaptureListener>& captureListener); - static status_t captureDisplay(uint64_t displayOrLayerStack, - const sp<IScreenCaptureListener>& captureListener); - static status_t captureLayers(const LayerCaptureArgs& captureArgs, - const sp<IScreenCaptureListener>& captureListener); + static status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&); + static status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&); + static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&); }; // --------------------------------------------------------------------------- diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index a02970c9bc..d1ad478dd1 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -752,21 +752,19 @@ public: } status_t setActiveColorMode(const sp<IBinder>& /*display*/, ColorMode /*colorMode*/) override { return NO_ERROR; } - status_t captureDisplay(const DisplayCaptureArgs& /* captureArgs */, - const sp<IScreenCaptureListener>& /* captureListener */) override { - return NO_ERROR; - } void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {} void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {} - status_t captureDisplay(uint64_t /*displayOrLayerStack*/, - const sp<IScreenCaptureListener>& /* captureListener */) override { + + status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override { return NO_ERROR; } - virtual status_t captureLayers( - const LayerCaptureArgs& /* captureArgs */, - const sp<IScreenCaptureListener>& /* captureListener */) override { + status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) override { return NO_ERROR; } + status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override { + return NO_ERROR; + } + status_t clearAnimationFrameStats() override { return NO_ERROR; } status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override { return NO_ERROR; diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 35209f7a07..1e8ff945ef 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -170,6 +170,9 @@ const char* inputEventTypeToString(int32_t type) { case AINPUT_EVENT_TYPE_DRAG: { return "DRAG"; } + case AINPUT_EVENT_TYPE_TOUCH_MODE: { + return "TOUCH_MODE"; + } } return "UNKNOWN"; } @@ -330,10 +333,6 @@ void PointerCoords::scale(float globalScaleFactor, float windowXScale, float win scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_Y, windowYScale); } -void PointerCoords::scale(float globalScaleFactor) { - scale(globalScaleFactor, globalScaleFactor, globalScaleFactor); -} - void PointerCoords::applyOffset(float xOffset, float yOffset) { setAxisValue(AMOTION_EVENT_AXIS_X, getX() + xOffset); setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset); @@ -899,6 +898,19 @@ void DragEvent::initialize(const DragEvent& from) { mY = from.mY; } +// --- TouchModeEvent --- + +void TouchModeEvent::initialize(int32_t id, bool isInTouchMode) { + InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, + ADISPLAY_ID_NONE, INVALID_HMAC); + mIsInTouchMode = isInTouchMode; +} + +void TouchModeEvent::initialize(const TouchModeEvent& from) { + InputEvent::initialize(from); + mIsInTouchMode = from.mIsInTouchMode; +} + // --- PooledInputEventFactory --- PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) : @@ -953,6 +965,15 @@ DragEvent* PooledInputEventFactory::createDragEvent() { return event; } +TouchModeEvent* PooledInputEventFactory::createTouchModeEvent() { + if (mTouchModeEventPool.empty()) { + return new TouchModeEvent(); + } + TouchModeEvent* event = mTouchModeEventPool.front().release(); + mTouchModeEventPool.pop(); + return event; +} + void PooledInputEventFactory::recycle(InputEvent* event) { switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index ea8b9a7ec8..1e93dfb488 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -116,6 +116,7 @@ bool InputMessage::isValid(size_t actualSize) const { case Type::FOCUS: case Type::CAPTURE: case Type::DRAG: + case Type::TOUCH_MODE: return true; case Type::TIMELINE: { const nsecs_t gpuCompletedTime = @@ -151,6 +152,8 @@ size_t InputMessage::size() const { return sizeof(Header) + body.drag.size(); case Type::TIMELINE: return sizeof(Header) + body.timeline.size(); + case Type::TOUCH_MODE: + return sizeof(Header) + body.touchMode.size(); } return sizeof(Header); } @@ -293,6 +296,10 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.timeline.graphicsTimeline = body.timeline.graphicsTimeline; break; } + case InputMessage::Type::TOUCH_MODE: { + msg->body.touchMode.eventId = body.touchMode.eventId; + msg->body.touchMode.isInTouchMode = body.touchMode.isInTouchMode; + } } } @@ -665,6 +672,22 @@ status_t InputPublisher::publishDragEvent(uint32_t seq, int32_t eventId, float x return mChannel->sendMessage(&msg); } +status_t InputPublisher::publishTouchModeEvent(uint32_t seq, int32_t eventId, bool isInTouchMode) { + if (ATRACE_ENABLED()) { + std::string message = + StringPrintf("publishTouchModeEvent(inputChannel=%s, isInTouchMode=%s)", + mChannel->getName().c_str(), toString(isInTouchMode)); + ATRACE_NAME(message.c_str()); + } + + InputMessage msg; + msg.header.type = InputMessage::Type::TOUCH_MODE; + msg.header.seq = seq; + msg.body.touchMode.eventId = eventId; + msg.body.touchMode.isInTouchMode = isInTouchMode; + return mChannel->sendMessage(&msg); +} + android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() { if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__); @@ -866,6 +889,16 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum *outEvent = dragEvent; break; } + + case InputMessage::Type::TOUCH_MODE: { + TouchModeEvent* touchModeEvent = factory->createTouchModeEvent(); + if (!touchModeEvent) return NO_MEMORY; + + initializeTouchModeEvent(touchModeEvent, &mMsg); + *outSeq = mMsg.header.seq; + *outEvent = touchModeEvent; + break; + } } } return OK; @@ -1370,6 +1403,10 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); } +void InputConsumer::initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg) { + event->initialize(msg->body.touchMode.eventId, msg->body.touchMode.isInTouchMode); +} + void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { uint32_t pointerCount = msg->body.motion.pointerCount; PointerCoords pointerCoords[pointerCount]; @@ -1476,6 +1513,11 @@ std::string InputConsumer::dump() const { presentTime); break; } + case InputMessage::Type::TOUCH_MODE: { + out += android::base::StringPrintf("isInTouchMode=%s", + toString(msg.body.touchMode.isInTouchMode)); + break; + } } out += "\n"; } diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 5d1f2c3bfc..8db5bf1289 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -56,6 +56,7 @@ protected: void PublishAndConsumeFocusEvent(); void PublishAndConsumeCaptureEvent(); void PublishAndConsumeDragEvent(); + void PublishAndConsumeTouchModeEvent(); }; TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { @@ -413,6 +414,46 @@ void InputPublisherAndConsumerTest::PublishAndConsumeDragEvent() { << "finished signal's consume time should be greater than publish time"; } +void InputPublisherAndConsumerTest::PublishAndConsumeTouchModeEvent() { + status_t status; + + constexpr uint32_t seq = 15; + int32_t eventId = InputEvent::nextId(); + constexpr bool touchModeEnabled = true; + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + + status = mPublisher->publishTouchModeEvent(seq, eventId, touchModeEnabled); + ASSERT_EQ(OK, status) << "publisher publishTouchModeEvent should return OK"; + + uint32_t consumeSeq; + InputEvent* event; + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); + ASSERT_EQ(OK, status) << "consumer consume should return OK"; + + ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; + ASSERT_EQ(AINPUT_EVENT_TYPE_TOUCH_MODE, event->getType()) + << "consumer should have returned a touch mode event"; + + const TouchModeEvent& touchModeEvent = static_cast<const TouchModeEvent&>(*event); + EXPECT_EQ(seq, consumeSeq); + EXPECT_EQ(eventId, touchModeEvent.getId()); + EXPECT_EQ(touchModeEnabled, touchModeEvent.isInTouchMode()); + + status = mConsumer->sendFinishedSignal(seq, true); + ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; + + Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse(); + ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK"; + ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result)); + const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result); + ASSERT_EQ(seq, finish.seq) + << "receiveConsumerResponse should have returned the original sequence number"; + ASSERT_TRUE(finish.handled) + << "receiveConsumerResponse should have set handled to consumer's reply"; + ASSERT_GE(finish.consumeTime, publishTime) + << "finished signal's consume time should be greater than publish time"; +} + TEST_F(InputPublisherAndConsumerTest, SendTimeline) { const int32_t inputEventId = 20; std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline; @@ -449,6 +490,10 @@ TEST_F(InputPublisherAndConsumerTest, PublishDragEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent()); } +TEST_F(InputPublisherAndConsumerTest, PublishTouchModeEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent()); +} + TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) { status_t status; const size_t pointerCount = 1; @@ -520,6 +565,7 @@ TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent()); } } // namespace android diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 59fed1fb41..18289a5f11 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -102,6 +102,10 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Timeline, eventId, 0); CHECK_OFFSET(InputMessage::Body::Timeline, empty, 4); CHECK_OFFSET(InputMessage::Body::Timeline, graphicsTimeline, 8); + + CHECK_OFFSET(InputMessage::Body::TouchMode, eventId, 0); + CHECK_OFFSET(InputMessage::Body::TouchMode, isInTouchMode, 4); + CHECK_OFFSET(InputMessage::Body::TouchMode, empty, 5); } void TestHeaderSize() { @@ -123,6 +127,7 @@ void TestBodySize() { static_assert(sizeof(InputMessage::Body::Focus) == 8); static_assert(sizeof(InputMessage::Body::Capture) == 8); static_assert(sizeof(InputMessage::Body::Drag) == 16); + static_assert(sizeof(InputMessage::Body::TouchMode) == 8); // Timeline static_assert(GraphicsTimeline::SIZE == 2); static_assert(sizeof(InputMessage::Body::Timeline) == 24); diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp index 2dd6c4fcaa..6a14d4f3d1 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -305,8 +305,9 @@ void Choreographer::scheduleLatestConfigRequest() { // Fortunately, these events are small so sending packets across the // socket should be atomic across processes. DisplayEventReceiver::Event event; - event.header = DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL, - PhysicalDisplayId(0), systemTime()}; + event.header = + DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL, + PhysicalDisplayId::fromPort(0), systemTime()}; injectEvent(event); } } diff --git a/libs/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/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index 467f848237..b1e1014fe4 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -1156,10 +1156,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) 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/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp index ae8f2384c4..0c5dd0a7e5 100644 --- a/libs/renderengine/skia/Cache.cpp +++ b/libs/renderengine/skia/Cache.cpp @@ -95,6 +95,7 @@ static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettin .alpha = 1, }; + base::unique_fd drawFence; auto layers = std::vector<const LayerSettings*>{&layer, &caster}; // When sourceDataspace matches dest, the general shadow fragment shader doesn't // have color correction added. @@ -111,7 +112,7 @@ static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettin for (bool translucent : {false, true}){ layer.shadow.casterIsTranslucent = translucent; renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd(), nullptr); + base::unique_fd(), &drawFence); } } } @@ -138,6 +139,7 @@ static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySetting }}, }; + base::unique_fd drawFence; auto layers = std::vector<const LayerSettings*>{&layer}; for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) { layer.sourceDataspace = dataspace; @@ -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(), &drawFence); } } } @@ -174,13 +176,14 @@ static void drawSolidLayers(SkiaRenderEngine* renderengine, const DisplaySetting .alpha = 0.5, }; + base::unique_fd drawFence; auto layers = std::vector<const LayerSettings*>{&layer}; for (auto transform : {mat4(), kScaleAndTranslate}) { layer.geometry.positionTransform = transform; for (float roundedCornersRadius : {0.0f, 50.f}) { layer.geometry.roundedCornersRadius = roundedCornersRadius; renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd(), nullptr); + base::unique_fd(), &drawFence); } } } @@ -199,12 +202,13 @@ static void drawBlurLayers(SkiaRenderEngine* renderengine, const DisplaySettings .skipContentDraw = true, }; + base::unique_fd drawFence; auto layers = std::vector<const LayerSettings*>{&layer}; // Different blur code is invoked for radii less and greater than 30 pixels for (int radius : {9, 60}) { layer.backgroundBlurRadius = radius; renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd(), nullptr); + base::unique_fd(), &drawFence); } } @@ -240,6 +244,7 @@ static void drawClippedLayers(SkiaRenderEngine* renderengine, const DisplaySetti }, }; + base::unique_fd drawFence; auto layers = std::vector<const LayerSettings*>{&layer}; for (auto pixelSource : {bufferSource, bufferOpaque, colorSource}) { layer.source = pixelSource; @@ -251,7 +256,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(), &drawFence); } } } @@ -287,9 +292,10 @@ static void drawPIPImageLayer(SkiaRenderEngine* renderengine, const DisplaySetti }; + base::unique_fd drawFence; auto layers = std::vector<const LayerSettings*>{&layer}; renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd(), nullptr); + base::unique_fd(), &drawFence); } static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display, @@ -316,9 +322,10 @@ static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySett }; + base::unique_fd drawFence; auto layers = std::vector<const LayerSettings*>{&layer}; renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd(), nullptr); + base::unique_fd(), &drawFence); } // @@ -421,6 +428,14 @@ 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}; + renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, + base::unique_fd(), nullptr); // null drawFence makes it synchronous + 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 f4b07d06a5..9fbbdc34ba 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -812,28 +812,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()); @@ -1149,6 +1127,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) { @@ -1166,66 +1211,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/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 33e3773d50..33053a0a61 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -524,10 +524,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 +1191,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, @@ -1647,11 +1621,6 @@ TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) { fillBufferWithoutPremultiplyAlpha(); } -TEST_P(RenderEngineTest, drawLayers_clearRegion) { - initializeRenderEngine(); - clearRegion(); -} - TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { initializeRenderEngine(); @@ -1900,6 +1869,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/ui/Android.bp b/libs/ui/Android.bp index 74d17ced74..5a118eccd3 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -137,7 +137,6 @@ cc_library_shared { "HdrCapabilities.cpp", "PixelFormat.cpp", "PublicFormat.cpp", - "Size.cpp", "StaticDisplayInfo.cpp", ], diff --git a/libs/ui/Size.cpp b/libs/ui/Size.cpp deleted file mode 100644 index d2996d164d..0000000000 --- a/libs/ui/Size.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2019 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/Size.h> - -namespace android::ui { - -const Size Size::INVALID{-1, -1}; -const Size Size::EMPTY{0, 0}; - -} // namespace android::ui 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/Size.h b/libs/ui/include/ui/Size.h index f1e825286e..ecc192dcae 100644 --- a/libs/ui/include/ui/Size.h +++ b/libs/ui/include/ui/Size.h @@ -23,103 +23,70 @@ #include <type_traits> #include <utility> -namespace android { -namespace ui { +namespace android::ui { -// Forward declare a few things. -struct Size; -bool operator==(const Size& lhs, const Size& rhs); - -/** - * A simple value type representing a two-dimensional size - */ +// A simple value type representing a two-dimensional size. struct Size { - int32_t width; - int32_t height; + int32_t width = -1; + int32_t height = -1; - // Special values - static const Size INVALID; - static const Size EMPTY; + constexpr Size() = default; - // ------------------------------------------------------------------------ - // Construction - // ------------------------------------------------------------------------ - - Size() : Size(INVALID) {} template <typename T> - Size(T&& w, T&& h) - : width(Size::clamp<int32_t, T>(std::forward<T>(w))), - height(Size::clamp<int32_t, T>(std::forward<T>(h))) {} - - // ------------------------------------------------------------------------ - // Accessors - // ------------------------------------------------------------------------ + constexpr Size(T w, T h) : width(clamp<int32_t>(w)), height(clamp<int32_t>(h)) {} int32_t getWidth() const { return width; } int32_t getHeight() const { return height; } + // Valid means non-negative width and height + bool isValid() const { return width >= 0 && height >= 0; } + + // Empty means zero width and height + bool isEmpty() const; + template <typename T> - void setWidth(T&& v) { - width = Size::clamp<int32_t, T>(std::forward<T>(v)); + void setWidth(T v) { + width = clamp<int32_t>(v); } + template <typename T> - void setHeight(T&& v) { - height = Size::clamp<int32_t, T>(std::forward<T>(v)); + void setHeight(T v) { + height = clamp<int32_t>(v); } - // ------------------------------------------------------------------------ - // Assignment - // ------------------------------------------------------------------------ + void set(Size size) { *this = size; } - void set(const Size& size) { *this = size; } template <typename T> - void set(T&& w, T&& h) { - set(Size(std::forward<T>(w), std::forward<T>(h))); + void set(T w, T h) { + set(Size(w, h)); } - // Sets the value to INVALID - void makeInvalid() { set(INVALID); } + // Sets the value to kInvalidSize + void makeInvalid(); - // Sets the value to EMPTY - void clear() { set(EMPTY); } + // Sets the value to kEmptySize + void clear(); - // ------------------------------------------------------------------------ - // Semantic checks - // ------------------------------------------------------------------------ - - // Valid means non-negative width and height - bool isValid() const { return width >= 0 && height >= 0; } - - // Empty means zero width and height - bool isEmpty() const { return *this == EMPTY; } - - // ------------------------------------------------------------------------ - // Clamp Helpers - // ------------------------------------------------------------------------ - - // Note: We use only features available in C++11 here for compatibility with - // external targets which include this file directly or indirectly and which - // themselves use C++11. - - // C++11 compatible replacement for std::remove_cv_reference_t [C++20] + // TODO: Replace with std::remove_cvref_t in C++20. template <typename T> - using remove_cv_reference_t = - typename std::remove_cv<typename std::remove_reference<T>::type>::type; + using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>; // Takes a value of type FromType, and ensures it can be represented as a value of type ToType, // clamping the input value to the output range if necessary. template <typename ToType, typename FromType> - static Size::remove_cv_reference_t<ToType> - clamp(typename std::enable_if< - std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_specialized && - std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_specialized, - FromType>::type v) { - using BareToType = remove_cv_reference_t<ToType>; - using BareFromType = remove_cv_reference_t<FromType>; - static constexpr auto toHighest = std::numeric_limits<BareToType>::max(); - static constexpr auto toLowest = std::numeric_limits<BareToType>::lowest(); - static constexpr auto fromHighest = std::numeric_limits<BareFromType>::max(); - static constexpr auto fromLowest = std::numeric_limits<BareFromType>::lowest(); + static constexpr remove_cvref_t<ToType> clamp(FromType v) { + using BareToType = remove_cvref_t<ToType>; + using ToLimits = std::numeric_limits<BareToType>; + + using BareFromType = remove_cvref_t<FromType>; + using FromLimits = std::numeric_limits<BareFromType>; + + static_assert(ToLimits::is_specialized && FromLimits::is_specialized); + + constexpr auto toHighest = ToLimits::max(); + constexpr auto toLowest = ToLimits::lowest(); + constexpr auto fromHighest = FromLimits::max(); + constexpr auto fromLowest = FromLimits::lowest(); // Get the closest representation of [toLowest, toHighest] in type // FromType to use to clamp the input value before conversion. @@ -127,37 +94,35 @@ struct Size { // std::common_type<...> is used to get a value-preserving type for the // top end of the range. using CommonHighestType = std::common_type_t<BareToType, BareFromType>; + using CommonLimits = std::numeric_limits<CommonHighestType>; // std::make_signed<std::common_type<...>> is used to get a // value-preserving type for the bottom end of the range, except this is // a bit trickier for non-integer types like float. - using CommonLowestType = - std::conditional_t<std::numeric_limits<CommonHighestType>::is_integer, - std::make_signed_t<std::conditional_t< - std::numeric_limits<CommonHighestType>::is_integer, - CommonHighestType, int /* not used */>>, - CommonHighestType>; + using CommonLowestType = std::conditional_t< + CommonLimits::is_integer, + std::make_signed_t<std::conditional_t<CommonLimits::is_integer, CommonHighestType, + int /* not used */>>, + CommonHighestType>; // We can then compute the clamp range in a way that can be later // trivially converted to either the 'from' or 'to' types, and be - // representabile in either. - static constexpr auto commonClampHighest = - std::min(static_cast<CommonHighestType>(fromHighest), - static_cast<CommonHighestType>(toHighest)); - static constexpr auto commonClampLowest = - std::max(static_cast<CommonLowestType>(fromLowest), - static_cast<CommonLowestType>(toLowest)); + // representable in either. + constexpr auto commonClampHighest = std::min(static_cast<CommonHighestType>(fromHighest), + static_cast<CommonHighestType>(toHighest)); + constexpr auto commonClampLowest = std::max(static_cast<CommonLowestType>(fromLowest), + static_cast<CommonLowestType>(toLowest)); - static constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest); - static constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest); + constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest); + constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest); // A clamp is needed only if the range we are clamping to is not the // same as the range of the input. - static constexpr bool isClampNeeded = + constexpr bool isClampNeeded = (fromLowest != fromClampLowest) || (fromHighest != fromClampHighest); // If a clamp is not needed, the conversion is just a trivial cast. - if (!isClampNeeded) { + if constexpr (!isClampNeeded) { return static_cast<BareToType>(v); } @@ -170,34 +135,46 @@ struct Size { // Otherwise clamping is done by using the already computed endpoints // for each type. - return (v <= fromClampLowest) - ? toClampLowest - : ((v >= fromClampHighest) ? toClampHighest : static_cast<BareToType>(v)); + if (v <= fromClampLowest) { + return toClampLowest; + } + + return v >= fromClampHighest ? toClampHighest : static_cast<BareToType>(v); } }; -// ------------------------------------------------------------------------ -// Comparisons -// ------------------------------------------------------------------------ +constexpr Size kInvalidSize; +constexpr Size kEmptySize{0, 0}; + +inline void Size::makeInvalid() { + set(kInvalidSize); +} -inline bool operator==(const Size& lhs, const Size& rhs) { +inline void Size::clear() { + set(kEmptySize); +} + +inline bool operator==(Size lhs, Size rhs) { return lhs.width == rhs.width && lhs.height == rhs.height; } -inline bool operator!=(const Size& lhs, const Size& rhs) { - return !operator==(lhs, rhs); +inline bool Size::isEmpty() const { + return *this == kEmptySize; +} + +inline bool operator!=(Size lhs, Size rhs) { + return !(lhs == rhs); } -inline bool operator<(const Size& lhs, const Size& rhs) { +inline bool operator<(Size lhs, Size rhs) { // Orders by increasing width, then height. if (lhs.width != rhs.width) return lhs.width < rhs.width; return lhs.height < rhs.height; } // Defining PrintTo helps with Google Tests. -static inline void PrintTo(const Size& size, ::std::ostream* os) { - *os << "Size(" << size.width << ", " << size.height << ")"; +inline void PrintTo(Size size, std::ostream* stream) { + *stream << "Size(" << size.width << ", " << size.height << ')'; } -} // namespace ui -} // namespace android +} // namespace android::ui diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp index 1d908b8ef1..8ddee7e740 100644 --- a/libs/ui/tests/DisplayId_test.cpp +++ b/libs/ui/tests/DisplayId_test.cpp @@ -32,6 +32,9 @@ TEST(DisplayIdTest, createPhysicalIdFromEdid) { EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); EXPECT_TRUE(PhysicalDisplayId::tryCast(id)); EXPECT_TRUE(HalDisplayId::tryCast(id)); + + EXPECT_EQ(id, DisplayId::fromValue(id.value)); + EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value)); } TEST(DisplayIdTest, createPhysicalIdFromPort) { @@ -43,6 +46,9 @@ TEST(DisplayIdTest, createPhysicalIdFromPort) { EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); EXPECT_TRUE(PhysicalDisplayId::tryCast(id)); EXPECT_TRUE(HalDisplayId::tryCast(id)); + + EXPECT_EQ(id, DisplayId::fromValue(id.value)); + EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value)); } TEST(DisplayIdTest, createGpuVirtualId) { @@ -52,6 +58,9 @@ TEST(DisplayIdTest, createGpuVirtualId) { EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); EXPECT_FALSE(HalDisplayId::tryCast(id)); + + EXPECT_EQ(id, DisplayId::fromValue(id.value)); + EXPECT_EQ(id, DisplayId::fromValue<GpuVirtualDisplayId>(id.value)); } TEST(DisplayIdTest, createHalVirtualId) { @@ -61,6 +70,9 @@ TEST(DisplayIdTest, createHalVirtualId) { EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); EXPECT_TRUE(HalDisplayId::tryCast(id)); + + EXPECT_EQ(id, DisplayId::fromValue(id.value)); + EXPECT_EQ(id, DisplayId::fromValue<HalVirtualDisplayId>(id.value)); } } // namespace android::ui diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp index 5f75aeabeb..acef47fb97 100644 --- a/libs/ui/tests/Size_test.cpp +++ b/libs/ui/tests/Size_test.cpp @@ -93,9 +93,8 @@ TEST(SizeTest, ValidAndEmpty) { } { - const auto& s = Size::INVALID; - EXPECT_FALSE(s.isValid()); - EXPECT_FALSE(s.isEmpty()); + EXPECT_FALSE(kInvalidSize.isValid()); + EXPECT_FALSE(kInvalidSize.isEmpty()); } { @@ -112,9 +111,8 @@ TEST(SizeTest, ValidAndEmpty) { } { - const auto& s = Size::EMPTY; - EXPECT_TRUE(s.isValid()); - EXPECT_TRUE(s.isEmpty()); + EXPECT_TRUE(kEmptySize.isValid()); + EXPECT_TRUE(kEmptySize.isEmpty()); } { diff --git a/services/automotive/display/AutomotiveDisplayProxyService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp index d6fc69562b..d205231033 100644 --- a/services/automotive/display/AutomotiveDisplayProxyService.cpp +++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp @@ -34,7 +34,10 @@ AutomotiveDisplayProxyService::getIGraphicBufferProducer(uint64_t id) { sp<IBinder> displayToken = nullptr; sp<SurfaceControl> surfaceControl = nullptr; if (it == mDisplays.end()) { - displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id)); + if (const auto displayId = DisplayId::fromValue<PhysicalDisplayId>(id)) { + displayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId); + } + if (displayToken == nullptr) { ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id); return nullptr; @@ -157,7 +160,11 @@ Return<void> AutomotiveDisplayProxyService::getDisplayInfo(uint64_t id, getDispl HwDisplayConfig activeConfig; HwDisplayState activeState; - auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id)); + sp<IBinder> displayToken; + if (const auto displayId = DisplayId::fromValue<PhysicalDisplayId>(id)) { + displayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId); + } + if (displayToken == nullptr) { ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id); } else { diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index b5ed0640cd..288068f584 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -176,10 +176,11 @@ std::string KeyEntry::getDescription() const { } return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64 ", source=0x%08x, displayId=%" PRId32 ", action=%s, " - "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, " + "flags=0x%08x, keyCode=%s(%d), scanCode=%d, metaState=0x%08x, " "repeatCount=%d), policyFlags=0x%08x", deviceId, eventTime, source, displayId, KeyEvent::actionToString(action), - flags, keyCode, scanCode, metaState, repeatCount, policyFlags); + flags, KeyEvent::getLabel(keyCode), keyCode, scanCode, metaState, + repeatCount, policyFlags); } void KeyEntry::recycle() { @@ -318,17 +319,4 @@ uint32_t DispatchEntry::nextSeq() { return seq; } -// --- CommandEntry --- - -CommandEntry::CommandEntry(Command command) - : command(command), - eventTime(0), - keyEntry(nullptr), - userActivityEventType(0), - seq(0), - handled(false), - enabled(false) {} - -CommandEntry::~CommandEntry() {} - } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 1b7fcf2f75..8feb98243b 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -245,55 +245,6 @@ private: VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry); VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry); -class InputDispatcher; -// A command entry captures state and behavior for an action to be performed in the -// dispatch loop after the initial processing has taken place. It is essentially -// a kind of continuation used to postpone sensitive policy interactions to a point -// in the dispatch loop where it is safe to release the lock (generally after finishing -// the critical parts of the dispatch cycle). -// -// The special thing about commands is that they can voluntarily release and reacquire -// the dispatcher lock at will. Initially when the command starts running, the -// dispatcher lock is held. However, if the command needs to call into the policy to -// do some work, it can release the lock, do the work, then reacquire the lock again -// before returning. -// -// This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch -// never calls into the policy while holding its lock. -// -// Commands are implicitly 'LockedInterruptible'. -struct CommandEntry; -typedef std::function<void(InputDispatcher&, CommandEntry*)> Command; - -class Connection; -struct CommandEntry { - explicit CommandEntry(Command command); - ~CommandEntry(); - - Command command; - - // parameters for the command (usage varies by command) - sp<Connection> connection; - nsecs_t eventTime; - std::shared_ptr<KeyEntry> keyEntry; - std::shared_ptr<SensorEntry> sensorEntry; - std::shared_ptr<InputApplicationHandle> inputApplicationHandle; - std::string reason; - int32_t userActivityEventType; - uint32_t seq; - bool handled; - sp<IBinder> connectionToken; - sp<IBinder> oldToken; - sp<IBinder> newToken; - std::string obscuringPackage; - bool enabled; - int32_t pid; - nsecs_t consumeTime; // time when the event was consumed by InputConsumer - int32_t displayId; - float x; - float y; -}; - } // namespace android::inputdispatcher #endif // _UI_INPUT_INPUTDISPATCHER_ENTRY_H diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index aa667803dd..1478a3c451 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> @@ -94,9 +66,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 +149,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 +179,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 +187,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 +212,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 +244,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 +265,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 +304,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 +321,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 +329,13 @@ static bool haveSameApplicationToken(const WindowInfo* first, const WindowInfo* first->applicationInfo.token == second->applicationInfo.token; } -static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { +bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT; } -static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget, - std::shared_ptr<EventEntry> eventEntry, - int32_t inputTargetFlags) { +std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget, + std::shared_ptr<EventEntry> eventEntry, + int32_t inputTargetFlags) { if (eventEntry->type == EventEntry::Type::MOTION) { const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry); if ((motionEntry.source & AINPUT_SOURCE_CLASS_JOYSTICK) || @@ -399,9 +412,9 @@ static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inp return dispatchEntry; } -static void addGestureMonitors(const std::vector<Monitor>& monitors, - std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0, - float yOffset = 0) { +void addGestureMonitors(const std::vector<Monitor>& monitors, + std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0, + float yOffset = 0) { if (monitors.empty()) { return; } @@ -411,9 +424,8 @@ static void addGestureMonitors(const std::vector<Monitor>& monitors, } } -static status_t openInputChannelPair(const std::string& name, - std::shared_ptr<InputChannel>& serverChannel, - std::unique_ptr<InputChannel>& clientChannel) { +status_t openInputChannelPair(const std::string& name, std::shared_ptr<InputChannel>& serverChannel, + std::unique_ptr<InputChannel>& clientChannel) { std::unique_ptr<InputChannel> uniqueServerChannel; status_t result = InputChannel::openInputChannelPair(name, uniqueServerChannel, clientChannel); @@ -422,7 +434,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 +444,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 +453,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 +461,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 +475,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 +488,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 +524,22 @@ static bool shouldReportFinishedEvent(const DispatchEntry& dispatchEntry, return true; } +/** + * Connection is responsive if it has no events in the waitQueue that are older than the + * current time. + */ +bool isConnectionResponsive(const Connection& connection) { + const nsecs_t currentTime = now(); + for (const DispatchEntry* entry : connection.waitQueue) { + if (entry->timeoutTime < currentTime) { + return false; + } + } + return true; +} + +} // namespace + // --- InputDispatcher --- InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) @@ -546,17 +573,17 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic } InputDispatcher::~InputDispatcher() { - { // acquire lock - std::scoped_lock _l(mLock); + std::scoped_lock _l(mLock); - resetKeyRepeatLocked(); - releasePendingEventLocked(); - drainInboundQueueLocked(); - } + resetKeyRepeatLocked(); + releasePendingEventLocked(); + drainInboundQueueLocked(); + mCommandQueue.clear(); while (!mConnectionsByToken.empty()) { sp<Connection> connection = mConnectionsByToken.begin()->second; - removeInputChannel(connection->inputChannel->getConnectionToken()); + removeInputChannelLocked(connection->inputChannel->getConnectionToken(), + false /* notify */); } } @@ -596,7 +623,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; } @@ -911,8 +938,7 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt } // Alternatively, maybe there's a gesture monitor that could handle this event - std::vector<TouchedMonitor> gestureMonitors = - findTouchedGestureMonitorsLocked(displayId, {}); + std::vector<TouchedMonitor> gestureMonitors = findTouchedGestureMonitorsLocked(displayId); for (TouchedMonitor& gestureMonitor : gestureMonitors) { sp<Connection> connection = getConnectionLocked(gestureMonitor.monitor.inputChannel->getConnectionToken()); @@ -957,9 +983,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; @@ -1006,11 +1032,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); @@ -1027,16 +1051,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; } @@ -1054,17 +1068,11 @@ sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayI } std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked( - int32_t displayId, const std::vector<sp<WindowInfoHandle>>& portalWindows) const { + int32_t displayId) const { std::vector<TouchedMonitor> touchedMonitors; std::vector<Monitor> monitors = getValueByKey(mGestureMonitorsByDisplay, displayId); addGestureMonitors(monitors, touchedMonitors); - for (const sp<WindowInfoHandle>& portalWindow : portalWindows) { - const WindowInfo* windowInfo = portalWindow->getInfo(); - monitors = getValueByKey(mGestureMonitorsByDisplay, windowInfo->portalToDisplayId); - addGestureMonitors(monitors, touchedMonitors, -windowInfo->frameLeft, - -windowInfo->frameTop); - } return touchedMonitors; } @@ -1072,9 +1080,9 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason const char* reason; switch (dropReason) { case DropReason::POLICY: -#if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("Dropped event because policy consumed it."); -#endif + if (DEBUG_INBOUND_EVENT_DETAILS) { + ALOGD("Dropped event because policy consumed it."); + } reason = "inbound event was dropped because the policy consumed it"; break; case DropReason::DISABLED: @@ -1158,37 +1166,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() { @@ -1210,9 +1216,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) { @@ -1247,27 +1253,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) { @@ -1408,9 +1415,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(); } @@ -1441,13 +1448,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; @@ -1489,47 +1496,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, + NamedEnum::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, + NamedEnum::string(sensorType).c_str()); + } { // acquire lock std::scoped_lock _l(mLock); @@ -1600,23 +1602,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, @@ -1657,42 +1642,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=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)); + } + } } 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); @@ -2004,12 +1990,11 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)); } bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN; - newTouchedWindowHandle = - findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, - isDown /*addOutsideTargets*/, true /*addPortalWindows*/); + newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, + isDown /*addOutsideTargets*/); std::vector<TouchedMonitor> newGestureMonitors = isDown - ? findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows) + ? findTouchedGestureMonitorsLocked(displayId) : std::vector<TouchedMonitor>{}; // Figure out whether splitting will be allowed for this window. @@ -2057,7 +2042,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); @@ -2174,10 +2159,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)); } @@ -2187,10 +2172,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)); @@ -2380,13 +2365,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(); } @@ -2415,8 +2399,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)) { @@ -2433,7 +2416,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(); } } @@ -2721,9 +2704,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; } } @@ -2761,12 +2744,12 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { } } - 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, @@ -2779,21 +2762,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; } @@ -2892,10 +2875,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; @@ -2925,11 +2909,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; @@ -2945,11 +2929,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 } @@ -3081,10 +3065,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, @@ -3094,9 +3079,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(); @@ -3234,11 +3219,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, " @@ -3305,10 +3290,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) { @@ -3316,16 +3301,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); @@ -3340,7 +3328,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)); } } } @@ -3407,7 +3403,7 @@ int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionTok gotOne = true; } if (gotOne) { - runCommandsLockedInterruptible(); + runCommandsLockedInterruptable(); if (status == WOULD_BLOCK) { return 1; } @@ -3484,12 +3480,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 = @@ -3553,10 +3549,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 = @@ -3699,9 +3695,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 @@ -3755,14 +3751,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; } @@ -3833,33 +3829,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; @@ -3912,6 +3908,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 @@ -3922,12 +3925,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, + NamedEnum::string(args->sensorType).c_str()); + } bool needWake; { // acquire lock @@ -3950,10 +3953,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); } @@ -3962,11 +3965,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; @@ -3974,10 +3977,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 @@ -3994,10 +3997,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->enabled ? "true" : "false"); -#endif + if (DEBUG_INBOUND_EVENT_DETAILS) { + ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime, + args->enabled ? "true" : "false"); + } bool needWake; { // acquire lock @@ -4015,11 +4018,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; @@ -4197,10 +4200,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; } @@ -4211,16 +4214,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; } @@ -4233,10 +4236,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; } @@ -4283,11 +4286,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. @@ -4447,8 +4450,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) || @@ -4693,7 +4695,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); @@ -5000,14 +5002,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"; @@ -5029,7 +5023,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, " @@ -5037,8 +5031,7 @@ 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, @@ -5134,6 +5127,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) { @@ -5200,9 +5199,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; @@ -5483,46 +5482,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); + } + + bool restartEvent; + if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) { + KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry)); + restartEvent = + afterKeyEventLockedInterruptable(connection, dispatchEntry, keyEntry, handled); + } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) { + MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry)); + restartEvent = afterMotionEventLockedInterruptable(connection, dispatchEntry, motionEntry, + handled); + } else { + restartEvent = false; + } + + // Dequeue the event and start the next cycle. + // Because the lock might have been released, it is possible that the + // contents of the wait queue to have been drained, so we need to double-check + // a few things. + dispatchEntryIt = connection->findWaitQueueEntry(seq); + if (dispatchEntryIt != connection->waitQueue.end()) { + dispatchEntry = *dispatchEntryIt; + connection->waitQueue.erase(dispatchEntryIt); + const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken(); + mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken); + if (!connection->responsive) { + connection->responsive = isConnectionResponsive(*connection); + if (connection->responsive) { + // The connection was unresponsive, and now it's responsive. + processConnectionResponsiveLocked(*connection); + } + } + traceWaitQueueLength(*connection); + if (restartEvent && connection->status == Connection::STATUS_NORMAL) { + connection->outboundQueue.push_front(dispatchEntry); + traceOutboundQueueLength(*connection); + } else { + releaseDispatchEntry(dispatchEntry); + } + } -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()); + // Start the next dispatch cycle for this connection. + startDispatchCycleLocked(now(), connection); +} - std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( - &InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); - commandEntry->connection = connection; - postCommandLocked(std::move(commandEntry)); +void InputDispatcher::sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, + const sp<IBinder>& newToken) { + auto command = [this, oldToken, newToken]() REQUIRES(mLock) { + scoped_unlock unlock(mLock); + mPolicy->notifyFocusChanged(oldToken, newToken); + }; + postCommandLocked(std::move(command)); } -void InputDispatcher::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) { @@ -5565,17 +5610,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, @@ -5606,109 +5645,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; @@ -5716,122 +5670,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)); } /** @@ -5879,7 +5748,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) { @@ -5904,11 +5773,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); @@ -5936,21 +5806,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(); @@ -5984,19 +5854,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"); @@ -6010,18 +5880,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. @@ -6037,16 +5907,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); @@ -6055,21 +5925,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()); @@ -6181,7 +6042,7 @@ void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& ch disablePointerCaptureForcedLocked(); if (mFocusedDisplayId == changes.displayId) { - notifyFocusChangedLocked(changes.oldFocus, changes.newFocus); + sendFocusChangedCommandLocked(changes.oldFocus, changes.newFocus); } } @@ -6215,19 +6076,11 @@ void InputDispatcher::disablePointerCaptureForcedLocked() { } void InputDispatcher::setPointerCaptureLocked(bool enabled) { - std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( - &InputDispatcher::doSetPointerCaptureLockedInterruptible); - commandEntry->enabled = enabled; - postCommandLocked(std::move(commandEntry)); -} - -void InputDispatcher::doSetPointerCaptureLockedInterruptible( - android::inputdispatcher::CommandEntry* commandEntry) { - mLock.unlock(); - - mPolicy->setPointerCapture(commandEntry->enabled); - - mLock.lock(); + auto command = [this, enabled]() REQUIRES(mLock) { + scoped_unlock unlock(mLock); + mPolicy->setPointerCapture(enabled); + }; + postCommandLocked(std::move(command)); } void InputDispatcher::displayRemoved(int32_t displayId) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 2436e73367..6df333a154 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -171,7 +171,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 +234,6 @@ private: sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool addOutsideTargets = false, - bool addPortalWindows = false, bool ignoreDragWindow = false) REQUIRES(mLock); @@ -296,8 +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); @@ -405,7 +423,7 @@ private: DropReason& dropReason) 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); @@ -452,24 +470,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. @@ -497,9 +502,7 @@ private: android::os::InputEventInjectionResult findTouchedWindowTargetsLocked( nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) REQUIRES(mLock); - std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked( - int32_t displayId, - const std::vector<sp<android::gui::WindowInfoHandle>>& portalWindows) const + std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(int32_t displayId) const REQUIRES(mLock); std::vector<TouchedMonitor> selectResponsiveMonitorsLocked( const std::vector<TouchedMonitor>& gestureMonitors) const REQUIRES(mLock); @@ -605,53 +608,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); // Statistics gathering. LatencyAggregator mLatencyAggregator GUARDED_BY(mLock); diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp index d634dcda1d..52f189c5c5 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.cpp +++ b/services/inputflinger/dispatcher/LatencyTracker.cpp @@ -50,13 +50,12 @@ static bool isMatureEvent(nsecs_t eventTime, nsecs_t now) { * key-value pair. Equivalent to the imaginary std api std::multimap::erase(key, value). */ template <typename K, typename V> -static void eraseByKeyAndValue(std::multimap<K, V>& map, K key, V value) { - auto iterpair = map.equal_range(key); - - for (auto it = iterpair.first; it != iterpair.second; ++it) { +static void eraseByValue(std::multimap<K, V>& map, const V& value) { + for (auto it = map.begin(); it != map.end();) { if (it->second == value) { - map.erase(it); - break; + it = map.erase(it); + } else { + it++; } } } @@ -76,9 +75,7 @@ void LatencyTracker::trackListener(int32_t inputEventId, bool isDown, nsecs_t ev // confuse us by reporting the rest of the timeline for one of them. This should happen // rarely, so we won't lose much data mTimelines.erase(it); - // In case we have another input event with a different id and at the same eventTime, - // only erase this specific inputEventId. - eraseByKeyAndValue(mEventTimes, eventTime, inputEventId); + eraseByValue(mEventTimes, inputEventId); return; } mTimelines.emplace(inputEventId, InputEventTimeline(isDown, eventTime, readTime)); @@ -90,7 +87,8 @@ void LatencyTracker::trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& nsecs_t finishTime) { const auto it = mTimelines.find(inputEventId); if (it == mTimelines.end()) { - // It's possible that an app sends a bad (or late)'Finish' signal, since it's free to do + // This could happen if we erased this event when duplicate events were detected. It's + // also possible that an app sent a bad (or late) 'Finish' signal, since it's free to do // anything in its process. Just drop the report and move on. return; } @@ -120,7 +118,8 @@ void LatencyTracker::trackGraphicsLatency( std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) { const auto it = mTimelines.find(inputEventId); if (it == mTimelines.end()) { - // It's possible that an app sends a bad (or late) 'Timeline' signal, since it's free to do + // This could happen if we erased this event when duplicate events were detected. It's + // also possible that an app sent a bad (or late) 'Timeline' signal, since it's free to do // anything in its process. Just drop the report and move on. return; } @@ -166,14 +165,6 @@ void LatencyTracker::reportAndPruneMatureRecords(nsecs_t newEventTime) { } } -void LatencyTracker::reportNow() { - for (const auto& [inputEventId, timeline] : mTimelines) { - mTimelineProcessor->processTimeline(timeline); - } - mTimelines.clear(); - mEventTimes.clear(); -} - std::string LatencyTracker::dump(const char* prefix) { return StringPrintf("%sLatencyTracker:\n", prefix) + StringPrintf("%s mTimelines.size() = %zu\n", prefix, mTimelines.size()) + diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h index 289b8ed6c4..4b0c618590 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.h +++ b/services/inputflinger/dispatcher/LatencyTracker.h @@ -43,6 +43,12 @@ public: LatencyTracker(InputEventTimelineProcessor* processor); /** * Start keeping track of an event identified by inputEventId. This must be called first. + * If duplicate events are encountered (events that have the same eventId), none of them will be + * tracked. This is because there is not enough information to correctly track them. The api's + * 'trackFinishedEvent' and 'trackGraphicsLatency' only contain the inputEventId, and not the + * eventTime. Even if eventTime was provided, there would still be a possibility of having + * duplicate events that happen to have the same eventTime and inputEventId. Therefore, we + * must drop all duplicate data. */ void trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, nsecs_t readTime); void trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken, @@ -50,14 +56,6 @@ public: void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline); - /** - * Report all collected events immediately, even if some of them are currently incomplete - * and may receive 'trackFinishedEvent' or 'trackGraphicsLatency' calls in the future. - * This is useful for tests. Otherwise, tests would have to inject additional "future" events, - * which is not convenient. - */ - void reportNow(); - std::string dump(const char* prefix); private: diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index 20b6eadf5b..b7ed658777 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -37,7 +37,6 @@ void TouchState::reset() { source = 0; displayId = ADISPLAY_ID_NONE; windows.clear(); - portalWindows.clear(); gestureMonitors.clear(); } @@ -48,7 +47,6 @@ void TouchState::copyFrom(const TouchState& other) { source = other.source; displayId = other.displayId; windows = other.windows; - portalWindows = other.portalWindows; gestureMonitors = other.gestureMonitors; } @@ -77,16 +75,6 @@ void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, int windows.push_back(touchedWindow); } -void TouchState::addPortalWindow(const sp<android::gui::WindowInfoHandle>& windowHandle) { - size_t numWindows = portalWindows.size(); - for (size_t i = 0; i < numWindows; i++) { - if (portalWindows[i] == windowHandle) { - return; - } - } - portalWindows.push_back(windowHandle); -} - void TouchState::addGestureMonitors(const std::vector<TouchedMonitor>& newMonitors) { const size_t newSize = gestureMonitors.size() + newMonitors.size(); gestureMonitors.reserve(newSize); @@ -119,7 +107,6 @@ void TouchState::filterNonAsIsTouchWindows() { void TouchState::filterNonMonitors() { windows.clear(); - portalWindows.clear(); } sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const { diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index a4e52b0d83..579b868443 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -36,11 +36,6 @@ struct TouchState { int32_t displayId; // id to the display that currently has a touch, others are rejected std::vector<TouchedWindow> windows; - // This collects the portal windows that the touch has gone through. Each portal window - // targets a display (embedded display for most cases). With this info, we can add the - // monitoring channels of the displays touched. - std::vector<sp<android::gui::WindowInfoHandle>> portalWindows; - std::vector<TouchedMonitor> gestureMonitors; TouchState(); diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp index e7e1937235..89c0741dfd 100644 --- a/services/inputflinger/tests/LatencyTracker_test.cpp +++ b/services/inputflinger/tests/LatencyTracker_test.cpp @@ -16,6 +16,7 @@ #include "../dispatcher/LatencyTracker.h" +#include <android-base/properties.h> #include <binder/Binder.h> #include <gtest/gtest.h> #include <inttypes.h> @@ -23,11 +24,16 @@ #define TAG "LatencyTracker_test" +using android::base::HwTimeoutMultiplier; using android::inputdispatcher::InputEventTimeline; using android::inputdispatcher::LatencyTracker; namespace android::inputdispatcher { +const std::chrono::duration ANR_TIMEOUT = std::chrono::milliseconds( + android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS * + HwTimeoutMultiplier()); + InputEventTimeline getTestTimeline() { InputEventTimeline t( /*isDown*/ true, @@ -57,6 +63,8 @@ protected: } void TearDown() override {} + void triggerEventReporting(nsecs_t lastEventTime); + void assertReceivedTimeline(const InputEventTimeline& timeline); /** * Timelines can be received in any order (order is not guaranteed). So if we are expecting more @@ -72,8 +80,17 @@ private: std::deque<InputEventTimeline> mReceivedTimelines; }; +/** + * Send an event that would trigger the reporting of all of the events that are at least as old as + * the provided 'lastEventTime'. + */ +void LatencyTrackerTest::triggerEventReporting(nsecs_t lastEventTime) { + const nsecs_t triggerEventTime = + lastEventTime + std::chrono::nanoseconds(ANR_TIMEOUT).count() + 1; + mTracker->trackListener(1 /*inputEventId*/, true /*isDown*/, triggerEventTime, 3 /*readTime*/); +} + void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeline) { - mTracker->reportNow(); ASSERT_FALSE(mReceivedTimelines.empty()); const InputEventTimeline& t = mReceivedTimelines.front(); ASSERT_EQ(timeline, t); @@ -88,7 +105,6 @@ void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeli * equal element in B, and for every element in B there is an equal element in A. */ void LatencyTrackerTest::assertReceivedTimelines(const std::vector<InputEventTimeline>& timelines) { - mTracker->reportNow(); ASSERT_EQ(timelines.size(), mReceivedTimelines.size()); for (const InputEventTimeline& expectedTimeline : timelines) { bool found = false; @@ -121,6 +137,7 @@ void LatencyTrackerTest::assertReceivedTimelines(const std::vector<InputEventTim */ TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) { mTracker->trackListener(1 /*inputEventId*/, false /*isDown*/, 2 /*eventTime*/, 3 /*readTime*/); + triggerEventReporting(2 /*eventTime*/); assertReceivedTimeline(InputEventTimeline{false, 2, 3}); } @@ -130,6 +147,7 @@ TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) { TEST_F(LatencyTrackerTest, TrackFinishedEvent_DoesNotTriggerReporting) { mTracker->trackFinishedEvent(1 /*inputEventId*/, connection1, 2 /*deliveryTime*/, 3 /*consumeTime*/, 4 /*finishTime*/); + triggerEventReporting(4 /*eventTime*/); assertReceivedTimelines({}); } @@ -141,6 +159,7 @@ TEST_F(LatencyTrackerTest, TrackGraphicsLatency_DoesNotTriggerReporting) { graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2; graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 3; mTracker->trackGraphicsLatency(1 /*inputEventId*/, connection2, graphicsTimeline); + triggerEventReporting(3 /*eventTime*/); assertReceivedTimelines({}); } @@ -155,9 +174,30 @@ TEST_F(LatencyTrackerTest, TrackAllParameters_ReportsFullTimeline) { expectedCT.consumeTime, expectedCT.finishTime); mTracker->trackGraphicsLatency(inputEventId, connectionToken, expectedCT.graphicsTimeline); + triggerEventReporting(expected.eventTime); assertReceivedTimeline(expected); } +/** + * Send 2 events with the same inputEventId, but different eventTime's. Ensure that no crash occurs, + * and that the tracker drops such events completely. + */ +TEST_F(LatencyTrackerTest, WhenDuplicateEventsAreReported_DoesNotCrash) { + constexpr nsecs_t inputEventId = 1; + constexpr nsecs_t readTime = 3; // does not matter for this test + constexpr bool isDown = true; // does not matter for this test + + // In the following 2 calls to trackListener, the inputEventId's are the same, but event times + // are different. + mTracker->trackListener(inputEventId, isDown, 1 /*eventTime*/, readTime); + mTracker->trackListener(inputEventId, isDown, 2 /*eventTime*/, readTime); + + triggerEventReporting(2 /*eventTime*/); + // Since we sent duplicate input events, the tracker should just delete all of them, because it + // does not have enough information to properly track them. + assertReceivedTimelines({}); +} + TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) { constexpr int32_t inputEventId1 = 1; InputEventTimeline timeline1( @@ -204,6 +244,7 @@ TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) { mTracker->trackGraphicsLatency(inputEventId2, connection2, connectionTimeline2.graphicsTimeline); // Now both events should be completed + triggerEventReporting(timeline2.eventTime); assertReceivedTimelines({timeline1, timeline2}); } @@ -228,6 +269,7 @@ TEST_F(LatencyTrackerTest, IncompleteEvents_AreHandledConsistently) { mTracker->trackGraphicsLatency(1 /*inputEventId*/, token, expectedCT.graphicsTimeline); expectedTimelines[0].connectionTimelines.emplace(token, std::move(expectedCT)); + triggerEventReporting(timeline.eventTime); assertReceivedTimelines(expectedTimelines); } @@ -246,6 +288,7 @@ TEST_F(LatencyTrackerTest, EventsAreTracked_WhenTrackListenerIsCalledFirst) { mTracker->trackGraphicsLatency(inputEventId, connection1, expectedCT.graphicsTimeline); mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime); + triggerEventReporting(expected.eventTime); assertReceivedTimeline( InputEventTimeline{expected.isDown, expected.eventTime, expected.readTime}); } diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp new file mode 100644 index 0000000000..df4db199f8 --- /dev/null +++ b/services/inputflinger/tests/fuzzers/Android.bp @@ -0,0 +1,45 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + + +cc_fuzz { + name: "inputflinger_latencytracker_fuzzer", + defaults: [ + "inputflinger_defaults", + ], + include_dirs: [ + "frameworks/native/services/inputflinger", + ], + shared_libs: [ + "libbase", + "libbinder", + "liblog", + "libui", + "libutils", + "libinput", + "libinputflinger", + ], + srcs: [ + "LatencyTrackerFuzzer.cpp", + ], +} diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp new file mode 100644 index 0000000000..4f066ad513 --- /dev/null +++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fuzzer/FuzzedDataProvider.h> +#include "dispatcher/LatencyTracker.h" + +namespace android { + +namespace inputdispatcher { + +/** + * A processor of InputEventTimelines that does nothing with the provided data. + */ +class EmptyProcessor : public InputEventTimelineProcessor { +public: + /** + * Just ignore the provided timeline + */ + void processTimeline(const InputEventTimeline& timeline) override { + for (const auto& [token, connectionTimeline] : timeline.connectionTimelines) { + connectionTimeline.isComplete(); + } + }; +}; + +static sp<IBinder> getConnectionToken(FuzzedDataProvider& fdp, + std::array<sp<IBinder>, 10>& tokens) { + const bool useExistingToken = fdp.ConsumeBool(); + if (useExistingToken) { + return tokens[fdp.ConsumeIntegralInRange<size_t>(0ul, tokens.size() - 1)]; + } + return new BBinder(); +} + +extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { + FuzzedDataProvider fdp(data, size); + + EmptyProcessor emptyProcessor; + LatencyTracker tracker(&emptyProcessor); + + // Make some pre-defined tokens to ensure that some timelines are complete. + std::array<sp<IBinder> /*token*/, 10> predefinedTokens; + for (size_t i = 0; i < predefinedTokens.size(); i++) { + predefinedTokens[i] = new BBinder(); + } + + // Randomly invoke LatencyTracker api's until randomness is exhausted. + while (fdp.remaining_bytes() > 0) { + fdp.PickValueInArray<std::function<void()>>({ + [&]() -> void { + int32_t inputEventId = fdp.ConsumeIntegral<int32_t>(); + int32_t isDown = fdp.ConsumeBool(); + nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>(); + nsecs_t readTime = fdp.ConsumeIntegral<nsecs_t>(); + tracker.trackListener(inputEventId, isDown, eventTime, readTime); + }, + [&]() -> void { + int32_t inputEventId = fdp.ConsumeIntegral<int32_t>(); + sp<IBinder> connectionToken = getConnectionToken(fdp, predefinedTokens); + nsecs_t deliveryTime = fdp.ConsumeIntegral<nsecs_t>(); + nsecs_t consumeTime = fdp.ConsumeIntegral<nsecs_t>(); + nsecs_t finishTime = fdp.ConsumeIntegral<nsecs_t>(); + tracker.trackFinishedEvent(inputEventId, connectionToken, deliveryTime, + consumeTime, finishTime); + }, + [&]() -> void { + int32_t inputEventId = fdp.ConsumeIntegral<int32_t>(); + sp<IBinder> connectionToken = getConnectionToken(fdp, predefinedTokens); + std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline; + for (size_t i = 0; i < graphicsTimeline.size(); i++) { + graphicsTimeline[i] = fdp.ConsumeIntegral<nsecs_t>(); + } + tracker.trackGraphicsLatency(inputEventId, connectionToken, graphicsTimeline); + }, + })(); + } + + return 0; +} + +} // namespace inputdispatcher + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index 71db330f01..b600fad66d 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); diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp index 99e470dfe6..81254ac64e 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -153,11 +153,12 @@ bool BufferQueueLayer::fenceHasSignaled() const { } bool BufferQueueLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const { + Mutex::Autolock lock(mQueueItemLock); + if (!hasFrameUpdate() || isRemovedFromCurrentState()) { return true; } - Mutex::Autolock lock(mQueueItemLock); return mQueueItems[0].item.mTimestamp <= expectedPresentTime; } @@ -269,13 +270,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 +308,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 +317,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 +338,12 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime); } mQueueItems.erase(mQueueItems.begin()); + more_frames_pending = (mQueuedFrames.fetch_sub(1) > 1); } // Decrement the queued-frames count. Signal another event if we // have more frames pending. - if ((queuedBuffer && mQueuedFrames.fetch_sub(1) > 1) || mAutoRefresh) { + if ((queuedBuffer && more_frames_pending) || mAutoRefresh) { mFlinger->signalLayerUpdate(); } diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h index 14eddb1861..526e7daa80 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h @@ -40,7 +40,7 @@ struct DisplayCreationArgs { 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; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h index e51019ae99..6d1017f678 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h @@ -115,10 +115,6 @@ public: // If set to true, the target buffer has protected content support. const bool supportsProtectedContent; - // Modified by each call to prepareClientComposition to indicate the - // region of the target buffer that should be cleared. - Region& clearRegion; - // Viewport of the target being rendered to. This is used to determine // the shadow light position. const Rect& viewport; @@ -177,11 +173,10 @@ using LayerFESet = std::unordered_set<sp<LayerFE>, LayerFESpHash>; static inline bool operator==(const LayerFE::ClientCompositionTargetSettings& lhs, const LayerFE::ClientCompositionTargetSettings& rhs) { - return lhs.clip.hasSameRects(rhs.clip) && - lhs.needsFiltering == rhs.needsFiltering && lhs.isSecure == rhs.isSecure && + return lhs.clip.hasSameRects(rhs.clip) && lhs.needsFiltering == rhs.needsFiltering && + lhs.isSecure == rhs.isSecure && lhs.supportsProtectedContent == rhs.supportsProtectedContent && - lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.viewport == rhs.viewport && - lhs.dataspace == rhs.dataspace && + lhs.viewport == rhs.viewport && lhs.dataspace == rhs.dataspace && lhs.realContentIsVisible == rhs.realContentIsVisible && lhs.clearContent == rhs.clearContent; } @@ -202,8 +197,6 @@ static inline void PrintTo(const LayerFE::ClientCompositionTargetSettings& setti *os << "\n .needsFiltering = " << settings.needsFiltering; *os << "\n .isSecure = " << settings.isSecure; *os << "\n .supportsProtectedContent = " << settings.supportsProtectedContent; - *os << "\n .clearRegion = "; - PrintTo(settings.clearRegion, os); *os << "\n .viewport = "; PrintTo(settings.viewport, os); *os << "\n .dataspace = "; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h index 0ef0b995c3..15a86af037 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -294,7 +294,7 @@ protected: virtual bool getSkipColorTransform() const = 0; virtual FrameFences presentAndGetFrameFences() = 0; virtual std::vector<LayerFE::LayerSettings> generateClientCompositionRequests( - bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) = 0; + bool supportsProtectedContent, ui::Dataspace outputDataspace) = 0; virtual void appendRegionFlashRequests( const Region& flashRegion, std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index ddcc907a91..14f2163f52 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -111,8 +111,7 @@ protected: bool getSkipColorTransform() const override; compositionengine::Output::FrameFences presentAndGetFrameFences() override; std::vector<LayerFE::LayerSettings> generateClientCompositionRequests( - bool supportsProtectedContent, Region& clearRegion, - ui::Dataspace outputDataspace) override; + bool supportsProtectedContent, ui::Dataspace outputDataspace) override; void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override; void setExpensiveRenderingExpected(bool enabled) override; void dumpBase(std::string&) const; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h index 8fdf3ae887..216019fd1d 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -113,8 +113,8 @@ public: MOCK_METHOD1(renderCachedSets, void(const CompositionRefreshArgs&)); MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences()); - MOCK_METHOD3(generateClientCompositionRequests, - std::vector<LayerFE::LayerSettings>(bool, Region&, ui::Dataspace)); + MOCK_METHOD2(generateClientCompositionRequests, + std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace)); MOCK_METHOD2(appendRegionFlashRequests, void(const Region&, std::vector<LayerFE::LayerSettings>&)); MOCK_METHOD1(setExpensiveRenderingExpected, void(bool)); diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 3310a71c79..6ac488be29 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -1075,13 +1075,9 @@ std::optional<base::unique_fd> Output::composeSurfaces( clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix; } - // Note: Updated by generateClientCompositionRequests - clientCompositionDisplay.clearRegion = Region::INVALID_REGION; - // Generate the client composition requests for the layers on this output. std::vector<LayerFE::LayerSettings> clientCompositionLayers = generateClientCompositionRequests(supportsProtectedContent, - clientCompositionDisplay.clearRegion, clientCompositionDisplay.outputDataspace); appendRegionFlashRequests(debugRegion, clientCompositionLayers); @@ -1149,15 +1145,13 @@ std::optional<base::unique_fd> Output::composeSurfaces( } std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( - bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) { + bool supportsProtectedContent, ui::Dataspace outputDataspace) { std::vector<LayerFE::LayerSettings> clientCompositionLayers; ALOGV("Rendering client layers"); const auto& outputState = getState(); const Region viewportRegion(outputState.layerStackSpace.content); bool firstLayer = true; - // Used when a layer clears part of the buffer. - Region stubRegion; bool disableBlurs = false; sp<GraphicBuffer> previousOverrideBuffer = nullptr; @@ -1219,7 +1213,6 @@ std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( outputState.needsFiltering, .isSecure = outputState.isSecure, .supportsProtectedContent = supportsProtectedContent, - .clearRegion = clientComposition ? clearRegion : stubRegion, .viewport = outputState.layerStackSpace.content, .dataspace = outputDataspace, .realContentIsVisible = realContentIsVisible, diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp index c1cd5ab5fd..ccacdfb19b 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp @@ -171,13 +171,11 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& te .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, diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index c037cc6173..72b16e0741 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -60,7 +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 ui::Size DEFAULT_RESOLUTION{1920, 1080}; constexpr uint32_t DEFAULT_LAYER_STACK = 42; struct Layer { diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index ada5adfa12..58c6628f60 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, diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index ee73cfc0c1..c3185e9e7e 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -2992,8 +2992,8 @@ struct OutputComposeSurfacesTest : public testing::Test { // Sets up the helper functions called by the function under test to use // mock implementations. MOCK_CONST_METHOD0(getSkipColorTransform, bool()); - MOCK_METHOD3(generateClientCompositionRequests, - std::vector<LayerFE::LayerSettings>(bool, Region&, ui::Dataspace)); + MOCK_METHOD2(generateClientCompositionRequests, + std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace)); MOCK_METHOD2(appendRegionFlashRequests, void(const Region&, std::vector<LayerFE::LayerSettings>&)); MOCK_METHOD1(setExpensiveRenderingExpected, void(bool)); @@ -3142,7 +3142,7 @@ 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()); @@ -3165,7 +3165,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( @@ -3195,7 +3195,7 @@ TEST_F(OutputComposeSurfacesTest, 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( @@ -3223,7 +3223,7 @@ 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()); @@ -3252,7 +3252,7 @@ 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()); @@ -3281,7 +3281,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()); @@ -3316,7 +3316,7 @@ 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), _)) @@ -3339,7 +3339,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()); @@ -3391,7 +3391,7 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrMixedComposi .andIfSkipColorTransform(false) .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(), - Region::INVALID_REGION, kDefaultOutputOrientationFlags}) + kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); } @@ -3402,7 +3402,7 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrMixedComp .andIfSkipColorTransform(false) .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(), - Region::INVALID_REGION, kDefaultOutputOrientationFlags}) + kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); } @@ -3413,7 +3413,7 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrOnlyClientCo .andIfSkipColorTransform(false) .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, - kDefaultColorTransformMat, Region::INVALID_REGION, + kDefaultColorTransformMat, kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); @@ -3425,7 +3425,7 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrOnlyClien .andIfSkipColorTransform(false) .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, - kDefaultColorTransformMat, Region::INVALID_REGION, + kDefaultColorTransformMat, kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); @@ -3438,7 +3438,7 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, .andIfSkipColorTransform(true) .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(), - Region::INVALID_REGION, kDefaultOutputOrientationFlags}) + kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); } @@ -3469,7 +3469,7 @@ struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeS EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); - EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, _)) + EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _)) .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); @@ -3590,7 +3590,7 @@ 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. @@ -3612,7 +3612,7 @@ 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(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u)); @@ -3652,10 +3652,9 @@ struct GenerateClientCompositionRequestsTest : public testing::Test { struct OutputPartialMock : public OutputPartialMockBase { // compositionengine::Output overrides std::vector<LayerFE::LayerSettings> generateClientCompositionRequests( - bool supportsProtectedContent, Region& clearRegion, - ui::Dataspace dataspace) override { + bool supportsProtectedContent, ui::Dataspace dataspace) override { return impl::Output::generateClientCompositionRequests(supportsProtectedContent, - clearRegion, dataspace); + dataspace); } }; @@ -3739,11 +3738,9 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, handlesNoClientCompost EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(false)); - Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); EXPECT_EQ(0u, requests.size()); - EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, requiresVisibleRegionAfterViewportClip) { @@ -3751,11 +3748,9 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, requiresVisibleRegionA mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(4000, 0, 4010, 10)); mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 0, 0)); - Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); EXPECT_EQ(0u, requests.size()); - EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, gathersClientCompositionRequests) { @@ -3770,16 +3765,13 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, gathersClientCompositi .WillOnce(Return(std::vector<LayerFE::LayerSettings>( {mShadowSettings, mLayers[2].mLayerSettings}))); - Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); ASSERT_EQ(3u, requests.size()); EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]); EXPECT_EQ(mShadowSettings, requests[1]); EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]); - EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); - // Check that a timestamp was set for the layers that generated requests EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp); EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp); @@ -3810,16 +3802,13 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, overridesBlur) { .WillOnce(Return(std::vector<LayerFE::LayerSettings>( {mShadowSettings, mLayers[2].mLayerSettings}))); - Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); ASSERT_EQ(3u, requests.size()); EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]); EXPECT_EQ(mShadowSettings, requests[1]); EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]); - EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); - // Check that a timestamp was set for the layers that generated requests EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp); EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp); @@ -3843,13 +3832,10 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_)) .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings}))); - Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); ASSERT_EQ(1u, requests.size()); EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]); - - EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, @@ -3869,13 +3855,10 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_)) .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings}))); - Region accumClearRegion(Rect(10, 11, 12, 13)); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); ASSERT_EQ(1u, requests.size()); EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]); - - EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqueAndNotFirst) { @@ -3896,15 +3879,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 */, @@ -3916,7 +3896,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqu false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -3936,15 +3915,13 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqu .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings}))); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); ASSERT_EQ(2u, requests.size()); // The second layer is expected to be rendered as alpha=0 black with no blending EXPECT_EQ(mBlackoutSettings, requests[0]); EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]); - - EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13)))); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, @@ -3953,14 +3930,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 */, @@ -3972,7 +3946,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -3984,7 +3957,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4001,7 +3973,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, static_cast<void>( mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace)); + kDisplayDataspace)); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, @@ -4009,14 +3981,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 */, @@ -4028,7 +3997,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4040,7 +4008,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4057,7 +4024,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, static_cast<void>( mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace)); + kDisplayDataspace)); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, @@ -4065,14 +4032,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 */, @@ -4085,7 +4049,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, true, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4097,7 +4060,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, true, /* needs filtering */ false, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4114,21 +4076,18 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, static_cast<void>( mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace)); + kDisplayDataspace)); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, wholeOutputSecurityUsedToGenerateRequests) { mOutput.mState.isSecure = true; - Region accumClearRegion(Rect(10, 11, 12, 13)); - compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{ Region(kDisplayFrame), false, /* needs filtering */ true, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4140,7 +4099,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ true, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4152,7 +4110,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ true, /* secure */ false, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4169,19 +4126,17 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, static_cast<void>( mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace)); + kDisplayDataspace)); } TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, protectedContentSupportUsedToGenerateRequests) { - Region accumClearRegion(Rect(10, 11, 12, 13)); compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{ Region(kDisplayFrame), false, /* needs filtering */ false, /* secure */ true, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4193,7 +4148,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ true, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4205,7 +4159,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, false, /* needs filtering */ false, /* secure */ true, /* supports protected content */ - accumClearRegion, kDisplayViewport, kDisplayDataspace, true /* realContentIsVisible */, @@ -4221,7 +4174,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, .WillOnce(Return(std::vector<LayerFE::LayerSettings>())); static_cast<void>(mOutput.generateClientCompositionRequests(true /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace)); } @@ -4336,14 +4288,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 */, @@ -4361,7 +4310,6 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq false, /* needs filtering */ true, /* secure */ true, /* supports protected content */ - accumClearRegion, kPortraitViewport, kOutputDataspace, true /* realContentIsVisible */, @@ -4375,8 +4323,8 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq .WillOnce(Return(std::vector<LayerFE::LayerSettings>({rightLayer.mLayerSettings}))); constexpr bool supportsProtectedContent = true; - auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, - accumClearRegion, kOutputDataspace); + auto requests = + mOutput.generateClientCompositionRequests(supportsProtectedContent, kOutputDataspace); ASSERT_EQ(2u, requests.size()); EXPECT_EQ(leftLayer.mLayerSettings, requests[0]); EXPECT_EQ(rightLayer.mLayerSettings, requests[1]); @@ -4389,13 +4337,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 */, @@ -4415,7 +4361,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mShadowSettings}))); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); ASSERT_EQ(1u, requests.size()); EXPECT_EQ(mShadowSettings, requests[0]); @@ -4435,13 +4381,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 */, @@ -4456,7 +4400,7 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, {mShadowSettings, mLayers[2].mLayerSettings}))); auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */, - accumClearRegion, kDisplayDataspace); + kDisplayDataspace); ASSERT_EQ(2u, requests.size()); EXPECT_EQ(mShadowSettings, requests[0]); diff --git a/services/surfaceflinger/CompositionEngine/tests/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/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 4d435c7e47..afd12b59a3 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -354,16 +354,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/Layer.cpp b/services/surfaceflinger/Layer.cpp index 7523362087..db590070aa 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -819,11 +819,7 @@ void Layer::setZOrderRelativeOf(const wp<Layer>& relativeOf) { } bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) { - sp<Handle> handle = static_cast<Handle*>(relativeToHandle.get()); - if (handle == nullptr) { - return false; - } - sp<Layer> relative = handle->owner.promote(); + sp<Layer> relative = fromHandle(relativeToHandle).promote(); if (relative == nullptr) { return false; } @@ -1615,8 +1611,7 @@ void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) { bool Layer::reparent(const sp<IBinder>& newParentHandle) { sp<Layer> newParent; if (newParentHandle != nullptr) { - auto handle = static_cast<Handle*>(newParentHandle.get()); - newParent = handle->owner.promote(); + newParent = fromHandle(newParentHandle).promote(); if (newParent == nullptr) { ALOGE("Unable to promote Layer handle"); return false; @@ -1991,24 +1986,10 @@ void Layer::commitChildList() { mDrawingParent = mCurrentParent; } -static wp<Layer> extractLayerFromBinder(const wp<IBinder>& weakBinderHandle) { - if (weakBinderHandle == nullptr) { - return nullptr; - } - sp<IBinder> binderHandle = weakBinderHandle.promote(); - if (binderHandle == nullptr) { - return nullptr; - } - sp<Layer::Handle> handle = static_cast<Layer::Handle*>(binderHandle.get()); - if (handle == nullptr) { - return nullptr; - } - return handle->owner; -} void Layer::setInputInfo(const WindowInfo& info) { mDrawingState.inputInfo = info; - mDrawingState.touchableRegionCrop = extractLayerFromBinder(info.touchableRegionCropHandle); + mDrawingState.touchableRegionCrop = fromHandle(info.touchableRegionCropHandle.promote()); mDrawingState.modified = true; mFlinger->mInputInfoChanged = true; setTransactionFlags(eTransactionNeeded); @@ -2571,6 +2552,23 @@ void Layer::setClonedChild(const sp<Layer>& clonedChild) { mFlinger->mNumClones++; } +const String16 Layer::Handle::kDescriptor = String16("android.Layer.Handle"); + +wp<Layer> Layer::fromHandle(const sp<IBinder>& handleBinder) { + if (handleBinder == nullptr) { + return nullptr; + } + + BBinder* b = handleBinder->localBinder(); + if (b == nullptr || b->getInterfaceDescriptor() != Handle::kDescriptor) { + return nullptr; + } + + // We can safely cast this binder since its local and we verified its interface descriptor. + sp<Handle> handle = static_cast<Handle*>(handleBinder.get()); + return handle->owner; +} + // --------------------------------------------------------------------------- std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) { diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index d9ab0a6026..3ed7ee9093 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -289,16 +289,17 @@ public: class LayerCleaner { sp<SurfaceFlinger> mFlinger; sp<Layer> mLayer; + BBinder* mHandle; protected: ~LayerCleaner() { // destroy client resources - mFlinger->onHandleDestroyed(mLayer); + mFlinger->onHandleDestroyed(mHandle, mLayer); } public: - LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer) - : mFlinger(flinger), mLayer(layer) {} + LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer, BBinder* handle) + : mFlinger(flinger), mLayer(layer), mHandle(handle) {} }; /* @@ -312,11 +313,15 @@ public: class Handle : public BBinder, public LayerCleaner { public: Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer) - : LayerCleaner(flinger, layer), owner(layer) {} + : LayerCleaner(flinger, layer, this), owner(layer) {} + const String16& getInterfaceDescriptor() const override { return kDescriptor; } + static const String16 kDescriptor; wp<Layer> owner; }; + static wp<Layer> fromHandle(const sp<IBinder>& handle); + explicit Layer(const LayerCreationArgs& args); virtual ~Layer(); diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index 8704c88a75..2adfe14ef7 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -25,6 +25,10 @@ #include "Client.h" #include "Layer.h" +#include <SkBlendMode.h> +#include <SkPaint.h> +#include <SkRect.h> +#include <SkSurface.h> #include <gui/IProducerListener.h> #undef LOG_TAG @@ -32,85 +36,64 @@ namespace android { -void RefreshRateOverlay::SevenSegmentDrawer::drawRect(const Rect& r, const half4& color, - const sp<GraphicBuffer>& buffer, - uint8_t* pixels) { - for (int32_t j = r.top; j < r.bottom; j++) { - if (j >= buffer->getHeight()) { - break; - } - - for (int32_t i = r.left; i < r.right; i++) { - if (i >= buffer->getWidth()) { - break; - } - - uint8_t* iter = pixels + 4 * (i + (buffer->getStride() * j)); - iter[0] = uint8_t(color.r * 255); - iter[1] = uint8_t(color.g * 255); - iter[2] = uint8_t(color.b * 255); - iter[3] = uint8_t(color.a * 255); - } - } -} - -void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, - const half4& color, - const sp<GraphicBuffer>& buffer, - uint8_t* pixels) { - const Rect rect = [&]() { +void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, SkColor& color, + SkCanvas& canvas) { + const SkRect rect = [&]() { switch (segment) { case Segment::Upper: - return Rect(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE); + return SkRect::MakeLTRB(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE); case Segment::UpperLeft: - return Rect(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2); + return SkRect::MakeLTRB(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2); case Segment::UpperRight: - return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH, - DIGIT_HEIGHT / 2); + return SkRect::MakeLTRB(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH, + DIGIT_HEIGHT / 2); case Segment::Middle: - return Rect(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2, left + DIGIT_WIDTH, - DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2); + return SkRect::MakeLTRB(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2, + left + DIGIT_WIDTH, DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2); case Segment::LowerLeft: - return Rect(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT); + return SkRect::MakeLTRB(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT); case Segment::LowerRight: - return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, DIGIT_HEIGHT / 2, left + DIGIT_WIDTH, - DIGIT_HEIGHT); - case Segment::Buttom: - return Rect(left, DIGIT_HEIGHT - DIGIT_SPACE, left + DIGIT_WIDTH, DIGIT_HEIGHT); + return SkRect::MakeLTRB(left + DIGIT_WIDTH - DIGIT_SPACE, DIGIT_HEIGHT / 2, + left + DIGIT_WIDTH, DIGIT_HEIGHT); + case Segment::Bottom: + return SkRect::MakeLTRB(left, DIGIT_HEIGHT - DIGIT_SPACE, left + DIGIT_WIDTH, + DIGIT_HEIGHT); } }(); - drawRect(rect, color, buffer, pixels); + SkPaint paint; + paint.setColor(color); + paint.setBlendMode(SkBlendMode::kSrc); + canvas.drawRect(rect, paint); } -void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, const half4& color, - const sp<GraphicBuffer>& buffer, - uint8_t* pixels) { +void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, SkColor& color, + SkCanvas& canvas) { if (digit < 0 || digit > 9) return; if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 || digit == 8 || digit == 9) - drawSegment(Segment::Upper, left, color, buffer, pixels); + drawSegment(Segment::Upper, left, color, canvas); if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9) - drawSegment(Segment::UpperLeft, left, color, buffer, pixels); + drawSegment(Segment::UpperLeft, left, color, canvas); if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 || digit == 8 || digit == 9) - drawSegment(Segment::UpperRight, left, color, buffer, pixels); + drawSegment(Segment::UpperRight, left, color, canvas); if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9) - drawSegment(Segment::Middle, left, color, buffer, pixels); + drawSegment(Segment::Middle, left, color, canvas); if (digit == 0 || digit == 2 || digit == 6 || digit == 8) - drawSegment(Segment::LowerLeft, left, color, buffer, pixels); + drawSegment(Segment::LowerLeft, left, color, canvas); if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 7 || digit == 8 || digit == 9) - drawSegment(Segment::LowerRight, left, color, buffer, pixels); + drawSegment(Segment::LowerRight, left, color, canvas); if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 || digit == 9) - drawSegment(Segment::Buttom, left, color, buffer, pixels); + drawSegment(Segment::Bottom, left, color, canvas); } -std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::drawNumber( - int number, const half4& color, bool showSpinner) { +std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::draw( + int number, SkColor& color, ui::Transform::RotationFlags rotation, bool showSpinner) { if (number < 0 || number > 1000) return {}; const auto hundreds = number / 100; @@ -120,55 +103,76 @@ std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::drawNumbe std::vector<sp<GraphicBuffer>> buffers; const auto loopCount = showSpinner ? 6 : 1; for (int i = 0; i < loopCount; i++) { + // Pre-rotate the buffer before it reaches SurfaceFlinger. + SkMatrix canvasTransform = SkMatrix(); + auto [bufferWidth, bufferHeight] = [&] { + switch (rotation) { + case ui::Transform::ROT_90: + canvasTransform.setTranslate(BUFFER_HEIGHT, 0); + canvasTransform.preRotate(90); + return std::make_tuple(BUFFER_HEIGHT, BUFFER_WIDTH); + case ui::Transform::ROT_270: + canvasTransform.setRotate(270, BUFFER_WIDTH / 2.0, BUFFER_WIDTH / 2.0); + return std::make_tuple(BUFFER_HEIGHT, BUFFER_WIDTH); + default: + return std::make_tuple(BUFFER_WIDTH, BUFFER_HEIGHT); + } + }(); sp<GraphicBuffer> buffer = - new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1, + new GraphicBuffer(bufferWidth, bufferHeight, HAL_PIXEL_FORMAT_RGBA_8888, 1, GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE, "RefreshRateOverlayBuffer"); const status_t bufferStatus = buffer->initCheck(); LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "RefreshRateOverlay: Buffer failed to allocate: %d", bufferStatus); - uint8_t* pixels; - buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels)); - // Clear buffer content - drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels); + + sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(bufferWidth, bufferHeight); + SkCanvas* canvas = surface->getCanvas(); + canvas->setMatrix(canvasTransform); + int left = 0; if (hundreds != 0) { - drawDigit(hundreds, left, color, buffer, pixels); + drawDigit(hundreds, left, color, *canvas); } left += DIGIT_WIDTH + DIGIT_SPACE; if (tens != 0) { - drawDigit(tens, left, color, buffer, pixels); + drawDigit(tens, left, color, *canvas); } left += DIGIT_WIDTH + DIGIT_SPACE; - drawDigit(ones, left, color, buffer, pixels); + drawDigit(ones, left, color, *canvas); left += DIGIT_WIDTH + DIGIT_SPACE; if (showSpinner) { switch (i) { case 0: - drawSegment(Segment::Upper, left, color, buffer, pixels); + drawSegment(Segment::Upper, left, color, *canvas); break; case 1: - drawSegment(Segment::UpperRight, left, color, buffer, pixels); + drawSegment(Segment::UpperRight, left, color, *canvas); break; case 2: - drawSegment(Segment::LowerRight, left, color, buffer, pixels); + drawSegment(Segment::LowerRight, left, color, *canvas); break; case 3: - drawSegment(Segment::Buttom, left, color, buffer, pixels); + drawSegment(Segment::Bottom, left, color, *canvas); break; case 4: - drawSegment(Segment::LowerLeft, left, color, buffer, pixels); + drawSegment(Segment::LowerLeft, left, color, *canvas); break; case 5: - drawSegment(Segment::UpperLeft, left, color, buffer, pixels); + drawSegment(Segment::UpperLeft, left, color, *canvas); break; } } + void* pixels = nullptr; + buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels)); + const SkImageInfo& imageInfo = surface->imageInfo(); + size_t dstRowBytes = buffer->getStride() * imageInfo.bytesPerPixel(); + canvas->readPixels(imageInfo, pixels, dstRowBytes, 0, 0); buffer->unlock(); buffers.emplace_back(buffer); } @@ -214,7 +218,22 @@ bool RefreshRateOverlay::createLayer() { const std::vector<std::shared_ptr<renderengine::ExternalTexture>>& RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) { - if (mBufferCache.find(fps) == mBufferCache.end()) { + ui::Transform::RotationFlags transformHint = mLayer->getTransformHint(); + // Tell SurfaceFlinger about the pre-rotation on the buffer. + const auto transform = [&] { + switch (transformHint) { + case ui::Transform::ROT_90: + return ui::Transform::ROT_270; + case ui::Transform::ROT_270: + return ui::Transform::ROT_90; + default: + return ui::Transform::ROT_0; + } + }(); + mLayer->setTransform(transform); + + if (mBufferCache.find(transformHint) == mBufferCache.end() || + mBufferCache.at(transformHint).find(fps) == mBufferCache.at(transformHint).end()) { // Ensure the range is > 0, so we don't divide by 0. const auto rangeLength = std::max(1u, mHighFps - mLowFps); // Clip values outside the range [mLowFps, mHighFps]. The current fps may be outside @@ -222,12 +241,14 @@ RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) { fps = std::max(fps, mLowFps); fps = std::min(fps, mHighFps); const auto fpsScale = static_cast<float>(fps - mLowFps) / rangeLength; - half4 color; - color.r = HIGH_FPS_COLOR.r * fpsScale + LOW_FPS_COLOR.r * (1 - fpsScale); - color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale); - color.b = HIGH_FPS_COLOR.b * fpsScale + LOW_FPS_COLOR.b * (1 - fpsScale); - color.a = ALPHA; - auto buffers = SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner); + SkColor4f colorBase = SkColor4f::FromColor(HIGH_FPS_COLOR) * fpsScale; + SkColor4f lowFpsColor = SkColor4f::FromColor(LOW_FPS_COLOR) * (1 - fpsScale); + colorBase.fR = colorBase.fR + lowFpsColor.fR; + colorBase.fG = colorBase.fG + lowFpsColor.fG; + colorBase.fB = colorBase.fB + lowFpsColor.fB; + colorBase.fA = ALPHA; + SkColor color = colorBase.toSkColor(); + auto buffers = SevenSegmentDrawer::draw(fps, color, transformHint, mShowSpinner); std::vector<std::shared_ptr<renderengine::ExternalTexture>> textures; std::transform(buffers.begin(), buffers.end(), std::back_inserter(textures), [&](const auto& buffer) -> std::shared_ptr<renderengine::ExternalTexture> { @@ -237,10 +258,10 @@ RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) { renderengine::ExternalTexture:: Usage::READABLE); }); - mBufferCache.emplace(fps, textures); + mBufferCache[transformHint].emplace(fps, textures); } - return mBufferCache[fps]; + return mBufferCache[transformHint][fps]; } void RefreshRateOverlay::setViewport(ui::Size viewport) { diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h index f9baa898dc..fd1e2df96d 100644 --- a/services/surfaceflinger/RefreshRateOverlay.h +++ b/services/surfaceflinger/RefreshRateOverlay.h @@ -22,6 +22,8 @@ #include <ui/Size.h> #include <utils/StrongPointer.h> +#include <SkCanvas.h> +#include <SkColor.h> #include <unordered_map> #include "Fps.h" @@ -47,20 +49,16 @@ public: private: class SevenSegmentDrawer { public: - static std::vector<sp<GraphicBuffer>> drawNumber(int number, const half4& color, - bool showSpinner); + static std::vector<sp<GraphicBuffer>> draw(int number, SkColor& color, + ui::Transform::RotationFlags, bool showSpinner); static uint32_t getHeight() { return BUFFER_HEIGHT; } static uint32_t getWidth() { return BUFFER_WIDTH; } private: - enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Buttom }; + enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Bottom }; - static void drawRect(const Rect& r, const half4& color, const sp<GraphicBuffer>& buffer, - uint8_t* pixels); - static void drawSegment(Segment segment, int left, const half4& color, - const sp<GraphicBuffer>& buffer, uint8_t* pixels); - static void drawDigit(int digit, int left, const half4& color, - const sp<GraphicBuffer>& buffer, uint8_t* pixels); + static void drawSegment(Segment segment, int left, SkColor& color, SkCanvas& canvas); + static void drawDigit(int digit, int left, SkColor& color, SkCanvas& canvas); static constexpr uint32_t DIGIT_HEIGHT = 100; static constexpr uint32_t DIGIT_WIDTH = 64; @@ -80,13 +78,15 @@ private: sp<IBinder> mIBinder; sp<IGraphicBufferProducer> mGbp; - std::unordered_map<int, std::vector<std::shared_ptr<renderengine::ExternalTexture>>> + std::unordered_map< + ui::Transform::RotationFlags, + std::unordered_map<int, std::vector<std::shared_ptr<renderengine::ExternalTexture>>>> mBufferCache; std::optional<int> mCurrentFps; int mFrame = 0; static constexpr float ALPHA = 0.8f; - const half3 LOW_FPS_COLOR = half3(1.0f, 0.0f, 0.0f); - const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f); + const SkColor LOW_FPS_COLOR = SK_ColorRED; + const SkColor HIGH_FPS_COLOR = SK_ColorGREEN; const bool mShowSpinner; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index a2e7ccb465..967f028ac1 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -3577,7 +3577,7 @@ bool SurfaceFlinger::transactionIsReadyToBeApplied( sp<Layer> layer = nullptr; if (s.surface) { - layer = fromHandleLocked(s.surface).promote(); + layer = fromHandle(s.surface).promote(); } else if (s.hasBufferChanges()) { ALOGW("Transaction with buffer, but no Layer?"); continue; @@ -3744,7 +3744,7 @@ void SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin setClientStateLocked(frameTimelineInfo, state, desiredPresentTime, isAutoTimestamp, postTime, permissions, listenerCallbacksWithSurfaces); if ((flags & eAnimation) && state.state.surface) { - if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) { + if (const auto layer = fromHandle(state.state.surface).promote(); layer) { mScheduler->recordLayerHistory(layer.get(), isAutoTimestamp ? 0 : desiredPresentTime, LayerHistory::LayerUpdateType::AnimationTX); @@ -3905,13 +3905,11 @@ uint32_t SurfaceFlinger::setClientStateLocked( if (what & layer_state_t::eLayerCreated) { layer = handleLayerCreatedLocked(s.surface); if (layer) { - // put the created layer into mLayersByLocalBinderToken. - mLayersByLocalBinderToken.emplace(s.surface->localBinder(), layer); flags |= eTransactionNeeded | eTraversalNeeded; mLayersAdded = true; } } else { - layer = fromHandleLocked(s.surface).promote(); + layer = fromHandle(s.surface).promote(); } } else { // The client may provide us a null handle. Treat it as if the layer was removed. @@ -4252,7 +4250,7 @@ status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder> { Mutex::Autolock _l(mStateLock); - mirrorFrom = fromHandleLocked(mirrorFromHandle).promote(); + mirrorFrom = fromHandle(mirrorFromHandle).promote(); if (!mirrorFrom) { return NAME_NOT_FOUND; } @@ -4450,7 +4448,7 @@ void SurfaceFlinger::markLayerPendingRemovalLocked(const sp<Layer>& layer) { setTransactionFlags(eTransactionNeeded); } -void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer) { +void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer) { Mutex::Autolock lock(mStateLock); // If a layer has a parent, we allow it to out-live it's handle // with the idea that the parent holds a reference and will eventually @@ -4461,17 +4459,7 @@ void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer) { mCurrentState.layersSortedByZ.remove(layer); } markLayerPendingRemovalLocked(layer); - - auto it = mLayersByLocalBinderToken.begin(); - while (it != mLayersByLocalBinderToken.end()) { - if (it->second == layer) { - mBufferCountTracker.remove(it->first->localBinder()); - it = mLayersByLocalBinderToken.erase(it); - } else { - it++; - } - } - + mBufferCountTracker.remove(handle); layer.clear(); } @@ -5689,37 +5677,24 @@ 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; - } - return std::make_optional<PhysicalDisplayId>(inputDisplayId); + const auto display = [&]() -> sp<IBinder> { + uint64_t value; + if (data.readUint64(&value) != NO_ERROR) { + return getDefaultDisplayDevice()->getDisplayToken().promote(); } - return getDefaultDisplayDevice()->getPhysicalId(); - }(); - - if (!displayId) { - ALOGE("No display found"); - return NO_ERROR; - } - - status_t result = setActiveMode(getPhysicalDisplayToken(*displayId), modeId); - if (result != NO_ERROR) { - return result; - } + if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(value)) { + return getPhysicalDisplayToken(*id); + } - mDebugDisplayModeSetByBackdoor = true; + ALOGE("Invalid physical display ID"); + return nullptr; + }(); - return NO_ERROR; + const status_t result = setActiveMode(display, modeId); + mDebugDisplayModeSetByBackdoor = result == NO_ERROR; + return result; } case 1036: { if (data.readInt32() > 0) { @@ -5782,14 +5757,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); @@ -6047,7 +6019,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; @@ -6055,21 +6027,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 = @@ -6114,7 +6079,7 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, { Mutex::Autolock lock(mStateLock); - parent = fromHandleLocked(args.layerHandle).promote(); + parent = fromHandle(args.layerHandle).promote(); if (parent == nullptr || parent->isRemovedFromCurrentState()) { ALOGE("captureLayers called with an invalid or removed parent"); return NAME_NOT_FOUND; @@ -6145,7 +6110,7 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY); for (const auto& handle : args.excludeHandles) { - sp<Layer> excludeLayer = fromHandleLocked(handle).promote(); + sp<Layer> excludeLayer = fromHandle(handle).promote(); if (excludeLayer != nullptr) { excludeLayers.emplace(excludeLayer); } else { @@ -6154,7 +6119,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; } @@ -6369,7 +6338,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; @@ -6381,7 +6349,6 @@ status_t SurfaceFlinger::renderScreenImplLocked( renderArea.needsFiltering(), renderArea.isSecure(), useProtected, - clearRegion, layerStackSpaceRect, clientCompositionDisplay.outputDataspace, true, /* realContentIsVisible */ @@ -6419,7 +6386,6 @@ 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; @@ -6615,24 +6581,8 @@ status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayTo return NO_ERROR; } -wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) { - Mutex::Autolock _l(mStateLock); - return fromHandleLocked(handle); -} - -wp<Layer> SurfaceFlinger::fromHandleLocked(const sp<IBinder>& handle) const { - BBinder* b = nullptr; - if (handle) { - b = handle->localBinder(); - } - if (b == nullptr) { - return nullptr; - } - auto it = mLayersByLocalBinderToken.find(b); - if (it != mLayersByLocalBinderToken.end()) { - return it->second; - } - return nullptr; +wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) const { + return Layer::fromHandle(handle); } void SurfaceFlinger::onLayerFirstRef(Layer* layer) { @@ -6938,7 +6888,7 @@ sp<Layer> SurfaceFlinger::handleLayerCreatedLocked(const sp<IBinder>& handle) { sp<Layer> parent; bool allowAddRoot = state->addToRoot; if (state->initialParent != nullptr) { - parent = fromHandleLocked(state->initialParent.promote()).promote(); + parent = fromHandle(state->initialParent.promote()).promote(); if (parent == nullptr) { ALOGE("Invalid parent %p", state->initialParent.unsafe_get()); allowAddRoot = false; diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index b2e5e7dc93..00590ddd2e 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -328,8 +328,7 @@ public: // Returns nullptr if the handle does not point to an existing layer. // Otherwise, returns a weak reference so that callers off the main-thread // won't accidentally hold onto the last strong reference. - wp<Layer> fromHandle(const sp<IBinder>& handle); - wp<Layer> fromHandleLocked(const sp<IBinder>& handle) const REQUIRES(mStateLock); + wp<Layer> fromHandle(const sp<IBinder>& handle) const; // If set, disables reusing client composition buffers. This can be set by // debug.sf.disable_client_composition_cache @@ -637,12 +636,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*) @@ -909,7 +906,7 @@ private: // called when all clients have released all their references to // this layer meaning it is entirely safe to destroy all // resources associated to this layer. - void onHandleDestroyed(sp<Layer>& layer); + void onHandleDestroyed(BBinder* handle, sp<Layer>& layer); void markLayerPendingRemovalLocked(const sp<Layer>& layer); // add a layer to SurfaceFlinger @@ -1314,8 +1311,6 @@ private: std::optional<DisplayIdGenerator<HalVirtualDisplayId>> hal; } mVirtualDisplayIdGenerators; - std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken GUARDED_BY(mStateLock); - // don't use a lock for these, we don't care int mDebugRegion = 0; bool mDebugDisableHWC = false; diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp index 1f08a1db8b..9be3abefab 100644 --- a/services/surfaceflinger/SurfaceInterceptor.cpp +++ b/services/surfaceflinger/SurfaceInterceptor.cpp @@ -184,12 +184,9 @@ status_t SurfaceInterceptor::writeProtoFileLocked() { return NO_ERROR; } -const sp<const Layer> SurfaceInterceptor::getLayer(const wp<const IBinder>& weakHandle) const { - const sp<const IBinder>& handle(weakHandle.promote()); - const auto layerHandle(static_cast<const Layer::Handle*>(handle.get())); - const sp<const Layer> layer(layerHandle->owner.promote()); - // layer could be a nullptr at this point - return layer; +const sp<const Layer> SurfaceInterceptor::getLayer(const wp<IBinder>& weakHandle) const { + sp<IBinder> handle = weakHandle.promote(); + return Layer::fromHandle(handle).promote(); } int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) const { @@ -204,12 +201,11 @@ int32_t SurfaceInterceptor::getLayerIdFromWeakRef(const wp<const Layer>& layer) return strongLayer == nullptr ? -1 : getLayerId(strongLayer); } -int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<const IBinder>& handle) const { +int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<IBinder>& handle) const { if (handle == nullptr) { return -1; } - const auto layerHandle(static_cast<const Layer::Handle*>(handle.get())); - const sp<const Layer> layer(layerHandle->owner.promote()); + const sp<const Layer> layer = Layer::fromHandle(handle).promote(); return layer == nullptr ? -1 : getLayerId(layer); } diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h index cc7e26e4d1..9a3ce2c58d 100644 --- a/services/surfaceflinger/SurfaceInterceptor.h +++ b/services/surfaceflinger/SurfaceInterceptor.h @@ -133,10 +133,10 @@ private: void addInitialDisplayStateLocked(Increment* increment, const DisplayDeviceState& display); status_t writeProtoFileLocked(); - const sp<const Layer> getLayer(const wp<const IBinder>& weakHandle) const; + const sp<const Layer> getLayer(const wp<IBinder>& weakHandle) const; int32_t getLayerId(const sp<const Layer>& layer) const; int32_t getLayerIdFromWeakRef(const wp<const Layer>& layer) const; - int32_t getLayerIdFromHandle(const sp<const IBinder>& weakHandle) const; + int32_t getLayerIdFromHandle(const sp<IBinder>& weakHandle) const; Increment* createTraceIncrementLocked(); void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer); diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp index 162711d6f5..a9e935df22 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()) { diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 9e704c32fc..8bd6e62278 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 diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 52a36a2719..98c18890cf 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -77,7 +77,7 @@ 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; diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index cc24323c98..60b0f5334c 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -122,7 +122,7 @@ void DisplayTransactionTest::injectFakeNativeWindowSurfaceFactory() { sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay( std::function<void(FakeDisplayDeviceInjector&)> injectExtra) { - constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(777); + constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(255u); constexpr int DEFAULT_DISPLAY_WIDTH = 1080; constexpr int DEFAULT_DISPLAY_HEIGHT = 1920; constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0; 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/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 6870fd45fb..c04919ff04 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -181,7 +181,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) 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/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 40c881e902..56ae414984 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()); @@ -645,7 +645,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) 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/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. |