1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /**
18  * @addtogroup NdkBinder
19  * @{
20  */
21 
22 /**
23  * @file binder_to_string.h
24  * @brief Helper for parcelable.
25  */
26 
27 #pragma once
28 
29 #include <codecvt>
30 #include <locale>
31 #include <memory>
32 #include <optional>
33 #include <sstream>
34 #include <string>
35 #include <type_traits>
36 
37 #if __has_include(<utils/StrongPointer.h>)
38 #include <utils/StrongPointer.h>
39 #define HAS_STRONG_POINTER
40 #endif
41 
42 #if __has_include(<utils/String16.h>)
43 #include <utils/String16.h>
44 #define HAS_STRING16
45 #endif
46 
47 #if __has_include(<android/binder_ibinder.h>)
48 #include <android/binder_auto_utils.h>
49 #include <android/binder_interface_utils.h>
50 #include <android/binder_parcelable_utils.h>
51 #define HAS_NDK_INTERFACE
52 #else
53 #include <binder/IBinder.h>
54 #include <binder/IInterface.h>
55 #include <binder/ParcelFileDescriptor.h>
56 #include <binder/ParcelableHolder.h>
57 #endif  //_has_include
58 
59 namespace android {
60 namespace internal {
61 
62 // ToString is a utility to generate string representation for various AIDL-supported types.
63 template <typename _T>
64 std::string ToString(const _T& t);
65 
66 namespace details {
67 
68 // Truthy if _T has toString() method.
69 template <typename _T>
70 class HasToStringMethod {
71     template <typename _U>
72     static auto _test(int) -> decltype(std::declval<_U>().toString(), std::true_type());
73     template <typename _U>
74     static std::false_type _test(...);
75 
76    public:
77     enum { value = decltype(_test<_T>(0))::value };
78 };
79 
80 // Truthy if _T has a overloaded toString(T)
81 template <typename _T>
82 class HasToStringFunction {
83     template <typename _U>
84     static auto _test(int) -> decltype(toString(std::declval<_U>()), std::true_type());
85     template <typename _U>
86     static std::false_type _test(...);
87 
88    public:
89     enum { value = decltype(_test<_T>(0))::value };
90 };
91 
92 template <typename T, template <typename...> typename U>
93 struct IsInstantiationOf : std::false_type {};
94 
95 template <template <typename...> typename U, typename... Args>
96 struct IsInstantiationOf<U<Args...>, U> : std::true_type {};
97 
98 // Truthy if _T is like a pointer: one of sp/optional/shared_ptr
99 template <typename _T>
100 class IsPointerLike {
101     template <typename _U>
102     static std::enable_if_t<
103 #ifdef HAS_STRONG_POINTER
104             IsInstantiationOf<_U, sp>::value ||  // for IBinder and interface types in the C++
105                                                  // backend
106 #endif
107                     IsInstantiationOf<_U, std::optional>::value ||  // for @nullable types in the
108                                                                     // C++/NDK backends
109                     IsInstantiationOf<_U, std::shared_ptr>::value,  // for interface types in the
110                                                                     // NDK backends
111 
112             std::true_type>
113     _test(int);
114     template <typename _U>
115     static std::false_type _test(...);
116 
117    public:
118     enum { value = decltype(_test<_T>(0))::value };
119 };
120 
121 // Truthy if _T is like a container
122 template <typename _T>
123 class IsIterable {
124     template <typename _U>
125     static auto _test(int)
126             -> decltype(begin(std::declval<_U>()), end(std::declval<_U>()), std::true_type());
127     template <typename _U>
128     static std::false_type _test(...);
129 
130    public:
131     enum { value = decltype(_test<_T>(0))::value };
132 };
133 
134 template <typename _T>
135 class ToEmptyString {
136     template <typename _U>
137     static std::enable_if_t<
138 #ifdef HAS_NDK_INTERFACE
139             std::is_base_of_v<::ndk::ICInterface, _U> ||
140                     std::is_same_v<::ndk::AParcelableHolder, _U>
141 #else
142             std::is_base_of_v<IInterface, _U> || std::is_same_v<IBinder, _U> ||
143                     std::is_same_v<os::ParcelFileDescriptor, _U> ||
144                     std::is_same_v<os::ParcelableHolder, _U>
145 #endif
146             ,
147             std::true_type>
148     _test(int);
149     template <typename _U>
150     static std::false_type _test(...);
151 
152    public:
153     enum { value = decltype(_test<_T>(0))::value };
154 };
155 
156 }  // namespace details
157 
158 template <typename _T>
159 std::string ToString(const _T& t) {
160     if constexpr (details::ToEmptyString<_T>::value) {
161         return "";
162     } else if constexpr (std::is_same_v<bool, _T>) {
163         return t ? "true" : "false";
164     } else if constexpr (std::is_same_v<char16_t, _T>) {
165         return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>().to_bytes(t);
166     } else if constexpr (std::is_arithmetic_v<_T>) {
167         return std::to_string(t);
168     } else if constexpr (std::is_same_v<std::string, _T>) {
169         return t;
170 #ifdef HAS_NDK_INTERFACE
171     } else if constexpr (std::is_same_v<::ndk::SpAIBinder, _T>) {
172         return (t.get() == nullptr) ? "(null)" : "";
173     } else if constexpr (std::is_same_v<::ndk::ScopedFileDescriptor, _T>) {
174         return (t.get() == -1) ? "(null)" : "";
175 #endif
176 #ifdef HAS_STRING16
177     } else if constexpr (std::is_same_v<String16, _T>) {
178         std::stringstream out;
179         out << t;
180         return out.str();
181 #endif
182     } else if constexpr (details::IsPointerLike<_T>::value || std::is_pointer_v<_T>) {
183         if (!t) return "(null)";
184         std::stringstream out;
185         out << ToString(*t);
186         return out.str();
187     } else if constexpr (details::HasToStringMethod<_T>::value) {
188         return t.toString();
189     } else if constexpr (details::HasToStringFunction<_T>::value) {
190         return toString(t);
191     } else if constexpr (details::IsIterable<_T>::value) {
192         std::stringstream out;
193         bool first = true;
194         out << "[";
195         for (const auto& e : t) {
196             if (first) {
197                 first = false;
198             } else {
199                 out << ", ";
200             }
201             // Use explicit type parameter in case deref of iterator has different type
202             // e.g. vector<bool>
203             out << ToString<typename _T::value_type>(e);
204         }
205         out << "]";
206         return out.str();
207     } else {
208         return "{no toString() implemented}";
209     }
210 }
211 
212 }  // namespace internal
213 }  // namespace android
214 
215 /** @} */
216