summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Michael Wright <michaelwr@google.com> 2020-07-21 00:46:45 +0100
committer Michael Wright <michaelwr@google.com> 2020-07-22 17:20:03 +0100
commit8759d67465ed463db24e313597c16fe04a1ded55 (patch)
tree1428593f6960207d285e0f6b1a4e48f233465ab0
parent7aab8a09d97e403b396622e96e3bc3a5b0ba9403 (diff)
Automatically generate flag value strings for flag enums.
Since there can generally only be a maximum of 64 flags for a given flage type, it isn't that expensive to generate the array of names at compile time by enumerating the possible flag values and then using a templated function's debug signature to extract the name. The debug signature should be relatively stable, and I've confirmed this works on both GCC as well as clang. If our parsing fails, however, we should just fallback to bare hex values again. Our tests should hopefully prevent this from happening for any extended period of time. Bug: 160010896 Test: atest libinput_tests Change-Id: I752100bbefb92e7a0ecf7a8473a47e37ff7b1662
-rw-r--r--include/input/Flags.h99
-rw-r--r--include/input/InputWindow.h2
-rw-r--r--libs/input/InputWindow.cpp103
-rw-r--r--libs/input/tests/Flags_test.cpp60
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.cpp3
5 files changed, 109 insertions, 158 deletions
diff --git a/include/input/Flags.h b/include/input/Flags.h
index 0f52e18719..f43829f62c 100644
--- a/include/input/Flags.h
+++ b/include/input/Flags.h
@@ -16,6 +16,7 @@
#include <android-base/stringprintf.h>
+#include <array>
#include <cstdint>
#include <optional>
#include <string>
@@ -28,6 +29,69 @@
namespace android {
+namespace details {
+template <typename F, F V>
+constexpr std::optional<std::string_view> enum_value_name() {
+ // Should look something like (but all on one line):
+ // std::optional<std::string_view>
+ // android::details::enum_value_name()
+ // [F = android::test::TestFlags, V = android::test::TestFlags::ONE]
+ std::string_view view = __PRETTY_FUNCTION__;
+ size_t templateStart = view.rfind("[");
+ size_t templateEnd = view.rfind("]");
+ if (templateStart == std::string::npos || templateEnd == std::string::npos) {
+ return std::nullopt;
+ }
+
+ // Extract the template parameters without the enclosing braces.
+ // Example (cont'd): F = android::test::TestFlags, V = android::test::TestFlags::ONE
+ view = view.substr(templateStart + 1, templateEnd - templateStart - 1);
+ size_t valStart = view.rfind("V = ");
+ if (valStart == std::string::npos) {
+ return std::nullopt;
+ }
+
+ // Example (cont'd): V = android::test::TestFlags::ONE
+ view = view.substr(valStart);
+ size_t nameStart = view.rfind("::");
+ if (nameStart == std::string::npos) {
+ return std::nullopt;
+ }
+
+ // Chop off the initial "::"
+ nameStart += 2;
+ return view.substr(nameStart);
+}
+
+template <typename F>
+inline constexpr auto flag_count = sizeof(F) * __CHAR_BIT__;
+
+template <typename F, typename T, T... I>
+constexpr auto generate_flag_values(std::integer_sequence<T, I...> seq) {
+ constexpr int count = seq.size();
+
+ std::array<F, count> values{};
+ for (int i = 0, v = 0; v < count; ++i) {
+ values[v++] = static_cast<F>(T{1} << i);
+ }
+
+ return values;
+}
+
+template <typename F>
+inline constexpr auto flag_values = generate_flag_values<F>(
+ std::make_integer_sequence<std::underlying_type_t<F>, flag_count<F>>{});
+
+template <typename F, std::size_t... I>
+constexpr auto generate_flag_names(std::index_sequence<I...>) noexcept {
+ return std::array<std::optional<std::string_view>, sizeof...(I)>{
+ {enum_value_name<F, flag_values<F>[I]>()...}};
+}
+
+template <typename F>
+inline constexpr auto flag_names =
+ generate_flag_names<F>(std::make_index_sequence<flag_count<F>>{});
+
// A trait for determining whether a type is specifically an enum class or not.
template <typename T, bool = std::is_enum_v<T>>
struct is_enum_class : std::false_type {};
@@ -40,13 +104,28 @@ struct is_enum_class<T, true>
template <typename T>
inline constexpr bool is_enum_class_v = is_enum_class<T>::value;
+} // namespace details
+
+template <auto V>
+constexpr auto flag_name() {
+ using F = decltype(V);
+ return details::enum_value_name<F, V>();
+}
+
+template <typename F>
+constexpr std::optional<std::string_view> flag_name(F flag) {
+ using U = std::underlying_type_t<F>;
+ auto idx = __builtin_ctzl(static_cast<U>(flag));
+ return details::flag_names<F>[idx];
+}
/* A class for handling flags defined by an enum or enum class in a type-safe way. */
-template <class F, typename = std::enable_if_t<std::is_enum_v<F>>>
+template <typename F>
class Flags {
// F must be an enum or its underlying type is undefined. Theoretically we could specialize this
// further to avoid this restriction but in general we want to encourage the use of enums
// anyways.
+ static_assert(std::is_enum_v<F>, "Flags type must be an enum");
using U = typename std::underlying_type_t<F>;
public:
@@ -59,10 +138,11 @@ public:
// should force them to be explicitly constructed from their underlying types to make full use
// of the type checker.
template <typename T = U>
- constexpr Flags(T t, typename std::enable_if_t<!is_enum_class_v<F>, T>* = nullptr)
+ constexpr Flags(T t, typename std::enable_if_t<!details::is_enum_class_v<F>, T>* = nullptr)
: mFlags(t) {}
template <typename T = U>
- explicit constexpr Flags(T t, typename std::enable_if_t<is_enum_class_v<F>, T>* = nullptr)
+ explicit constexpr Flags(T t,
+ typename std::enable_if_t<details::is_enum_class_v<F>, T>* = nullptr)
: mFlags(t) {}
class Iterator {
@@ -176,14 +256,12 @@ public:
*/
U get() const { return mFlags; }
- std::string string() const { return string(defaultStringify); }
-
- std::string string(std::function<std::optional<std::string>(F)> stringify) const {
+ std::string string() const {
std::string result;
bool first = true;
U unstringified = 0;
for (const F f : *this) {
- std::optional<std::string> flagString = stringify(f);
+ std::optional<std::string_view> flagString = flag_name(f);
if (flagString) {
appendFlag(result, flagString.value(), first);
} else {
@@ -205,8 +283,7 @@ public:
private:
U mFlags;
- static std::optional<std::string> defaultStringify(F) { return std::nullopt; }
- static void appendFlag(std::string& str, const std::string& flag, bool& first) {
+ static void appendFlag(std::string& str, const std::string_view& flag, bool& first) {
if (first) {
first = false;
} else {
@@ -220,12 +297,12 @@ private:
// as flags. In order to use these, add them via a `using namespace` declaration.
namespace flag_operators {
-template <typename F, typename = std::enable_if_t<is_enum_class_v<F>>>
+template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>
inline Flags<F> operator~(F f) {
using U = typename std::underlying_type_t<F>;
return static_cast<F>(~static_cast<U>(f));
}
-template <typename F, typename = std::enable_if_t<is_enum_class_v<F>>>
+template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>
Flags<F> operator|(F lhs, F rhs) {
using U = typename std::underlying_type_t<F>;
return static_cast<F>(static_cast<U>(lhs) | static_cast<U>(rhs));
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 233c7aea4f..268a3b8ccf 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -205,8 +205,6 @@ struct InputWindowInfo : public Parcelable {
status_t writeToParcel(android::Parcel* parcel) const override;
status_t readFromParcel(const android::Parcel* parcel) override;
-
- static std::optional<std::string> flagToString(Flag f);
};
/*
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index 36c1f8068d..4a8e2722e8 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -198,107 +198,4 @@ sp<IBinder> InputWindowHandle::getToken() const {
void InputWindowHandle::updateFrom(sp<InputWindowHandle> handle) {
mInfo = handle->mInfo;
}
-
-std::optional<std::string> InputWindowInfo::flagToString(Flag flag) {
- switch (flag) {
- case InputWindowInfo::Flag::ALLOW_LOCK_WHILE_SCREEN_ON: {
- return "ALLOW_LOCK_WHILE_SCREEN_ON";
- }
- case InputWindowInfo::Flag::DIM_BEHIND: {
- return "DIM_BEHIND";
- }
- case InputWindowInfo::Flag::BLUR_BEHIND: {
- return "BLUR_BEHIND";
- }
- case InputWindowInfo::Flag::NOT_FOCUSABLE: {
- return "NOT_FOCUSABLE";
- }
- case InputWindowInfo::Flag::NOT_TOUCHABLE: {
- return "NOT_TOUCHABLE";
- }
- case InputWindowInfo::Flag::NOT_TOUCH_MODAL: {
- return "NOT_TOUCH_MODAL";
- }
- case InputWindowInfo::Flag::TOUCHABLE_WHEN_WAKING: {
- return "TOUCHABLE_WHEN_WAKING";
- }
- case InputWindowInfo::Flag::KEEP_SCREEN_ON: {
- return "KEEP_SCREEN_ON";
- }
- case InputWindowInfo::Flag::LAYOUT_IN_SCREEN: {
- return "LAYOUT_IN_SCREEN";
- }
- case InputWindowInfo::Flag::LAYOUT_NO_LIMITS: {
- return "LAYOUT_NO_LIMITS";
- }
- case InputWindowInfo::Flag::FULLSCREEN: {
- return "FULLSCREEN";
- }
- case InputWindowInfo::Flag::FORCE_NOT_FULLSCREEN: {
- return "FORCE_NOT_FULLSCREEN";
- }
- case InputWindowInfo::Flag::DITHER: {
- return "DITHER";
- }
- case InputWindowInfo::Flag::SECURE: {
- return "SECURE";
- }
- case InputWindowInfo::Flag::SCALED: {
- return "SCALED";
- }
- case InputWindowInfo::Flag::IGNORE_CHEEK_PRESSES: {
- return "IGNORE_CHEEK_PRESSES";
- }
- case InputWindowInfo::Flag::LAYOUT_INSET_DECOR: {
- return "LAYOUT_INSET_DECOR";
- }
- case InputWindowInfo::Flag::ALT_FOCUSABLE_IM: {
- return "ALT_FOCUSABLE_IM";
- }
- case InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH: {
- return "WATCH_OUTSIDE_TOUCH";
- }
- case InputWindowInfo::Flag::SHOW_WHEN_LOCKED: {
- return "SHOW_WHEN_LOCKED";
- }
- case InputWindowInfo::Flag::SHOW_WALLPAPER: {
- return "SHOW_WALLPAPER";
- }
- case InputWindowInfo::Flag::TURN_SCREEN_ON: {
- return "TURN_SCREEN_ON";
- }
- case InputWindowInfo::Flag::DISMISS_KEYGUARD: {
- return "DISMISS_KEYGUARD";
- }
- case InputWindowInfo::Flag::SPLIT_TOUCH: {
- return "SPLIT_TOUCH";
- }
- case InputWindowInfo::Flag::HARDWARE_ACCELERATED: {
- return "HARDWARE_ACCELERATED";
- }
- case InputWindowInfo::Flag::LAYOUT_IN_OVERSCAN: {
- return "LAYOUT_IN_OVERSCAN";
- }
- case InputWindowInfo::Flag::TRANSLUCENT_STATUS: {
- return "TRANSLUCENT_STATUS";
- }
- case InputWindowInfo::Flag::TRANSLUCENT_NAVIGATION: {
- return "TRANSLUCENT_NAVIGATION";
- }
- case InputWindowInfo::Flag::LOCAL_FOCUS_MODE: {
- return "LOCAL_FOCUS_MODE";
- }
- case InputWindowInfo::Flag::SLIPPERY: {
- return "SLIPPERY";
- }
- case InputWindowInfo::Flag::LAYOUT_ATTACHED_IN_DECOR: {
- return "LAYOUT_ATTACHED_IN_DECOR";
- }
- case InputWindowInfo::Flag::DRAWS_SYSTEM_BAR_BACKGROUNDS: {
- return "DRAWS_SYSTEM_BAR_BACKGROUNDS";
- }
- }
- return std::nullopt;
-}
-
} // namespace android
diff --git a/libs/input/tests/Flags_test.cpp b/libs/input/tests/Flags_test.cpp
index b979f37b86..0dbb4cfe32 100644
--- a/libs/input/tests/Flags_test.cpp
+++ b/libs/input/tests/Flags_test.cpp
@@ -25,30 +25,6 @@ using namespace android::flag_operators;
enum class TestFlags { ONE = 0x1, TWO = 0x2, THREE = 0x4 };
-static std::optional<std::string> toStringComplete(TestFlags f) {
- switch (f) {
- case TestFlags::ONE:
- return "ONE";
- case TestFlags::TWO:
- return "TWO";
- case TestFlags::THREE:
- return "THREE";
- }
- return std::nullopt;
-}
-
-static std::optional<std::string> toStringIncomplete(TestFlags f) {
- switch (f) {
- case TestFlags::ONE:
- return "ONE";
- case TestFlags::TWO:
- return "TWO";
- case TestFlags::THREE:
- default:
- return std::nullopt;
- }
-}
-
TEST(Flags, Test) {
Flags<TestFlags> flags = TestFlags::ONE;
ASSERT_TRUE(flags.test(TestFlags::ONE));
@@ -172,29 +148,19 @@ TEST(Flags, EqualsOperator_DontShareState) {
ASSERT_NE(flags1, flags2);
}
-TEST(Flags, String_NoFlagsWithDefaultStringify) {
+TEST(Flags, String_NoFlags) {
Flags<TestFlags> flags;
ASSERT_EQ(flags.string(), "0x0");
}
-TEST(Flags, String_NoFlagsWithNonDefaultStringify) {
- Flags<TestFlags> flags;
- ASSERT_EQ(flags.string(toStringComplete), "0x0");
-}
-
-TEST(Flags, String_WithDefaultStringify) {
+TEST(Flags, String_KnownValues) {
Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
- ASSERT_EQ(flags.string(), "0x00000003");
+ ASSERT_EQ(flags.string(), "ONE | TWO");
}
-TEST(Flags, String_WithCompleteStringify) {
- Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
- ASSERT_EQ(flags.string(toStringComplete), "ONE | TWO");
-}
-
-TEST(Flags, String_WithIncompleteStringify) {
- Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
- ASSERT_EQ(flags.string(toStringIncomplete), "ONE | 0x00000004");
+TEST(Flags, String_UnknownValues) {
+ auto flags = Flags<TestFlags>(0b1011);
+ ASSERT_EQ(flags.string(), "ONE | TWO | 0x00000008");
}
TEST(FlagsIterator, IteratesOverAllFlags) {
@@ -239,4 +205,18 @@ TEST(FlagsIterator, PreFixIncrement) {
ASSERT_EQ(++iter, flags.end());
}
+TEST(FlagNames, RuntimeFlagName) {
+ TestFlags f = TestFlags::ONE;
+ ASSERT_EQ(flag_name(f), "ONE");
+}
+
+TEST(FlagNames, RuntimeUnknownFlagName) {
+ TestFlags f = static_cast<TestFlags>(0x8);
+ ASSERT_EQ(flag_name(f), std::nullopt);
+}
+
+TEST(FlagNames, CompileTimeFlagName) {
+ static_assert(flag_name<TestFlags::TWO>() == "TWO");
+}
+
} // namespace android::test \ No newline at end of file
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index da098984b1..5cdbfa32f2 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -4229,8 +4229,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {
toString(windowInfo->hasWallpaper),
toString(windowInfo->visible),
toString(windowInfo->canReceiveKeys),
- windowInfo->flags.string(InputWindowInfo::flagToString)
- .c_str(),
+ windowInfo->flags.string().c_str(),
static_cast<int32_t>(windowInfo->type),
windowInfo->frameLeft, windowInfo->frameTop,
windowInfo->frameRight, windowInfo->frameBottom,