blob: c82d3123edc0d81462c6e747e447a5db00df7ae0 [file] [log] [blame]
Elliott Hughes87f62a82011-04-22 19:22:54 -07001/*
2 * Copyright (C) 2006 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#define LOG_TAG "JNIHelp"
18
Orion Hodsonaadb3732018-11-21 12:58:34 +000019#include "nativehelper/JNIHelp.h"
Elliott Hughes87f62a82011-04-22 19:22:54 -070020
Orion Hodsonaadb3732018-11-21 12:58:34 +000021#include "ALog-priv.h"
Elliott Hughes87f62a82011-04-22 19:22:54 -070022
Brian Carlstromdd8af232012-05-13 23:56:07 -070023#include <string>
24
Orion Hodsonaadb3732018-11-21 12:58:34 +000025#include "JniConstants.h"
26#include "nativehelper/ScopedLocalRef.h"
Logan Chien63e49172016-06-08 11:33:36 +080027
Elliott Hughes87f62a82011-04-22 19:22:54 -070028/**
29 * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
30 */
31template<typename T>
32class scoped_local_ref {
33public:
Chih-Hung Hsieh01332142016-05-02 11:47:43 -070034 explicit scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
Elliott Hughes87f62a82011-04-22 19:22:54 -070035 : mEnv(env), mLocalRef(localRef)
36 {
37 }
38
39 ~scoped_local_ref() {
40 reset();
41 }
42
43 void reset(T localRef = NULL) {
44 if (mLocalRef != NULL) {
45 (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
46 mLocalRef = localRef;
47 }
48 }
49
50 T get() const {
51 return mLocalRef;
52 }
53
54private:
Ian Rogers8288dde2014-11-04 11:42:02 -080055 C_JNIEnv* const mEnv;
Elliott Hughes87f62a82011-04-22 19:22:54 -070056 T mLocalRef;
57
Ian Rogers8288dde2014-11-04 11:42:02 -080058 DISALLOW_COPY_AND_ASSIGN(scoped_local_ref);
Elliott Hughes87f62a82011-04-22 19:22:54 -070059};
60
61static jclass findClass(C_JNIEnv* env, const char* className) {
62 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
63 return (*env)->FindClass(e, className);
64}
65
Orion Hodsonb01e7fe2018-11-07 06:07:50 +000066MODULE_API int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
Elliott Hughes87f62a82011-04-22 19:22:54 -070067 const JNINativeMethod* gMethods, int numMethods)
68{
69 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
70
Brian Carlstromdd8af232012-05-13 23:56:07 -070071 ALOGV("Registering %s's %d native methods...", className, numMethods);
Elliott Hughes87f62a82011-04-22 19:22:54 -070072
73 scoped_local_ref<jclass> c(env, findClass(env, className));
Jerome Gaillardf0d42052019-03-12 15:17:22 +000074 ALOG_ALWAYS_FATAL_IF(c.get() == NULL,
75 "Native registration unable to find class '%s'; aborting...",
76 className);
Elliott Hughes87f62a82011-04-22 19:22:54 -070077
Jerome Gaillardf0d42052019-03-12 15:17:22 +000078 int result = e->RegisterNatives(c.get(), gMethods, numMethods);
79 ALOG_ALWAYS_FATAL_IF(result < 0, "RegisterNatives failed for '%s'; aborting...",
80 className);
Elliott Hughes87f62a82011-04-22 19:22:54 -070081
82 return 0;
83}
84
85/*
86 * Returns a human-readable summary of an exception object. The buffer will
87 * be populated with the "binary" class name and, if present, the
88 * exception message.
89 */
Brian Carlstromdd8af232012-05-13 23:56:07 -070090static bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -070091 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
92
93 /* get the name of the exception's class */
94 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
95 scoped_local_ref<jclass> classClass(env,
96 (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
97 jmethodID classGetNameMethod =
98 (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
99 scoped_local_ref<jstring> classNameStr(env,
100 (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
101 if (classNameStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700102 (*env)->ExceptionClear(e);
103 result = "<error getting class name>";
104 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700105 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700106 const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
107 if (classNameChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700108 (*env)->ExceptionClear(e);
109 result = "<error getting class name UTF-8>";
110 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700111 }
Brian Carlstromdd8af232012-05-13 23:56:07 -0700112 result += classNameChars;
113 (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700114
115 /* if the exception has a detail message, get that */
116 jmethodID getMessage =
117 (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
118 scoped_local_ref<jstring> messageStr(env,
119 (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
120 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700121 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700122 }
123
Brian Carlstromdd8af232012-05-13 23:56:07 -0700124 result += ": ";
125
Elliott Hughes87f62a82011-04-22 19:22:54 -0700126 const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
127 if (messageChars != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700128 result += messageChars;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700129 (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
130 } else {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700131 result += "<error getting message>";
Elliott Hughes87f62a82011-04-22 19:22:54 -0700132 (*env)->ExceptionClear(e); // clear OOM
Elliott Hughes87f62a82011-04-22 19:22:54 -0700133 }
134
Brian Carlstromdd8af232012-05-13 23:56:07 -0700135 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700136}
137
138/*
139 * Returns an exception (with stack trace) as a string.
140 */
Brian Carlstromdd8af232012-05-13 23:56:07 -0700141static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700142 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
143
144 scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
145 if (stringWriterClass.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700146 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700147 }
148
149 jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
150 jmethodID stringWriterToStringMethod =
151 (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
152
153 scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
154 if (printWriterClass.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700155 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700156 }
157
158 jmethodID printWriterCtor =
159 (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
160
161 scoped_local_ref<jobject> stringWriter(env,
162 (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
163 if (stringWriter.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700164 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700165 }
166
Huanqi Chend47c3252014-06-24 13:31:31 +0200167 scoped_local_ref<jobject> printWriter(env,
168 (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get()));
169 if (printWriter.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700170 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700171 }
172
173 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
174 jmethodID printStackTraceMethod =
175 (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
Huanqi Chend47c3252014-06-24 13:31:31 +0200176 (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter.get());
Elliott Hughes87f62a82011-04-22 19:22:54 -0700177
178 if ((*env)->ExceptionCheck(e)) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700179 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700180 }
181
182 scoped_local_ref<jstring> messageStr(env,
183 (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
184 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700185 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700186 }
187
188 const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
189 if (utfChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700190 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700191 }
192
Brian Carlstromdd8af232012-05-13 23:56:07 -0700193 result = utfChars;
194
Elliott Hughes87f62a82011-04-22 19:22:54 -0700195 (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700196 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700197}
198
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000199MODULE_API int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700200 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
201
202 if ((*env)->ExceptionCheck(e)) {
203 /* TODO: consider creating the new exception with this as "cause" */
204 scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
205 (*env)->ExceptionClear(e);
206
207 if (exception.get() != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700208 std::string text;
209 getExceptionSummary(env, exception.get(), text);
210 ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700211 }
212 }
213
214 scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
215 if (exceptionClass.get() == NULL) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000216 ALOGE("Unable to find exception class %s", className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700217 /* ClassNotFoundException now pending */
218 return -1;
219 }
220
221 if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000222 ALOGE("Failed throwing '%s' '%s'", className, msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700223 /* an exception, most likely OOM, will now be pending */
224 return -1;
225 }
226
227 return 0;
228}
229
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000230MODULE_API int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700231 char msgBuf[512];
232 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
233 return jniThrowException(env, className, msgBuf);
234}
235
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000236MODULE_API int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700237 return jniThrowException(env, "java/lang/NullPointerException", msg);
238}
239
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000240MODULE_API int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700241 return jniThrowException(env, "java/lang/RuntimeException", msg);
242}
243
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000244MODULE_API int jniThrowIOException(C_JNIEnv* env, int errnum) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700245 char buffer[80];
246 const char* message = jniStrError(errnum, buffer, sizeof(buffer));
247 return jniThrowException(env, "java/io/IOException", message);
248}
249
Elliott Hughesa4e4e742013-09-03 17:47:18 -0700250static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700251 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
252
Jeff Browne2b11e72012-04-10 20:34:39 -0700253 scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
Elliott Hughes87f62a82011-04-22 19:22:54 -0700254 if (exception == NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700255 exception = currentException.get();
Elliott Hughes87f62a82011-04-22 19:22:54 -0700256 if (exception == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700257 return "<no pending exception>";
Elliott Hughes87f62a82011-04-22 19:22:54 -0700258 }
Jeff Browne2b11e72012-04-10 20:34:39 -0700259 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700260
Jeff Browne2b11e72012-04-10 20:34:39 -0700261 if (currentException.get() != NULL) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700262 (*env)->ExceptionClear(e);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700263 }
264
Brian Carlstromdd8af232012-05-13 23:56:07 -0700265 std::string trace;
266 if (!getStackTrace(env, exception, trace)) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700267 (*env)->ExceptionClear(e);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700268 getExceptionSummary(env, exception, trace);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700269 }
270
Elliott Hughes87f62a82011-04-22 19:22:54 -0700271 if (currentException.get() != NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700272 (*env)->Throw(e, currentException.get()); // rethrow
Elliott Hughes87f62a82011-04-22 19:22:54 -0700273 }
Brian Carlstromdd8af232012-05-13 23:56:07 -0700274
275 return trace;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700276}
277
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000278MODULE_API void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
Elliott Hughesa4e4e742013-09-03 17:47:18 -0700279 std::string trace(jniGetStackTrace(env, exception));
280 __android_log_write(priority, tag, trace.c_str());
281}
282
Alex Light48694f72018-10-31 14:50:54 -0700283// Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
284// char *strerror_r(int errnum, char *buf, size_t n);
285//
286// Some versions of bionic support the glibc style call. Since the set of defines that determine
287// which version is used is byzantine in its complexity we will just use this C++ template hack to
288// select the correct jniStrError implementation based on the libc being used.
289namespace impl {
290
Alex Light48694f72018-10-31 14:50:54 -0700291using GNUStrError = char* (*)(int,char*,size_t);
292using POSIXStrError = int (*)(int,char*,size_t);
293
Alex Light48694f72018-10-31 14:50:54 -0700294inline const char* realJniStrError(GNUStrError func, int errnum, char* buf, size_t buflen) {
295 return func(errnum, buf, buflen);
296}
297
Alex Light48694f72018-10-31 14:50:54 -0700298inline const char* realJniStrError(POSIXStrError func, int errnum, char* buf, size_t buflen) {
299 int rc = func(errnum, buf, buflen);
Elliott Hughesd60512c2013-10-08 14:15:18 -0700300 if (rc != 0) {
301 // (POSIX only guarantees a value other than 0. The safest
Elliott Hughes87f62a82011-04-22 19:22:54 -0700302 // way to implement this function is to use C++ and overload on the
Elliott Hughesd60512c2013-10-08 14:15:18 -0700303 // type of strerror_r to accurately distinguish GNU from POSIX.)
Elliott Hughes87f62a82011-04-22 19:22:54 -0700304 snprintf(buf, buflen, "errno %d", errnum);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700305 }
Elliott Hughesd60512c2013-10-08 14:15:18 -0700306 return buf;
Alex Light48694f72018-10-31 14:50:54 -0700307}
Elliott Hughes71cb8412018-11-09 11:10:40 -0800308
Alex Light48694f72018-10-31 14:50:54 -0700309} // namespace impl
310
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000311MODULE_API const char* jniStrError(int errnum, char* buf, size_t buflen) {
Jerome Gaillardf0d42052019-03-12 15:17:22 +0000312#ifdef _WIN32
313 strerror_s(buf, buflen, errnum);
314 return buf;
315#else
Elliott Hughes71cb8412018-11-09 11:10:40 -0800316 // The magic of C++ overloading selects the correct implementation based on the declared type of
Alex Light48694f72018-10-31 14:50:54 -0700317 // strerror_r. The inline will ensure that we don't have any indirect calls.
318 return impl::realJniStrError(strerror_r, errnum, buf, buflen);
Jerome Gaillardf0d42052019-03-12 15:17:22 +0000319#endif
Elliott Hughes87f62a82011-04-22 19:22:54 -0700320}
321
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000322MODULE_API jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700323 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Orion Hodsonaadb3732018-11-21 12:58:34 +0000324 jobject fileDescriptor = e->NewObject(JniConstants::GetFileDescriptorClass(e),
325 JniConstants::GetFileDescriptorInitMethod(e));
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000326 // NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java
Orion Hodsonaadb3732018-11-21 12:58:34 +0000327 // caller if the alloc fails, so we just return nullptr when that happens.
328 if (fileDescriptor != nullptr) {
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000329 jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
330 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700331 return fileDescriptor;
332}
333
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000334MODULE_API int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700335 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Orion Hodsonaadb3732018-11-21 12:58:34 +0000336 if (fileDescriptor != nullptr) {
337 return e->GetIntField(fileDescriptor,
338 JniConstants::GetFileDescriptorDescriptorField(e));
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000339 } else {
340 return -1;
341 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700342}
343
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000344MODULE_API void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700345 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Pete Bentley0505b222018-07-20 18:18:44 +0100346 if (fileDescriptor == nullptr) {
347 jniThrowNullPointerException(e, "null FileDescriptor");
348 } else {
Orion Hodsonaadb3732018-11-21 12:58:34 +0000349 e->SetIntField(fileDescriptor, JniConstants::GetFileDescriptorDescriptorField(e), value);
Pete Bentley0505b222018-07-20 18:18:44 +0100350 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700351}
352
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000353MODULE_API jlong jniGetOwnerIdFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
Josh Gao669bc9e2018-06-25 16:22:11 -0700354 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Orion Hodsonaadb3732018-11-21 12:58:34 +0000355 return e->GetLongField(fileDescriptor, JniConstants::GetFileDescriptorOwnerIdField(e));
Josh Gao669bc9e2018-06-25 16:22:11 -0700356}
357
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000358MODULE_API jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
Jeff Browndc176c52013-04-02 18:09:29 -0700359 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Orion Hodsonaadb3732018-11-21 12:58:34 +0000360 return e->CallObjectMethod(ref, JniConstants::GetReferenceGetMethod(e));
Jeff Browndc176c52013-04-02 18:09:29 -0700361}
362
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000363MODULE_API jstring jniCreateString(C_JNIEnv* env, const jchar* unicodeChars, jsize len) {
Fredrik Roubert2e312802017-07-04 21:53:08 +0200364 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Orion Hodsonaadb3732018-11-21 12:58:34 +0000365 return e->NewString(unicodeChars, len);
Fredrik Roubert2e312802017-07-04 21:53:08 +0200366}