1*0797b24eSAndroid Build Coastguard Worker /*
2*0797b24eSAndroid Build Coastguard Worker * Copyright (C) 2006 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 #include "include/nativehelper/JNIHelp.h"
18*0797b24eSAndroid Build Coastguard Worker
19*0797b24eSAndroid Build Coastguard Worker #include <stdarg.h>
20*0797b24eSAndroid Build Coastguard Worker #include <stdbool.h>
21*0797b24eSAndroid Build Coastguard Worker #include <stdio.h>
22*0797b24eSAndroid Build Coastguard Worker #include <stdlib.h>
23*0797b24eSAndroid Build Coastguard Worker #include <string.h>
24*0797b24eSAndroid Build Coastguard Worker
25*0797b24eSAndroid Build Coastguard Worker #include <jni.h>
26*0797b24eSAndroid Build Coastguard Worker
27*0797b24eSAndroid Build Coastguard Worker #define LOG_TAG "JNIHelp"
28*0797b24eSAndroid Build Coastguard Worker #include "ALog-priv.h"
29*0797b24eSAndroid Build Coastguard Worker
30*0797b24eSAndroid Build Coastguard Worker #include "ExpandableString.h"
31*0797b24eSAndroid Build Coastguard Worker
32*0797b24eSAndroid Build Coastguard Worker //
33*0797b24eSAndroid Build Coastguard Worker // Helper methods
34*0797b24eSAndroid Build Coastguard Worker //
35*0797b24eSAndroid Build Coastguard Worker
platformStrError(int errnum,char * buf,size_t buflen)36*0797b24eSAndroid Build Coastguard Worker static const char* platformStrError(int errnum, char* buf, size_t buflen) {
37*0797b24eSAndroid Build Coastguard Worker #ifdef _WIN32
38*0797b24eSAndroid Build Coastguard Worker strerror_s(buf, buflen, errnum);
39*0797b24eSAndroid Build Coastguard Worker return buf;
40*0797b24eSAndroid Build Coastguard Worker #elif defined(__USE_GNU) && __ANDROID_API__ >= 23
41*0797b24eSAndroid Build Coastguard Worker // char *strerror_r(int errnum, char *buf, size_t buflen); /* GNU-specific */
42*0797b24eSAndroid Build Coastguard Worker return strerror_r(errnum, buf, buflen);
43*0797b24eSAndroid Build Coastguard Worker #else
44*0797b24eSAndroid Build Coastguard Worker // int strerror_r(int errnum, char *buf, size_t buflen); /* XSI-compliant */
45*0797b24eSAndroid Build Coastguard Worker int rc = strerror_r(errnum, buf, buflen);
46*0797b24eSAndroid Build Coastguard Worker if (rc != 0) {
47*0797b24eSAndroid Build Coastguard Worker snprintf(buf, buflen, "errno %d", errnum);
48*0797b24eSAndroid Build Coastguard Worker }
49*0797b24eSAndroid Build Coastguard Worker return buf;
50*0797b24eSAndroid Build Coastguard Worker #endif
51*0797b24eSAndroid Build Coastguard Worker }
52*0797b24eSAndroid Build Coastguard Worker
FindMethod(JNIEnv * env,const char * className,const char * methodName,const char * descriptor)53*0797b24eSAndroid Build Coastguard Worker static jmethodID FindMethod(JNIEnv* env,
54*0797b24eSAndroid Build Coastguard Worker const char* className,
55*0797b24eSAndroid Build Coastguard Worker const char* methodName,
56*0797b24eSAndroid Build Coastguard Worker const char* descriptor) {
57*0797b24eSAndroid Build Coastguard Worker // This method is only valid for classes in the core library which are
58*0797b24eSAndroid Build Coastguard Worker // not unloaded during the lifetime of managed code execution.
59*0797b24eSAndroid Build Coastguard Worker jclass clazz = (*env)->FindClass(env, className);
60*0797b24eSAndroid Build Coastguard Worker jmethodID methodId = (*env)->GetMethodID(env, clazz, methodName, descriptor);
61*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, clazz);
62*0797b24eSAndroid Build Coastguard Worker return methodId;
63*0797b24eSAndroid Build Coastguard Worker }
64*0797b24eSAndroid Build Coastguard Worker
AppendJString(JNIEnv * env,jstring text,struct ExpandableString * dst)65*0797b24eSAndroid Build Coastguard Worker static bool AppendJString(JNIEnv* env, jstring text, struct ExpandableString* dst) {
66*0797b24eSAndroid Build Coastguard Worker const char* utfText = (*env)->GetStringUTFChars(env, text, NULL);
67*0797b24eSAndroid Build Coastguard Worker if (utfText == NULL) {
68*0797b24eSAndroid Build Coastguard Worker return false;
69*0797b24eSAndroid Build Coastguard Worker }
70*0797b24eSAndroid Build Coastguard Worker bool success = ExpandableStringAppend(dst, utfText);
71*0797b24eSAndroid Build Coastguard Worker (*env)->ReleaseStringUTFChars(env, text, utfText);
72*0797b24eSAndroid Build Coastguard Worker return success;
73*0797b24eSAndroid Build Coastguard Worker }
74*0797b24eSAndroid Build Coastguard Worker
75*0797b24eSAndroid Build Coastguard Worker /*
76*0797b24eSAndroid Build Coastguard Worker * Returns a human-readable summary of an exception object. The buffer will
77*0797b24eSAndroid Build Coastguard Worker * be populated with the "binary" class name and, if present, the
78*0797b24eSAndroid Build Coastguard Worker * exception message.
79*0797b24eSAndroid Build Coastguard Worker */
GetExceptionSummary(JNIEnv * env,jthrowable thrown,struct ExpandableString * dst)80*0797b24eSAndroid Build Coastguard Worker static bool GetExceptionSummary(JNIEnv* env, jthrowable thrown, struct ExpandableString* dst) {
81*0797b24eSAndroid Build Coastguard Worker // Summary is <exception_class_name> ": " <exception_message>
82*0797b24eSAndroid Build Coastguard Worker jclass exceptionClass = (*env)->GetObjectClass(env, thrown); // Always succeeds
83*0797b24eSAndroid Build Coastguard Worker jmethodID getName = FindMethod(env, "java/lang/Class", "getName", "()Ljava/lang/String;");
84*0797b24eSAndroid Build Coastguard Worker jstring className = (jstring) (*env)->CallObjectMethod(env, exceptionClass, getName);
85*0797b24eSAndroid Build Coastguard Worker if (className == NULL) {
86*0797b24eSAndroid Build Coastguard Worker ExpandableStringAssign(dst, "<error getting class name>");
87*0797b24eSAndroid Build Coastguard Worker (*env)->ExceptionClear(env);
88*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, exceptionClass);
89*0797b24eSAndroid Build Coastguard Worker return false;
90*0797b24eSAndroid Build Coastguard Worker }
91*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, exceptionClass);
92*0797b24eSAndroid Build Coastguard Worker exceptionClass = NULL;
93*0797b24eSAndroid Build Coastguard Worker
94*0797b24eSAndroid Build Coastguard Worker if (!AppendJString(env, className, dst)) {
95*0797b24eSAndroid Build Coastguard Worker ExpandableStringAssign(dst, "<error getting class name UTF-8>");
96*0797b24eSAndroid Build Coastguard Worker (*env)->ExceptionClear(env);
97*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, className);
98*0797b24eSAndroid Build Coastguard Worker return false;
99*0797b24eSAndroid Build Coastguard Worker }
100*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, className);
101*0797b24eSAndroid Build Coastguard Worker className = NULL;
102*0797b24eSAndroid Build Coastguard Worker
103*0797b24eSAndroid Build Coastguard Worker bool success = false;
104*0797b24eSAndroid Build Coastguard Worker jmethodID getMessage =
105*0797b24eSAndroid Build Coastguard Worker FindMethod(env, "java/lang/Throwable", "getMessage", "()Ljava/lang/String;");
106*0797b24eSAndroid Build Coastguard Worker jstring message = (jstring) (*env)->CallObjectMethod(env, thrown, getMessage);
107*0797b24eSAndroid Build Coastguard Worker if (message != NULL) {
108*0797b24eSAndroid Build Coastguard Worker success = (ExpandableStringAppend(dst, ": ") && AppendJString(env, message, dst));
109*0797b24eSAndroid Build Coastguard Worker } else if ((*env)->ExceptionOccurred(env) == NULL) {
110*0797b24eSAndroid Build Coastguard Worker success = true;
111*0797b24eSAndroid Build Coastguard Worker }
112*0797b24eSAndroid Build Coastguard Worker
113*0797b24eSAndroid Build Coastguard Worker if (!success) {
114*0797b24eSAndroid Build Coastguard Worker // Two potential reasons for reaching here:
115*0797b24eSAndroid Build Coastguard Worker //
116*0797b24eSAndroid Build Coastguard Worker // 1. managed heap allocation failure (OOME).
117*0797b24eSAndroid Build Coastguard Worker // 2. native heap allocation failure for the storage in |dst|.
118*0797b24eSAndroid Build Coastguard Worker //
119*0797b24eSAndroid Build Coastguard Worker // Attempt to append failure notification, okay to fail, |dst| contains the class name
120*0797b24eSAndroid Build Coastguard Worker // of |thrown|.
121*0797b24eSAndroid Build Coastguard Worker ExpandableStringAppend(dst, "<error getting message>");
122*0797b24eSAndroid Build Coastguard Worker // Clear OOME if present.
123*0797b24eSAndroid Build Coastguard Worker (*env)->ExceptionClear(env);
124*0797b24eSAndroid Build Coastguard Worker }
125*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, message);
126*0797b24eSAndroid Build Coastguard Worker message = NULL;
127*0797b24eSAndroid Build Coastguard Worker return success;
128*0797b24eSAndroid Build Coastguard Worker }
129*0797b24eSAndroid Build Coastguard Worker
NewStringWriter(JNIEnv * env)130*0797b24eSAndroid Build Coastguard Worker static jobject NewStringWriter(JNIEnv* env) {
131*0797b24eSAndroid Build Coastguard Worker jclass clazz = (*env)->FindClass(env, "java/io/StringWriter");
132*0797b24eSAndroid Build Coastguard Worker jmethodID init = (*env)->GetMethodID(env, clazz, "<init>", "()V");
133*0797b24eSAndroid Build Coastguard Worker jobject instance = (*env)->NewObject(env, clazz, init);
134*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, clazz);
135*0797b24eSAndroid Build Coastguard Worker return instance;
136*0797b24eSAndroid Build Coastguard Worker }
137*0797b24eSAndroid Build Coastguard Worker
StringWriterToString(JNIEnv * env,jobject stringWriter)138*0797b24eSAndroid Build Coastguard Worker static jstring StringWriterToString(JNIEnv* env, jobject stringWriter) {
139*0797b24eSAndroid Build Coastguard Worker jmethodID toString =
140*0797b24eSAndroid Build Coastguard Worker FindMethod(env, "java/io/StringWriter", "toString", "()Ljava/lang/String;");
141*0797b24eSAndroid Build Coastguard Worker return (jstring) (*env)->CallObjectMethod(env, stringWriter, toString);
142*0797b24eSAndroid Build Coastguard Worker }
143*0797b24eSAndroid Build Coastguard Worker
NewPrintWriter(JNIEnv * env,jobject writer)144*0797b24eSAndroid Build Coastguard Worker static jobject NewPrintWriter(JNIEnv* env, jobject writer) {
145*0797b24eSAndroid Build Coastguard Worker jclass clazz = (*env)->FindClass(env, "java/io/PrintWriter");
146*0797b24eSAndroid Build Coastguard Worker jmethodID init = (*env)->GetMethodID(env, clazz, "<init>", "(Ljava/io/Writer;)V");
147*0797b24eSAndroid Build Coastguard Worker jobject instance = (*env)->NewObject(env, clazz, init, writer);
148*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, clazz);
149*0797b24eSAndroid Build Coastguard Worker return instance;
150*0797b24eSAndroid Build Coastguard Worker }
151*0797b24eSAndroid Build Coastguard Worker
GetStackTrace(JNIEnv * env,jthrowable thrown,struct ExpandableString * dst)152*0797b24eSAndroid Build Coastguard Worker static bool GetStackTrace(JNIEnv* env, jthrowable thrown, struct ExpandableString* dst) {
153*0797b24eSAndroid Build Coastguard Worker // This function is equivalent to the following Java snippet:
154*0797b24eSAndroid Build Coastguard Worker // StringWriter sw = new StringWriter();
155*0797b24eSAndroid Build Coastguard Worker // PrintWriter pw = new PrintWriter(sw);
156*0797b24eSAndroid Build Coastguard Worker // thrown.printStackTrace(pw);
157*0797b24eSAndroid Build Coastguard Worker // String trace = sw.toString();
158*0797b24eSAndroid Build Coastguard Worker // return trace;
159*0797b24eSAndroid Build Coastguard Worker jobject sw = NewStringWriter(env);
160*0797b24eSAndroid Build Coastguard Worker if (sw == NULL) {
161*0797b24eSAndroid Build Coastguard Worker return false;
162*0797b24eSAndroid Build Coastguard Worker }
163*0797b24eSAndroid Build Coastguard Worker
164*0797b24eSAndroid Build Coastguard Worker jobject pw = NewPrintWriter(env, sw);
165*0797b24eSAndroid Build Coastguard Worker if (pw == NULL) {
166*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, sw);
167*0797b24eSAndroid Build Coastguard Worker return false;
168*0797b24eSAndroid Build Coastguard Worker }
169*0797b24eSAndroid Build Coastguard Worker
170*0797b24eSAndroid Build Coastguard Worker jmethodID printStackTrace =
171*0797b24eSAndroid Build Coastguard Worker FindMethod(env, "java/lang/Throwable", "printStackTrace", "(Ljava/io/PrintWriter;)V");
172*0797b24eSAndroid Build Coastguard Worker (*env)->CallVoidMethod(env, thrown, printStackTrace, pw);
173*0797b24eSAndroid Build Coastguard Worker
174*0797b24eSAndroid Build Coastguard Worker jstring trace = ((*env)->ExceptionOccurred(env) != NULL) ? NULL : StringWriterToString(env, sw);
175*0797b24eSAndroid Build Coastguard Worker
176*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, pw);
177*0797b24eSAndroid Build Coastguard Worker pw = NULL;
178*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, sw);
179*0797b24eSAndroid Build Coastguard Worker sw = NULL;
180*0797b24eSAndroid Build Coastguard Worker
181*0797b24eSAndroid Build Coastguard Worker if (trace == NULL) {
182*0797b24eSAndroid Build Coastguard Worker return false;
183*0797b24eSAndroid Build Coastguard Worker }
184*0797b24eSAndroid Build Coastguard Worker
185*0797b24eSAndroid Build Coastguard Worker bool success = AppendJString(env, trace, dst);
186*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, trace);
187*0797b24eSAndroid Build Coastguard Worker return success;
188*0797b24eSAndroid Build Coastguard Worker }
189*0797b24eSAndroid Build Coastguard Worker
GetStackTraceOrSummary(JNIEnv * env,jthrowable thrown,struct ExpandableString * dst)190*0797b24eSAndroid Build Coastguard Worker static void GetStackTraceOrSummary(JNIEnv* env, jthrowable thrown, struct ExpandableString* dst) {
191*0797b24eSAndroid Build Coastguard Worker // This method attempts to get a stack trace or summary info for an exception.
192*0797b24eSAndroid Build Coastguard Worker // The exception may be provided in the |thrown| argument to this function.
193*0797b24eSAndroid Build Coastguard Worker // If |thrown| is NULL, then any pending exception is used if it exists.
194*0797b24eSAndroid Build Coastguard Worker
195*0797b24eSAndroid Build Coastguard Worker // Save pending exception, callees may raise other exceptions. Any pending exception is
196*0797b24eSAndroid Build Coastguard Worker // rethrown when this function exits.
197*0797b24eSAndroid Build Coastguard Worker jthrowable pendingException = (*env)->ExceptionOccurred(env);
198*0797b24eSAndroid Build Coastguard Worker if (pendingException != NULL) {
199*0797b24eSAndroid Build Coastguard Worker (*env)->ExceptionClear(env);
200*0797b24eSAndroid Build Coastguard Worker }
201*0797b24eSAndroid Build Coastguard Worker
202*0797b24eSAndroid Build Coastguard Worker if (thrown == NULL) {
203*0797b24eSAndroid Build Coastguard Worker if (pendingException == NULL) {
204*0797b24eSAndroid Build Coastguard Worker ExpandableStringAssign(dst, "<no pending exception>");
205*0797b24eSAndroid Build Coastguard Worker return;
206*0797b24eSAndroid Build Coastguard Worker }
207*0797b24eSAndroid Build Coastguard Worker thrown = pendingException;
208*0797b24eSAndroid Build Coastguard Worker }
209*0797b24eSAndroid Build Coastguard Worker
210*0797b24eSAndroid Build Coastguard Worker if (!GetStackTrace(env, thrown, dst)) {
211*0797b24eSAndroid Build Coastguard Worker // GetStackTrace may have raised an exception, clear it since it's not for the caller.
212*0797b24eSAndroid Build Coastguard Worker (*env)->ExceptionClear(env);
213*0797b24eSAndroid Build Coastguard Worker GetExceptionSummary(env, thrown, dst);
214*0797b24eSAndroid Build Coastguard Worker }
215*0797b24eSAndroid Build Coastguard Worker
216*0797b24eSAndroid Build Coastguard Worker if (pendingException != NULL) {
217*0797b24eSAndroid Build Coastguard Worker // Re-throw the pending exception present when this method was called.
218*0797b24eSAndroid Build Coastguard Worker (*env)->Throw(env, pendingException);
219*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, pendingException);
220*0797b24eSAndroid Build Coastguard Worker }
221*0797b24eSAndroid Build Coastguard Worker }
222*0797b24eSAndroid Build Coastguard Worker
DiscardPendingException(JNIEnv * env,const char * className)223*0797b24eSAndroid Build Coastguard Worker static void DiscardPendingException(JNIEnv* env, const char* className) {
224*0797b24eSAndroid Build Coastguard Worker jthrowable exception = (*env)->ExceptionOccurred(env);
225*0797b24eSAndroid Build Coastguard Worker (*env)->ExceptionClear(env);
226*0797b24eSAndroid Build Coastguard Worker if (exception == NULL) {
227*0797b24eSAndroid Build Coastguard Worker return;
228*0797b24eSAndroid Build Coastguard Worker }
229*0797b24eSAndroid Build Coastguard Worker
230*0797b24eSAndroid Build Coastguard Worker struct ExpandableString summary;
231*0797b24eSAndroid Build Coastguard Worker ExpandableStringInitialize(&summary);
232*0797b24eSAndroid Build Coastguard Worker GetExceptionSummary(env, exception, &summary);
233*0797b24eSAndroid Build Coastguard Worker const char* details = (summary.data != NULL) ? summary.data : "Unknown";
234*0797b24eSAndroid Build Coastguard Worker ALOGW("Discarding pending exception (%s) to throw %s", details, className);
235*0797b24eSAndroid Build Coastguard Worker ExpandableStringRelease(&summary);
236*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, exception);
237*0797b24eSAndroid Build Coastguard Worker }
238*0797b24eSAndroid Build Coastguard Worker
ThrowException(JNIEnv * env,const char * className,const char * ctorSig,...)239*0797b24eSAndroid Build Coastguard Worker static int ThrowException(JNIEnv* env, const char* className, const char* ctorSig, ...) {
240*0797b24eSAndroid Build Coastguard Worker int status = -1;
241*0797b24eSAndroid Build Coastguard Worker jclass exceptionClass = NULL;
242*0797b24eSAndroid Build Coastguard Worker
243*0797b24eSAndroid Build Coastguard Worker va_list args;
244*0797b24eSAndroid Build Coastguard Worker va_start(args, ctorSig);
245*0797b24eSAndroid Build Coastguard Worker
246*0797b24eSAndroid Build Coastguard Worker DiscardPendingException(env, className);
247*0797b24eSAndroid Build Coastguard Worker
248*0797b24eSAndroid Build Coastguard Worker {
249*0797b24eSAndroid Build Coastguard Worker /* We want to clean up local references before returning from this function, so,
250*0797b24eSAndroid Build Coastguard Worker * regardless of return status, the end block must run. Have the work done in a
251*0797b24eSAndroid Build Coastguard Worker * nested block to avoid using any uninitialized variables in the end block. */
252*0797b24eSAndroid Build Coastguard Worker exceptionClass = (*env)->FindClass(env, className);
253*0797b24eSAndroid Build Coastguard Worker if (exceptionClass == NULL) {
254*0797b24eSAndroid Build Coastguard Worker ALOGE("Unable to find exception class %s", className);
255*0797b24eSAndroid Build Coastguard Worker /* an exception, most likely ClassNotFoundException, will now be pending */
256*0797b24eSAndroid Build Coastguard Worker goto end;
257*0797b24eSAndroid Build Coastguard Worker }
258*0797b24eSAndroid Build Coastguard Worker
259*0797b24eSAndroid Build Coastguard Worker jmethodID init = (*env)->GetMethodID(env, exceptionClass, "<init>", ctorSig);
260*0797b24eSAndroid Build Coastguard Worker if(init == NULL) {
261*0797b24eSAndroid Build Coastguard Worker ALOGE("Failed to find constructor for '%s' '%s'", className, ctorSig);
262*0797b24eSAndroid Build Coastguard Worker goto end;
263*0797b24eSAndroid Build Coastguard Worker }
264*0797b24eSAndroid Build Coastguard Worker
265*0797b24eSAndroid Build Coastguard Worker jobject instance = (*env)->NewObjectV(env, exceptionClass, init, args);
266*0797b24eSAndroid Build Coastguard Worker if (instance == NULL) {
267*0797b24eSAndroid Build Coastguard Worker ALOGE("Failed to construct '%s'", className);
268*0797b24eSAndroid Build Coastguard Worker goto end;
269*0797b24eSAndroid Build Coastguard Worker }
270*0797b24eSAndroid Build Coastguard Worker
271*0797b24eSAndroid Build Coastguard Worker if ((*env)->Throw(env, (jthrowable)instance) != JNI_OK) {
272*0797b24eSAndroid Build Coastguard Worker ALOGE("Failed to throw '%s'", className);
273*0797b24eSAndroid Build Coastguard Worker /* an exception, most likely OOM, will now be pending */
274*0797b24eSAndroid Build Coastguard Worker goto end;
275*0797b24eSAndroid Build Coastguard Worker }
276*0797b24eSAndroid Build Coastguard Worker
277*0797b24eSAndroid Build Coastguard Worker /* everything worked fine, just update status to success and clean up */
278*0797b24eSAndroid Build Coastguard Worker status = 0;
279*0797b24eSAndroid Build Coastguard Worker }
280*0797b24eSAndroid Build Coastguard Worker
281*0797b24eSAndroid Build Coastguard Worker end:
282*0797b24eSAndroid Build Coastguard Worker va_end(args);
283*0797b24eSAndroid Build Coastguard Worker if (exceptionClass != NULL) {
284*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, exceptionClass);
285*0797b24eSAndroid Build Coastguard Worker }
286*0797b24eSAndroid Build Coastguard Worker return status;
287*0797b24eSAndroid Build Coastguard Worker }
288*0797b24eSAndroid Build Coastguard Worker
CreateExceptionMsg(JNIEnv * env,const char * msg)289*0797b24eSAndroid Build Coastguard Worker static jstring CreateExceptionMsg(JNIEnv* env, const char* msg) {
290*0797b24eSAndroid Build Coastguard Worker jstring detailMessage = (*env)->NewStringUTF(env, msg);
291*0797b24eSAndroid Build Coastguard Worker if (detailMessage == NULL) {
292*0797b24eSAndroid Build Coastguard Worker /* Not really much we can do here. We're probably dead in the water,
293*0797b24eSAndroid Build Coastguard Worker but let's try to stumble on... */
294*0797b24eSAndroid Build Coastguard Worker (*env)->ExceptionClear(env);
295*0797b24eSAndroid Build Coastguard Worker }
296*0797b24eSAndroid Build Coastguard Worker return detailMessage;
297*0797b24eSAndroid Build Coastguard Worker }
298*0797b24eSAndroid Build Coastguard Worker
299*0797b24eSAndroid Build Coastguard Worker /* Helper macro to deal with conversion of the exception message from a C string
300*0797b24eSAndroid Build Coastguard Worker * to jstring.
301*0797b24eSAndroid Build Coastguard Worker *
302*0797b24eSAndroid Build Coastguard Worker * This is useful because most exceptions have a message as the first parameter
303*0797b24eSAndroid Build Coastguard Worker * and delegating the conversion to all the callers of ThrowException results in
304*0797b24eSAndroid Build Coastguard Worker * code duplication. However, since we try to allow variable number of arguments
305*0797b24eSAndroid Build Coastguard Worker * for the exception constructor we'd either need to do the conversion inside
306*0797b24eSAndroid Build Coastguard Worker * the macro, or manipulate the va_list to replace the C string to a jstring.
307*0797b24eSAndroid Build Coastguard Worker * This seems like the cleaner solution.
308*0797b24eSAndroid Build Coastguard Worker */
309*0797b24eSAndroid Build Coastguard Worker #define THROW_EXCEPTION_WITH_MESSAGE(env, className, ctorSig, msg, ...) ({ \
310*0797b24eSAndroid Build Coastguard Worker jstring _detailMessage = CreateExceptionMsg(env, msg); \
311*0797b24eSAndroid Build Coastguard Worker int _status = ThrowException(env, className, ctorSig, _detailMessage, ## __VA_ARGS__); \
312*0797b24eSAndroid Build Coastguard Worker if (_detailMessage != NULL) { \
313*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, _detailMessage); \
314*0797b24eSAndroid Build Coastguard Worker } \
315*0797b24eSAndroid Build Coastguard Worker _status; })
316*0797b24eSAndroid Build Coastguard Worker
317*0797b24eSAndroid Build Coastguard Worker //
318*0797b24eSAndroid Build Coastguard Worker // JNIHelp external API
319*0797b24eSAndroid Build Coastguard Worker //
320*0797b24eSAndroid Build Coastguard Worker
jniRegisterNativeMethods(JNIEnv * env,const char * className,const JNINativeMethod * methods,int numMethods)321*0797b24eSAndroid Build Coastguard Worker int jniRegisterNativeMethods(JNIEnv* env, const char* className,
322*0797b24eSAndroid Build Coastguard Worker const JNINativeMethod* methods, int numMethods)
323*0797b24eSAndroid Build Coastguard Worker {
324*0797b24eSAndroid Build Coastguard Worker ALOGV("Registering %s's %d native methods...", className, numMethods);
325*0797b24eSAndroid Build Coastguard Worker jclass clazz = (*env)->FindClass(env, className);
326*0797b24eSAndroid Build Coastguard Worker ALOG_ALWAYS_FATAL_IF(clazz == NULL,
327*0797b24eSAndroid Build Coastguard Worker "Native registration unable to find class '%s'; aborting...",
328*0797b24eSAndroid Build Coastguard Worker className);
329*0797b24eSAndroid Build Coastguard Worker int result = (*env)->RegisterNatives(env, clazz, methods, numMethods);
330*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, clazz);
331*0797b24eSAndroid Build Coastguard Worker if (result == 0) {
332*0797b24eSAndroid Build Coastguard Worker return 0;
333*0797b24eSAndroid Build Coastguard Worker }
334*0797b24eSAndroid Build Coastguard Worker
335*0797b24eSAndroid Build Coastguard Worker // Failure to register natives is fatal. Try to report the corresponding exception,
336*0797b24eSAndroid Build Coastguard Worker // otherwise abort with generic failure message.
337*0797b24eSAndroid Build Coastguard Worker jthrowable thrown = (*env)->ExceptionOccurred(env);
338*0797b24eSAndroid Build Coastguard Worker if (thrown != NULL) {
339*0797b24eSAndroid Build Coastguard Worker struct ExpandableString summary;
340*0797b24eSAndroid Build Coastguard Worker ExpandableStringInitialize(&summary);
341*0797b24eSAndroid Build Coastguard Worker if (GetExceptionSummary(env, thrown, &summary)) {
342*0797b24eSAndroid Build Coastguard Worker ALOGF("%s", summary.data);
343*0797b24eSAndroid Build Coastguard Worker }
344*0797b24eSAndroid Build Coastguard Worker ExpandableStringRelease(&summary);
345*0797b24eSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, thrown);
346*0797b24eSAndroid Build Coastguard Worker }
347*0797b24eSAndroid Build Coastguard Worker ALOGF("RegisterNatives failed for '%s'; aborting...", className);
348*0797b24eSAndroid Build Coastguard Worker return result;
349*0797b24eSAndroid Build Coastguard Worker }
350*0797b24eSAndroid Build Coastguard Worker
jniLogException(JNIEnv * env,int priority,const char * tag,jthrowable thrown)351*0797b24eSAndroid Build Coastguard Worker void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable thrown) {
352*0797b24eSAndroid Build Coastguard Worker struct ExpandableString summary;
353*0797b24eSAndroid Build Coastguard Worker ExpandableStringInitialize(&summary);
354*0797b24eSAndroid Build Coastguard Worker GetStackTraceOrSummary(env, thrown, &summary);
355*0797b24eSAndroid Build Coastguard Worker const char* details = (summary.data != NULL) ? summary.data : "No memory to report exception";
356*0797b24eSAndroid Build Coastguard Worker __android_log_write(priority, tag, details);
357*0797b24eSAndroid Build Coastguard Worker ExpandableStringRelease(&summary);
358*0797b24eSAndroid Build Coastguard Worker }
359*0797b24eSAndroid Build Coastguard Worker
jniThrowException(JNIEnv * env,const char * className,const char * message)360*0797b24eSAndroid Build Coastguard Worker int jniThrowException(JNIEnv* env, const char* className, const char* message) {
361*0797b24eSAndroid Build Coastguard Worker return THROW_EXCEPTION_WITH_MESSAGE(env, className, "(Ljava/lang/String;)V", message);
362*0797b24eSAndroid Build Coastguard Worker }
363*0797b24eSAndroid Build Coastguard Worker
jniThrowExceptionFmt(JNIEnv * env,const char * className,const char * fmt,va_list args)364*0797b24eSAndroid Build Coastguard Worker int jniThrowExceptionFmt(JNIEnv* env, const char* className, const char* fmt, va_list args) {
365*0797b24eSAndroid Build Coastguard Worker char msgBuf[512];
366*0797b24eSAndroid Build Coastguard Worker vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
367*0797b24eSAndroid Build Coastguard Worker return jniThrowException(env, className, msgBuf);
368*0797b24eSAndroid Build Coastguard Worker }
369*0797b24eSAndroid Build Coastguard Worker
jniThrowNullPointerException(JNIEnv * env,const char * msg)370*0797b24eSAndroid Build Coastguard Worker int jniThrowNullPointerException(JNIEnv* env, const char* msg) {
371*0797b24eSAndroid Build Coastguard Worker return jniThrowException(env, "java/lang/NullPointerException", msg);
372*0797b24eSAndroid Build Coastguard Worker }
373*0797b24eSAndroid Build Coastguard Worker
jniThrowRuntimeException(JNIEnv * env,const char * msg)374*0797b24eSAndroid Build Coastguard Worker int jniThrowRuntimeException(JNIEnv* env, const char* msg) {
375*0797b24eSAndroid Build Coastguard Worker return jniThrowException(env, "java/lang/RuntimeException", msg);
376*0797b24eSAndroid Build Coastguard Worker }
377*0797b24eSAndroid Build Coastguard Worker
jniThrowIOException(JNIEnv * env,int errno_value)378*0797b24eSAndroid Build Coastguard Worker int jniThrowIOException(JNIEnv* env, int errno_value) {
379*0797b24eSAndroid Build Coastguard Worker char buffer[80];
380*0797b24eSAndroid Build Coastguard Worker const char* message = platformStrError(errno_value, buffer, sizeof(buffer));
381*0797b24eSAndroid Build Coastguard Worker return jniThrowException(env, "java/io/IOException", message);
382*0797b24eSAndroid Build Coastguard Worker }
383*0797b24eSAndroid Build Coastguard Worker
jniThrowErrnoException(JNIEnv * env,const char * functionName,int errno_value)384*0797b24eSAndroid Build Coastguard Worker int jniThrowErrnoException(JNIEnv* env, const char* functionName, int errno_value) {
385*0797b24eSAndroid Build Coastguard Worker return THROW_EXCEPTION_WITH_MESSAGE(env, "android/system/ErrnoException",
386*0797b24eSAndroid Build Coastguard Worker "(Ljava/lang/String;I)V", functionName, errno_value);
387*0797b24eSAndroid Build Coastguard Worker }
388*0797b24eSAndroid Build Coastguard Worker
jniCreateString(JNIEnv * env,const jchar * unicodeChars,jsize len)389*0797b24eSAndroid Build Coastguard Worker jstring jniCreateString(JNIEnv* env, const jchar* unicodeChars, jsize len) {
390*0797b24eSAndroid Build Coastguard Worker return (*env)->NewString(env, unicodeChars, len);
391*0797b24eSAndroid Build Coastguard Worker }
392