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