1 /*
2 * Copyright 2015 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 // Android's FindClass() is tricky because the app-specific ClassLoader is not
12 // consulted when there is no app-specific frame on the stack (i.e. when called
13 // from a thread created from native C++ code). These helper functions provide a
14 // workaround for this.
15 // http://developer.android.com/training/articles/perf-jni.html#faq_FindClass
16
17 #ifndef SDK_ANDROID_NATIVE_API_JNI_JAVA_TYPES_H_
18 #define SDK_ANDROID_NATIVE_API_JNI_JAVA_TYPES_H_
19
20 #include <jni.h>
21
22 #include <map>
23 #include <memory>
24 #include <string>
25 #include <vector>
26
27 #include "absl/types/optional.h"
28 #include "api/array_view.h"
29 #include "api/sequence_checker.h"
30 #include "rtc_base/checks.h"
31 #include "sdk/android/native_api/jni/scoped_java_ref.h"
32
33 // Abort the process if `jni` has a Java exception pending.
34 // This macros uses the comma operator to execute ExceptionDescribe
35 // and ExceptionClear ignoring their return values and sending ""
36 // to the error stream.
37 #define CHECK_EXCEPTION(jni) \
38 RTC_CHECK(!jni->ExceptionCheck()) \
39 << (jni->ExceptionDescribe(), jni->ExceptionClear(), "")
40
41 namespace webrtc {
42
43 // ---------------
44 // -- Utilities --
45 // ---------------
46
47 // Provides a convenient way to iterate over a Java Iterable using the
48 // C++ range-for loop.
49 // E.g. for (jobject value : Iterable(jni, j_iterable)) { ... }
50 // Note: Since Java iterators cannot be duplicated, the iterator class is not
51 // copyable to prevent creating multiple C++ iterators that refer to the same
52 // Java iterator.
53 class Iterable {
54 public:
55 Iterable(JNIEnv* jni, const JavaRef<jobject>& iterable);
56 Iterable(Iterable&& other);
57
58 ~Iterable();
59
60 Iterable(const Iterable&) = delete;
61 Iterable& operator=(const Iterable&) = delete;
62
63 class Iterator {
64 public:
65 // Creates an iterator representing the end of any collection.
66 Iterator();
67 // Creates an iterator pointing to the beginning of the specified
68 // collection.
69 Iterator(JNIEnv* jni, const JavaRef<jobject>& iterable);
70
71 // Move constructor - necessary to be able to return iterator types from
72 // functions.
73 Iterator(Iterator&& other);
74
75 ~Iterator();
76
77 Iterator(const Iterator&) = delete;
78 Iterator& operator=(const Iterator&) = delete;
79
80 // Move assignment should not be used.
81 Iterator& operator=(Iterator&&) = delete;
82
83 // Advances the iterator one step.
84 Iterator& operator++();
85
86 // Removes the element the iterator is pointing to. Must still advance the
87 // iterator afterwards.
88 void Remove();
89
90 // Provides a way to compare the iterator with itself and with the end
91 // iterator.
92 // Note: all other comparison results are undefined, just like for C++ input
93 // iterators.
94 bool operator==(const Iterator& other);
95 bool operator!=(const Iterator& other) { return !(*this == other); }
96 ScopedJavaLocalRef<jobject>& operator*();
97
98 private:
99 bool AtEnd() const;
100
101 JNIEnv* jni_ = nullptr;
102 ScopedJavaLocalRef<jobject> iterator_;
103 ScopedJavaLocalRef<jobject> value_;
104 SequenceChecker thread_checker_;
105 };
106
begin()107 Iterable::Iterator begin() { return Iterable::Iterator(jni_, iterable_); }
end()108 Iterable::Iterator end() { return Iterable::Iterator(); }
109
110 private:
111 JNIEnv* jni_;
112 ScopedJavaLocalRef<jobject> iterable_;
113 };
114
115 // Returns true if `obj` == null in Java.
116 bool IsNull(JNIEnv* jni, const JavaRef<jobject>& obj);
117
118 // Returns the name of a Java enum.
119 std::string GetJavaEnumName(JNIEnv* jni, const JavaRef<jobject>& j_enum);
120
121 Iterable GetJavaMapEntrySet(JNIEnv* jni, const JavaRef<jobject>& j_map);
122 ScopedJavaLocalRef<jobject> GetJavaMapEntryKey(JNIEnv* jni,
123 const JavaRef<jobject>& j_entry);
124 ScopedJavaLocalRef<jobject> GetJavaMapEntryValue(
125 JNIEnv* jni,
126 const JavaRef<jobject>& j_entry);
127
128 // --------------------------------------------------------
129 // -- Methods for converting Java types to native types. --
130 // --------------------------------------------------------
131
132 int64_t JavaToNativeLong(JNIEnv* env, const JavaRef<jobject>& j_long);
133
134 absl::optional<bool> JavaToNativeOptionalBool(JNIEnv* jni,
135 const JavaRef<jobject>& boolean);
136 absl::optional<double> JavaToNativeOptionalDouble(
137 JNIEnv* jni,
138 const JavaRef<jobject>& j_double);
139 absl::optional<int32_t> JavaToNativeOptionalInt(
140 JNIEnv* jni,
141 const JavaRef<jobject>& integer);
142
143 // Given a (UTF-16) jstring return a new UTF-8 native string.
144 std::string JavaToNativeString(JNIEnv* jni, const JavaRef<jstring>& j_string);
145
146 template <typename T, typename Convert>
JavaToNativeVector(JNIEnv * env,const JavaRef<jobjectArray> & j_container,Convert convert)147 std::vector<T> JavaToNativeVector(JNIEnv* env,
148 const JavaRef<jobjectArray>& j_container,
149 Convert convert) {
150 std::vector<T> container;
151 const size_t size = env->GetArrayLength(j_container.obj());
152 container.reserve(size);
153 for (size_t i = 0; i < size; ++i) {
154 container.emplace_back(convert(
155 env, ScopedJavaLocalRef<jobject>(
156 env, env->GetObjectArrayElement(j_container.obj(), i))));
157 }
158 CHECK_EXCEPTION(env) << "Error during JavaToNativeVector";
159 return container;
160 }
161
162 template <typename T, typename Java_T = jobject, typename Convert>
JavaListToNativeVector(JNIEnv * env,const JavaRef<jobject> & j_list,Convert convert)163 std::vector<T> JavaListToNativeVector(JNIEnv* env,
164 const JavaRef<jobject>& j_list,
165 Convert convert) {
166 std::vector<T> native_list;
167 if (!j_list.is_null()) {
168 for (ScopedJavaLocalRef<jobject>& j_item : Iterable(env, j_list)) {
169 native_list.emplace_back(
170 convert(env, static_java_ref_cast<Java_T>(env, j_item)));
171 }
172 CHECK_EXCEPTION(env) << "Error during JavaListToNativeVector";
173 }
174 return native_list;
175 }
176
177 template <typename Key, typename T, typename Convert>
JavaToNativeMap(JNIEnv * env,const JavaRef<jobject> & j_map,Convert convert)178 std::map<Key, T> JavaToNativeMap(JNIEnv* env,
179 const JavaRef<jobject>& j_map,
180 Convert convert) {
181 std::map<Key, T> container;
182 for (auto const& j_entry : GetJavaMapEntrySet(env, j_map)) {
183 container.emplace(convert(env, GetJavaMapEntryKey(env, j_entry),
184 GetJavaMapEntryValue(env, j_entry)));
185 }
186 return container;
187 }
188
189 // Converts Map<String, String> to std::map<std::string, std::string>.
190 std::map<std::string, std::string> JavaToNativeStringMap(
191 JNIEnv* env,
192 const JavaRef<jobject>& j_map);
193
194 // --------------------------------------------------------
195 // -- Methods for converting native types to Java types. --
196 // --------------------------------------------------------
197
198 ScopedJavaLocalRef<jobject> NativeToJavaBoolean(JNIEnv* env, bool b);
199 ScopedJavaLocalRef<jobject> NativeToJavaDouble(JNIEnv* env, double d);
200 ScopedJavaLocalRef<jobject> NativeToJavaInteger(JNIEnv* jni, int32_t i);
201 ScopedJavaLocalRef<jobject> NativeToJavaLong(JNIEnv* env, int64_t u);
202 ScopedJavaLocalRef<jstring> NativeToJavaString(JNIEnv* jni, const char* str);
203 ScopedJavaLocalRef<jstring> NativeToJavaString(JNIEnv* jni,
204 const std::string& str);
205
206 ScopedJavaLocalRef<jobject> NativeToJavaDouble(
207 JNIEnv* jni,
208 const absl::optional<double>& optional_double);
209 ScopedJavaLocalRef<jobject> NativeToJavaInteger(
210 JNIEnv* jni,
211 const absl::optional<int32_t>& optional_int);
212 ScopedJavaLocalRef<jstring> NativeToJavaString(
213 JNIEnv* jni,
214 const absl::optional<std::string>& str);
215
216 // Helper function for converting std::vector<T> into a Java array.
217 template <typename T, typename Convert>
NativeToJavaObjectArray(JNIEnv * env,const std::vector<T> & container,jclass clazz,Convert convert)218 ScopedJavaLocalRef<jobjectArray> NativeToJavaObjectArray(
219 JNIEnv* env,
220 const std::vector<T>& container,
221 jclass clazz,
222 Convert convert) {
223 ScopedJavaLocalRef<jobjectArray> j_container(
224 env, env->NewObjectArray(container.size(), clazz, nullptr));
225 int i = 0;
226 for (const T& element : container) {
227 env->SetObjectArrayElement(j_container.obj(), i,
228 convert(env, element).obj());
229 ++i;
230 }
231 return j_container;
232 }
233
234 ScopedJavaLocalRef<jbyteArray> NativeToJavaByteArray(
235 JNIEnv* env,
236 rtc::ArrayView<int8_t> container);
237 ScopedJavaLocalRef<jintArray> NativeToJavaIntArray(
238 JNIEnv* env,
239 rtc::ArrayView<int32_t> container);
240
241 std::vector<int8_t> JavaToNativeByteArray(JNIEnv* env,
242 const JavaRef<jbyteArray>& jarray);
243 std::vector<int32_t> JavaToNativeIntArray(JNIEnv* env,
244 const JavaRef<jintArray>& jarray);
245
246 ScopedJavaLocalRef<jobjectArray> NativeToJavaBooleanArray(
247 JNIEnv* env,
248 const std::vector<bool>& container);
249 ScopedJavaLocalRef<jobjectArray> NativeToJavaDoubleArray(
250 JNIEnv* env,
251 const std::vector<double>& container);
252 ScopedJavaLocalRef<jobjectArray> NativeToJavaIntegerArray(
253 JNIEnv* env,
254 const std::vector<int32_t>& container);
255 ScopedJavaLocalRef<jobjectArray> NativeToJavaLongArray(
256 JNIEnv* env,
257 const std::vector<int64_t>& container);
258 ScopedJavaLocalRef<jobjectArray> NativeToJavaStringArray(
259 JNIEnv* env,
260 const std::vector<std::string>& container);
261
262 // This is a helper class for NativeToJavaList(). Use that function instead of
263 // using this class directly.
264 class JavaListBuilder {
265 public:
266 explicit JavaListBuilder(JNIEnv* env);
267 ~JavaListBuilder();
268 void add(const JavaRef<jobject>& element);
java_list()269 ScopedJavaLocalRef<jobject> java_list() { return j_list_; }
270
271 private:
272 JNIEnv* env_;
273 ScopedJavaLocalRef<jobject> j_list_;
274 };
275
276 template <typename C, typename Convert>
NativeToJavaList(JNIEnv * env,const C & container,Convert convert)277 ScopedJavaLocalRef<jobject> NativeToJavaList(JNIEnv* env,
278 const C& container,
279 Convert convert) {
280 JavaListBuilder builder(env);
281 for (const auto& e : container)
282 builder.add(convert(env, e));
283 return builder.java_list();
284 }
285
286 // This is a helper class for NativeToJavaMap(). Use that function instead of
287 // using this class directly.
288 class JavaMapBuilder {
289 public:
290 explicit JavaMapBuilder(JNIEnv* env);
291 ~JavaMapBuilder();
292 void put(const JavaRef<jobject>& key, const JavaRef<jobject>& value);
GetJavaMap()293 ScopedJavaLocalRef<jobject> GetJavaMap() { return j_map_; }
294
295 private:
296 JNIEnv* env_;
297 ScopedJavaLocalRef<jobject> j_map_;
298 };
299
300 template <typename C, typename Convert>
NativeToJavaMap(JNIEnv * env,const C & container,Convert convert)301 ScopedJavaLocalRef<jobject> NativeToJavaMap(JNIEnv* env,
302 const C& container,
303 Convert convert) {
304 JavaMapBuilder builder(env);
305 for (const auto& e : container) {
306 const auto key_value_pair = convert(env, e);
307 builder.put(key_value_pair.first, key_value_pair.second);
308 }
309 return builder.GetJavaMap();
310 }
311
312 template <typename C>
NativeToJavaStringMap(JNIEnv * env,const C & container)313 ScopedJavaLocalRef<jobject> NativeToJavaStringMap(JNIEnv* env,
314 const C& container) {
315 JavaMapBuilder builder(env);
316 for (const auto& e : container) {
317 const auto key_value_pair = std::make_pair(
318 NativeToJavaString(env, e.first), NativeToJavaString(env, e.second));
319 builder.put(key_value_pair.first, key_value_pair.second);
320 }
321 return builder.GetJavaMap();
322 }
323
324 // Return a `jlong` that will correctly convert back to `ptr`. This is needed
325 // because the alternative (of silently passing a 32-bit pointer to a vararg
326 // function expecting a 64-bit param) picks up garbage in the high 32 bits.
327 jlong NativeToJavaPointer(void* ptr);
328
329 // ------------------------
330 // -- Deprecated methods --
331 // ------------------------
332
333 // Deprecated. Use JavaToNativeString.
JavaToStdString(JNIEnv * jni,const JavaRef<jstring> & j_string)334 inline std::string JavaToStdString(JNIEnv* jni,
335 const JavaRef<jstring>& j_string) {
336 return JavaToNativeString(jni, j_string);
337 }
338
339 // Deprecated. Use scoped jobjects instead.
JavaToStdString(JNIEnv * jni,jstring j_string)340 inline std::string JavaToStdString(JNIEnv* jni, jstring j_string) {
341 return JavaToStdString(jni, JavaParamRef<jstring>(j_string));
342 }
343
344 // Deprecated. Use JavaListToNativeVector<std::string, jstring> instead.
345 // Given a List of (UTF-16) jstrings
346 // return a new vector of UTF-8 native strings.
347 std::vector<std::string> JavaToStdVectorStrings(JNIEnv* jni,
348 const JavaRef<jobject>& list);
349
350 // Deprecated. Use JavaToNativeStringMap instead.
351 // Parses Map<String, String> to std::map<std::string, std::string>.
JavaToStdMapStrings(JNIEnv * jni,const JavaRef<jobject> & j_map)352 inline std::map<std::string, std::string> JavaToStdMapStrings(
353 JNIEnv* jni,
354 const JavaRef<jobject>& j_map) {
355 return JavaToNativeStringMap(jni, j_map);
356 }
357
358 // Deprecated. Use scoped jobjects instead.
JavaToStdMapStrings(JNIEnv * jni,jobject j_map)359 inline std::map<std::string, std::string> JavaToStdMapStrings(JNIEnv* jni,
360 jobject j_map) {
361 return JavaToStdMapStrings(jni, JavaParamRef<jobject>(j_map));
362 }
363
364 } // namespace webrtc
365
366 #endif // SDK_ANDROID_NATIVE_API_JNI_JAVA_TYPES_H_
367