| /* |
| * 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. |
| */ |
| |
| /** |
| * @addtogroup NdkBinder |
| * @{ |
| */ |
| |
| /** |
| * @file binder_to_string.h |
| * @brief Helper for parcelable. |
| */ |
| |
| #pragma once |
| |
| #include <codecvt> |
| #include <locale> |
| #include <memory> |
| #include <optional> |
| #include <sstream> |
| #include <string> |
| #include <type_traits> |
| |
| #if __has_include(<utils/StrongPointer.h>) |
| #include <utils/StrongPointer.h> |
| #define HAS_STRONG_POINTER |
| #endif |
| |
| #if __has_include(<utils/String16.h>) |
| #include <utils/String16.h> |
| #define HAS_STRING16 |
| #endif |
| |
| #if __has_include(<android/binder_ibinder.h>) |
| #include <android/binder_auto_utils.h> |
| #include <android/binder_interface_utils.h> |
| #include <android/binder_parcelable_utils.h> |
| #define HAS_NDK_INTERFACE |
| #endif |
| |
| // TODO: some things include libbinder without having access to libbase. This is |
| // due to frameworks/native/include, which symlinks to libbinder headers, so even |
| // though we don't use it here, we detect a different header, so that we are more |
| // confident libbase will be included |
| #if __has_include(<binder/RpcSession.h>) |
| #include <binder/IBinder.h> |
| #include <binder/IInterface.h> |
| #define HAS_CPP_INTERFACE |
| #endif |
| |
| namespace android { |
| namespace internal { |
| |
| // ToString is a utility to generate string representation for various AIDL-supported types. |
| template <typename _T> |
| std::string ToString(const _T& t); |
| |
| namespace details { |
| |
| // Truthy if _T has toString() method. |
| template <typename _T> |
| class HasToStringMethod { |
| template <typename _U> |
| static auto _test(int) -> decltype(std::declval<_U>().toString(), std::true_type()); |
| template <typename _U> |
| static std::false_type _test(...); |
| |
| public: |
| enum { value = decltype(_test<_T>(0))::value }; |
| }; |
| |
| // Truthy if _T has a overloaded toString(T) |
| template <typename _T> |
| class HasToStringFunction { |
| template <typename _U> |
| static auto _test(int) -> decltype(toString(std::declval<_U>()), std::true_type()); |
| template <typename _U> |
| static std::false_type _test(...); |
| |
| public: |
| enum { value = decltype(_test<_T>(0))::value }; |
| }; |
| |
| template <typename T, template <typename...> typename U> |
| struct IsInstantiationOf : std::false_type {}; |
| |
| template <template <typename...> typename U, typename... Args> |
| struct IsInstantiationOf<U<Args...>, U> : std::true_type {}; |
| |
| // Truthy if _T is like a pointer: one of sp/optional/shared_ptr |
| template <typename _T> |
| class IsPointerLike { |
| template <typename _U> |
| static std::enable_if_t< |
| #ifdef HAS_STRONG_POINTER |
| IsInstantiationOf<_U, sp>::value || // for IBinder and interface types in the C++ |
| // backend |
| #endif |
| IsInstantiationOf<_U, std::optional>::value || // for @nullable types in the |
| // C++/NDK backends |
| IsInstantiationOf<_U, std::unique_ptr>::value || // for @nullable(heap=true) |
| // in C++/NDK backends |
| IsInstantiationOf<_U, std::shared_ptr>::value, // for interface types in the |
| // NDK backends |
| |
| std::true_type> |
| _test(int); |
| template <typename _U> |
| static std::false_type _test(...); |
| |
| public: |
| enum { value = decltype(_test<_T>(0))::value }; |
| }; |
| |
| // Truthy if _T is like a container |
| template <typename _T> |
| class IsIterable { |
| template <typename _U> |
| static auto _test(int) |
| -> decltype(begin(std::declval<_U>()), end(std::declval<_U>()), std::true_type()); |
| template <typename _U> |
| static std::false_type _test(...); |
| |
| public: |
| enum { value = decltype(_test<_T>(0))::value }; |
| }; |
| |
| template <typename _T> |
| struct TypeDependentFalse { |
| enum { value = false }; |
| }; |
| |
| } // namespace details |
| |
| template <typename _T> |
| std::string ToString(const _T& t) { |
| if constexpr (std::is_same_v<bool, _T>) { |
| return t ? "true" : "false"; |
| } else if constexpr (std::is_same_v<char16_t, _T>) { |
| // TODO(b/244494451): codecvt is deprecated in C++17 -- suppress the |
| // warnings. There's no replacement in the standard library yet. |
| _Pragma("clang diagnostic push") |
| _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\""); |
| return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>().to_bytes(t); |
| _Pragma("clang diagnostic pop"); |
| } else if constexpr (std::is_arithmetic_v<_T>) { |
| return std::to_string(t); |
| } else if constexpr (std::is_same_v<std::string, _T>) { |
| return t; |
| #ifdef HAS_NDK_INTERFACE |
| } else if constexpr (std::is_same_v<::ndk::SpAIBinder, _T>) { |
| std::stringstream ss; |
| ss << "binder:" << std::hex << t.get(); |
| return ss.str(); |
| } else if constexpr (std::is_same_v<::ndk::ScopedFileDescriptor, _T>) { |
| return "fd:" + std::to_string(t.get()); |
| } else if constexpr (std::is_base_of_v<::ndk::ICInterface, _T>) { |
| // TODO(b/266248339): this format is to make it easy to handle resolv_integration_test |
| // freezing the output format. We would like to print more info. |
| return "<interface>"; |
| #if __ANDROID_API__ >= 31 |
| } else if constexpr (std::is_same_v<::ndk::AParcelableHolder, _T>) { |
| return "AParcelableHolder"; |
| #endif |
| #endif // HAS_NDK_INTERFACE |
| #ifdef HAS_CPP_INTERFACE |
| } else if constexpr (std::is_base_of_v<IInterface, _T>) { |
| std::stringstream ss; |
| ss << "interface:" << std::hex << &t; |
| return ss.str(); |
| } else if constexpr (std::is_same_v<IBinder, _T>) { |
| std::stringstream ss; |
| ss << "binder:" << std::hex << &t; |
| return ss.str(); |
| #endif |
| #ifdef HAS_STRING16 |
| } else if constexpr (std::is_same_v<String16, _T>) { |
| std::stringstream out; |
| out << t; |
| return out.str(); |
| #endif |
| } else if constexpr (details::IsPointerLike<_T>::value || std::is_pointer_v<_T>) { |
| if (!t) return "(null)"; |
| std::stringstream out; |
| out << ToString(*t); |
| return out.str(); |
| } else if constexpr (details::HasToStringMethod<_T>::value) { |
| return t.toString(); |
| } else if constexpr (details::HasToStringFunction<_T>::value) { |
| return toString(t); |
| } else if constexpr (details::IsIterable<_T>::value) { |
| std::stringstream out; |
| bool first = true; |
| out << "["; |
| for (const auto& e : t) { |
| if (first) { |
| first = false; |
| } else { |
| out << ", "; |
| } |
| // Use explicit type parameter in case deref of iterator has different type |
| // e.g. vector<bool> |
| out << ToString<typename _T::value_type>(e); |
| } |
| out << "]"; |
| return out.str(); |
| } else { |
| static_assert(details::TypeDependentFalse<_T>::value, "no toString implemented, huh?"); |
| } |
| } |
| |
| } // namespace internal |
| } // namespace android |
| |
| #ifdef HAS_STRONG_POINTER |
| #undef HAS_STRONG_POINTER |
| #endif |
| |
| #ifdef HAS_STRING16 |
| #undef HAS_STRING16 |
| #endif |
| |
| #ifdef HAS_NDK_INTERFACE |
| #undef HAS_NDK_INTERFACE |
| #endif |
| |
| #ifdef HAS_CPP_INTERFACE |
| #undef HAS_CPP_INTERFACE |
| #endif |
| |
| /** @} */ |