• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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 #ifndef SRC_ANDROID_SDK_NATIVEHELPER_JNIHELP_H_
18 #define SRC_ANDROID_SDK_NATIVEHELPER_JNIHELP_H_
19 
20 /*
21  * JNI helper functions.
22  *
23  * This file may be included by C or C++ code, which is trouble because jni.h
24  * uses different typedefs for JNIEnv in each language.
25  */
26 
27 // Copied from
28 // https://cs.android.com/android/platform/superproject/main/+/main:libnativehelper/include/nativehelper/JNIHelp.h;drc=88c826d2c0b20f8c29e5549333c49fb824055e6a
29 
30 #include <sys/cdefs.h>
31 
32 #include <errno.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 
39 #include <jni.h>
40 
41 #include <android/log.h>
42 
43 // Avoid formatting this as it must match webview's usage
44 // (webview/graphics_utils.cpp).
45 // clang-format off
46 #ifndef NELEM
47 #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
48 #endif
49 // clang-format on
50 
51 /*
52  * For C++ code, we provide inlines that map to the C functions.  g++ always
53  * inlines these, even on non-optimized builds.
54  */
55 #if defined(__cplusplus)
56 
57 namespace android::jnihelp {
58 struct [[maybe_unused]] ExpandableString {
59   size_t dataSize;  // The length of the C string data (not including the
60                     // null-terminator).
61   char* data;       // The C string data.
62 };
63 
ExpandableStringInitialize(struct ExpandableString * s)64 [[maybe_unused]] static void ExpandableStringInitialize(
65     struct ExpandableString* s) {
66   memset(s, 0, sizeof(*s));
67 }
68 
ExpandableStringRelease(struct ExpandableString * s)69 [[maybe_unused]] static void ExpandableStringRelease(
70     struct ExpandableString* s) {
71   free(s->data);
72   memset(s, 0, sizeof(*s));
73 }
74 
ExpandableStringAppend(struct ExpandableString * s,const char * text)75 [[maybe_unused]] static bool ExpandableStringAppend(struct ExpandableString* s,
76                                                     const char* text) {
77   size_t textSize = strlen(text);
78   size_t requiredSize = s->dataSize + textSize + 1;
79   char* data = (char*)realloc(s->data, requiredSize);
80   if (data == NULL) {
81     return false;
82   }
83   s->data = data;
84   memcpy(s->data + s->dataSize, text, textSize + 1);
85   s->dataSize += textSize;
86   return true;
87 }
88 
ExpandableStringAssign(struct ExpandableString * s,const char * text)89 [[maybe_unused]] static bool ExpandableStringAssign(struct ExpandableString* s,
90                                                     const char* text) {
91   ExpandableStringRelease(s);
92   return ExpandableStringAppend(s, text);
93 }
94 
safe_strerror(char * (* strerror_r_method)(int,char *,size_t),int errnum,char * buf,size_t buflen)95 [[maybe_unused]] inline char* safe_strerror(char* (*strerror_r_method)(int,
96                                                                        char*,
97                                                                        size_t),
98                                             int errnum,
99                                             char* buf,
100                                             size_t buflen) {
101   return strerror_r_method(errnum, buf, buflen);
102 }
103 
safe_strerror(int (* strerror_r_method)(int,char *,size_t),int errnum,char * buf,size_t buflen)104 [[maybe_unused]] inline char* safe_strerror(int (*strerror_r_method)(int,
105                                                                      char*,
106                                                                      size_t),
107                                             int errnum,
108                                             char* buf,
109                                             size_t buflen) {
110   int rc = strerror_r_method(errnum, buf, buflen);
111   if (rc != 0) {
112     snprintf(buf, buflen, "errno %d", errnum);
113   }
114   return buf;
115 }
116 
platformStrError(int errnum,char * buf,size_t buflen)117 [[maybe_unused]] static const char* platformStrError(int errnum,
118                                                      char* buf,
119                                                      size_t buflen) {
120 #ifdef _WIN32
121   strerror_s(buf, buflen, errnum);
122   return buf;
123 #else
124   return safe_strerror(strerror_r, errnum, buf, buflen);
125 #endif
126 }
127 
FindMethod(JNIEnv * env,const char * className,const char * methodName,const char * descriptor)128 [[maybe_unused]] static jmethodID FindMethod(JNIEnv* env,
129                                              const char* className,
130                                              const char* methodName,
131                                              const char* descriptor) {
132   // This method is only valid for classes in the core library which are
133   // not unloaded during the lifetime of managed code execution.
134   jclass clazz = env->FindClass(className);
135   jmethodID methodId = env->GetMethodID(clazz, methodName, descriptor);
136   env->DeleteLocalRef(clazz);
137   return methodId;
138 }
139 
AppendJString(JNIEnv * env,jstring text,struct ExpandableString * dst)140 [[maybe_unused]] static bool AppendJString(JNIEnv* env,
141                                            jstring text,
142                                            struct ExpandableString* dst) {
143   const char* utfText = env->GetStringUTFChars(text, NULL);
144   if (utfText == NULL) {
145     return false;
146   }
147   bool success = ExpandableStringAppend(dst, utfText);
148   env->ReleaseStringUTFChars(text, utfText);
149   return success;
150 }
151 
152 /*
153  * Returns a human-readable summary of an exception object.  The buffer will
154  * be populated with the "binary" class name and, if present, the
155  * exception message.
156  */
GetExceptionSummary(JNIEnv * env,jthrowable thrown,struct ExpandableString * dst)157 [[maybe_unused]] static bool GetExceptionSummary(JNIEnv* env,
158                                                  jthrowable thrown,
159                                                  struct ExpandableString* dst) {
160   // Summary is <exception_class_name> ": " <exception_message>
161   jclass exceptionClass = env->GetObjectClass(thrown);  // Always succeeds
162   jmethodID getName =
163       FindMethod(env, "java/lang/Class", "getName", "()Ljava/lang/String;");
164   jstring className = (jstring)env->CallObjectMethod(exceptionClass, getName);
165   if (className == NULL) {
166     ExpandableStringAssign(dst, "<error getting class name>");
167     env->ExceptionClear();
168     env->DeleteLocalRef(exceptionClass);
169     return false;
170   }
171   env->DeleteLocalRef(exceptionClass);
172   exceptionClass = NULL;
173 
174   if (!AppendJString(env, className, dst)) {
175     ExpandableStringAssign(dst, "<error getting class name UTF-8>");
176     env->ExceptionClear();
177     env->DeleteLocalRef(className);
178     return false;
179   }
180   env->DeleteLocalRef(className);
181   className = NULL;
182 
183   jmethodID getMessage = FindMethod(env, "java/lang/Throwable", "getMessage",
184                                     "()Ljava/lang/String;");
185   jstring message = (jstring)env->CallObjectMethod(thrown, getMessage);
186   if (message == NULL) {
187     return true;
188   }
189 
190   bool success =
191       (ExpandableStringAppend(dst, ": ") && AppendJString(env, message, dst));
192   if (!success) {
193     // Two potential reasons for reaching here:
194     //
195     // 1. managed heap allocation failure (OOME).
196     // 2. native heap allocation failure for the storage in |dst|.
197     //
198     // Attempt to append failure notification, okay to fail, |dst| contains the
199     // class name of |thrown|.
200     ExpandableStringAppend(dst, "<error getting message>");
201     // Clear OOME if present.
202     env->ExceptionClear();
203   }
204   env->DeleteLocalRef(message);
205   message = NULL;
206   return success;
207 }
208 
NewStringWriter(JNIEnv * env)209 [[maybe_unused]] static jobject NewStringWriter(JNIEnv* env) {
210   jclass clazz = env->FindClass("java/io/StringWriter");
211   jmethodID init = env->GetMethodID(clazz, "<init>", "()V");
212   jobject instance = env->NewObject(clazz, init);
213   env->DeleteLocalRef(clazz);
214   return instance;
215 }
216 
StringWriterToString(JNIEnv * env,jobject stringWriter)217 [[maybe_unused]] static jstring StringWriterToString(JNIEnv* env,
218                                                      jobject stringWriter) {
219   jmethodID toString = FindMethod(env, "java/io/StringWriter", "toString",
220                                   "()Ljava/lang/String;");
221   return (jstring)env->CallObjectMethod(stringWriter, toString);
222 }
223 
NewPrintWriter(JNIEnv * env,jobject writer)224 [[maybe_unused]] static jobject NewPrintWriter(JNIEnv* env, jobject writer) {
225   jclass clazz = env->FindClass("java/io/PrintWriter");
226   jmethodID init = env->GetMethodID(clazz, "<init>", "(Ljava/io/Writer;)V");
227   jobject instance = env->NewObject(clazz, init, writer);
228   env->DeleteLocalRef(clazz);
229   return instance;
230 }
231 
GetStackTrace(JNIEnv * env,jthrowable thrown,struct ExpandableString * dst)232 [[maybe_unused]] static bool GetStackTrace(JNIEnv* env,
233                                            jthrowable thrown,
234                                            struct ExpandableString* dst) {
235   // This function is equivalent to the following Java snippet:
236   //   StringWriter sw = new StringWriter();
237   //   PrintWriter pw = new PrintWriter(sw);
238   //   thrown.printStackTrace(pw);
239   //   String trace = sw.toString();
240   //   return trace;
241   jobject sw = NewStringWriter(env);
242   if (sw == NULL) {
243     return false;
244   }
245 
246   jobject pw = NewPrintWriter(env, sw);
247   if (pw == NULL) {
248     env->DeleteLocalRef(sw);
249     return false;
250   }
251 
252   jmethodID printStackTrace =
253       FindMethod(env, "java/lang/Throwable", "printStackTrace",
254                  "(Ljava/io/PrintWriter;)V");
255   env->CallVoidMethod(thrown, printStackTrace, pw);
256 
257   jstring trace = StringWriterToString(env, sw);
258 
259   env->DeleteLocalRef(pw);
260   pw = NULL;
261   env->DeleteLocalRef(sw);
262   sw = NULL;
263 
264   if (trace == NULL) {
265     return false;
266   }
267 
268   bool success = AppendJString(env, trace, dst);
269   env->DeleteLocalRef(trace);
270   return success;
271 }
272 
GetStackTraceOrSummary(JNIEnv * env,jthrowable thrown,struct ExpandableString * dst)273 [[maybe_unused]] static void GetStackTraceOrSummary(
274     JNIEnv* env,
275     jthrowable thrown,
276     struct ExpandableString* dst) {
277   // This method attempts to get a stack trace or summary info for an exception.
278   // The exception may be provided in the |thrown| argument to this function.
279   // If |thrown| is NULL, then any pending exception is used if it exists.
280 
281   // Save pending exception, callees may raise other exceptions. Any pending
282   // exception is rethrown when this function exits.
283   jthrowable pendingException = env->ExceptionOccurred();
284   if (pendingException != NULL) {
285     env->ExceptionClear();
286   }
287 
288   if (thrown == NULL) {
289     if (pendingException == NULL) {
290       ExpandableStringAssign(dst, "<no pending exception>");
291       return;
292     }
293     thrown = pendingException;
294   }
295 
296   if (!GetStackTrace(env, thrown, dst)) {
297     // GetStackTrace may have raised an exception, clear it since it's not for
298     // the caller.
299     env->ExceptionClear();
300     GetExceptionSummary(env, thrown, dst);
301   }
302 
303   if (pendingException != NULL) {
304     // Re-throw the pending exception present when this method was called.
305     env->Throw(pendingException);
306     env->DeleteLocalRef(pendingException);
307   }
308 }
309 
DiscardPendingException(JNIEnv * env,const char * className)310 [[maybe_unused]] static void DiscardPendingException(JNIEnv* env,
311                                                      const char* className) {
312   jthrowable exception = env->ExceptionOccurred();
313   env->ExceptionClear();
314   if (exception == NULL) {
315     return;
316   }
317 
318   struct ExpandableString summary;
319   ExpandableStringInitialize(&summary);
320   GetExceptionSummary(env, exception, &summary);
321   const char* details = (summary.data != NULL) ? summary.data : "Unknown";
322   __android_log_print(ANDROID_LOG_WARN, "JNIHelp",
323                       "Discarding pending exception (%s) to throw %s", details,
324                       className);
325   ExpandableStringRelease(&summary);
326   env->DeleteLocalRef(exception);
327 }
328 
ThrowException(JNIEnv * env,const char * className,const char * ctorSig,...)329 [[maybe_unused]] static int ThrowException(JNIEnv* env,
330                                            const char* className,
331                                            const char* ctorSig,
332                                            ...) {
333   int status = -1;
334   jclass exceptionClass = NULL;
335 
336   va_list args;
337   va_start(args, ctorSig);
338 
339   DiscardPendingException(env, className);
340 
341   {
342     /* We want to clean up local references before returning from this function,
343      * so, regardless of return status, the end block must run. Have the work
344      * done in a nested block to avoid using any uninitialized variables in the
345      * end block. */
346     exceptionClass = env->FindClass(className);
347     if (exceptionClass == NULL) {
348       __android_log_print(ANDROID_LOG_ERROR, "JNIHelp",
349                           "Unable to find exception class %s", className);
350       /* an exception, most likely ClassNotFoundException, will now be pending
351        */
352       goto end;
353     }
354 
355     jmethodID init = env->GetMethodID(exceptionClass, "<init>", ctorSig);
356     if (init == NULL) {
357       __android_log_print(ANDROID_LOG_ERROR, "JNIHelp",
358                           "Failed to find constructor for '%s' '%s'", className,
359                           ctorSig);
360       goto end;
361     }
362 
363     jobject instance = env->NewObjectV(exceptionClass, init, args);
364     if (instance == NULL) {
365       __android_log_print(ANDROID_LOG_ERROR, "JNIHelp",
366                           "Failed to construct '%s'", className);
367       goto end;
368     }
369 
370     if (env->Throw((jthrowable)instance) != JNI_OK) {
371       __android_log_print(ANDROID_LOG_ERROR, "JNIHelp", "Failed to throw '%s'",
372                           className);
373       /* an exception, most likely OOM, will now be pending */
374       goto end;
375     }
376 
377     /* everything worked fine, just update status to success and clean up */
378     status = 0;
379   }
380 
381 end:
382   va_end(args);
383   if (exceptionClass != NULL) {
384     env->DeleteLocalRef(exceptionClass);
385   }
386   return status;
387 }
388 
CreateExceptionMsg(JNIEnv * env,const char * msg)389 [[maybe_unused]] static jstring CreateExceptionMsg(JNIEnv* env,
390                                                    const char* msg) {
391   jstring detailMessage = env->NewStringUTF(msg);
392   if (detailMessage == NULL) {
393     /* Not really much we can do here. We're probably dead in the water,
394     but let's try to stumble on... */
395     env->ExceptionClear();
396   }
397   return detailMessage;
398 }
399 }  // namespace android::jnihelp
400 
401 /*
402  * Register one or more native methods with a particular class.  "className"
403  * looks like "java/lang/String". Aborts on failure, returns 0 on success.
404  */
jniRegisterNativeMethods(JNIEnv * env,const char * className,const JNINativeMethod * methods,int numMethods)405 [[maybe_unused]] static int jniRegisterNativeMethods(
406     JNIEnv* env,
407     const char* className,
408     const JNINativeMethod* methods,
409     int numMethods) {
410   using namespace android::jnihelp;
411   jclass clazz = env->FindClass(className);
412   if (clazz == NULL) {
413     __android_log_assert(
414         "clazz == NULL", "JNIHelp",
415         "Native registration unable to find class '%s'; aborting...",
416         className);
417   }
418   int result = env->RegisterNatives(clazz, methods, numMethods);
419   env->DeleteLocalRef(clazz);
420   if (result == 0) {
421     return 0;
422   }
423 
424   // Failure to register natives is fatal. Try to report the corresponding
425   // exception, otherwise abort with generic failure message.
426   jthrowable thrown = env->ExceptionOccurred();
427   if (thrown != NULL) {
428     struct ExpandableString summary;
429     ExpandableStringInitialize(&summary);
430     if (GetExceptionSummary(env, thrown, &summary)) {
431       __android_log_print(ANDROID_LOG_FATAL, "JNIHelp", "%s", summary.data);
432     }
433     ExpandableStringRelease(&summary);
434     env->DeleteLocalRef(thrown);
435   }
436   __android_log_print(ANDROID_LOG_FATAL, "JNIHelp",
437                       "RegisterNatives failed for '%s'; aborting...",
438                       className);
439   return result;
440 }
441 
442 /*
443  * Throw an exception with the specified class and an optional message.
444  *
445  * The "className" argument will be passed directly to FindClass, which
446  * takes strings with slashes (e.g. "java/lang/Object").
447  *
448  * If an exception is currently pending, we log a warning message and
449  * clear it.
450  *
451  * Returns 0 on success, nonzero if something failed (e.g. the exception
452  * class couldn't be found, so *an* exception will still be pending).
453  *
454  * Currently aborts the VM if it can't throw the exception.
455  */
jniThrowException(JNIEnv * env,const char * className,const char * msg)456 [[maybe_unused]] static int jniThrowException(JNIEnv* env,
457                                               const char* className,
458                                               const char* msg) {
459   using namespace android::jnihelp;
460   jstring _detailMessage = CreateExceptionMsg(env, msg);
461   int _status =
462       ThrowException(env, className, "(Ljava/lang/String;)V", _detailMessage);
463   if (_detailMessage != NULL) {
464     env->DeleteLocalRef(_detailMessage);
465   }
466   return _status;
467 }
468 
469 /*
470  * Throw an android.system.ErrnoException, with the given function name and
471  * errno value.
472  */
jniThrowErrnoException(JNIEnv * env,const char * functionName,int errnum)473 [[maybe_unused]] static int jniThrowErrnoException(JNIEnv* env,
474                                                    const char* functionName,
475                                                    int errnum) {
476   using namespace android::jnihelp;
477   jstring _detailMessage = CreateExceptionMsg(env, functionName);
478   int _status =
479       ThrowException(env, "android/system/ErrnoException",
480                      "(Ljava/lang/String;I)V", _detailMessage, errnum);
481   if (_detailMessage != NULL) {
482     env->DeleteLocalRef(_detailMessage);
483   }
484   return _status;
485 }
486 
487 /*
488  * Throw an exception with the specified class and formatted error message.
489  *
490  * The "className" argument will be passed directly to FindClass, which
491  * takes strings with slashes (e.g. "java/lang/Object").
492  *
493  * If an exception is currently pending, we log a warning message and
494  * clear it.
495  *
496  * Returns 0 on success, nonzero if something failed (e.g. the exception
497  * class couldn't be found, so *an* exception will still be pending).
498  *
499  * Currently aborts the VM if it can't throw the exception.
500  */
jniThrowExceptionFmt(JNIEnv * env,const char * className,const char * fmt,...)501 [[maybe_unused]] static int jniThrowExceptionFmt(JNIEnv* env,
502                                                  const char* className,
503                                                  const char* fmt,
504                                                  ...) {
505   va_list args;
506   va_start(args, fmt);
507   char msgBuf[512];
508   vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
509   va_end(args);
510   return jniThrowException(env, className, msgBuf);
511 }
512 
jniThrowNullPointerException(JNIEnv * env,const char * msg)513 [[maybe_unused]] static int jniThrowNullPointerException(JNIEnv* env,
514                                                          const char* msg) {
515   return jniThrowException(env, "java/lang/NullPointerException", msg);
516 }
517 
jniThrowRuntimeException(JNIEnv * env,const char * msg)518 [[maybe_unused]] static int jniThrowRuntimeException(JNIEnv* env,
519                                                      const char* msg) {
520   return jniThrowException(env, "java/lang/RuntimeException", msg);
521 }
522 
jniThrowIOException(JNIEnv * env,int errno_value)523 [[maybe_unused]] static int jniThrowIOException(JNIEnv* env, int errno_value) {
524   using namespace android::jnihelp;
525   char buffer[80];
526   const char* message = platformStrError(errno_value, buffer, sizeof(buffer));
527   return jniThrowException(env, "java/io/IOException", message);
528 }
529 
530 /*
531  * Returns a Java String object created from UTF-16 data either from jchar or,
532  * if called from C++11, char16_t (a bitwise identical distinct type).
533  */
534 [[maybe_unused]] static inline jstring
jniCreateString(JNIEnv * env,const jchar * unicodeChars,jsize len)535 jniCreateString(JNIEnv* env, const jchar* unicodeChars, jsize len) {
536   return env->NewString(unicodeChars, len);
537 }
538 
539 [[maybe_unused]] static inline jstring
jniCreateString(JNIEnv * env,const char16_t * unicodeChars,jsize len)540 jniCreateString(JNIEnv* env, const char16_t* unicodeChars, jsize len) {
541   return jniCreateString(env, reinterpret_cast<const jchar*>(unicodeChars),
542                          len);
543 }
544 
545 /*
546  * Log a message and an exception.
547  * If exception is NULL, logs the current exception in the JNI environment.
548  */
549 [[maybe_unused]] static void jniLogException(JNIEnv* env,
550                                              int priority,
551                                              const char* tag,
552                                              jthrowable exception = NULL) {
553   using namespace android::jnihelp;
554   struct ExpandableString summary;
555   ExpandableStringInitialize(&summary);
556   GetStackTraceOrSummary(env, exception, &summary);
557   const char* details =
558       (summary.data != NULL) ? summary.data : "No memory to report exception";
559   __android_log_write(priority, tag, details);
560   ExpandableStringRelease(&summary);
561 }
562 
563 #else  // defined(__cplusplus)
564 
565 // ART-internal only methods (not exported), exposed for legacy C users
566 
567 int jniRegisterNativeMethods(JNIEnv* env,
568                              const char* className,
569                              const JNINativeMethod* gMethods,
570                              int numMethods);
571 
572 void jniLogException(JNIEnv* env,
573                      int priority,
574                      const char* tag,
575                      jthrowable thrown);
576 
577 int jniThrowException(JNIEnv* env, const char* className, const char* msg);
578 
579 int jniThrowNullPointerException(JNIEnv* env, const char* msg);
580 
581 #endif  // defined(__cplusplus)
582 
583 #endif  // SRC_ANDROID_SDK_NATIVEHELPER_JNIHELP_H_
584