/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* JNI helper functions.
*/
#define LOG_TAG "JNIHelp"
#include "JNIHelp.h"
#include "utils/Log.h"
#include <string.h>
#include <assert.h>
/*
* Register native JNI-callable methods.
*
* "className" looks like "java/lang/String".
*/
int jniRegisterNativeMethods(JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
LOGV("Registering %s natives\n", className);
clazz = (*env)->FindClass(env, className);
if (clazz == NULL) {
LOGE("Native registration unable to find class '%s'\n", className);
return -1;
}
if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
LOGE("RegisterNatives failed for '%s'\n", className);
return -1;
}
return 0;
}
/*
* Get a human-readable summary of an exception object. The buffer will
* be populated with the "binary" class name and, if present, the
* exception message.
*/
static void getExceptionSummary(JNIEnv* env, jthrowable excep, char* buf,
size_t bufLen)
{
if (excep == NULL)
return;
/* get the name of the exception's class; none of these should fail */
jclass clazz = (*env)->GetObjectClass(env, excep); // exception's class
jclass jlc = (*env)->GetObjectClass(env, clazz); // java.lang.Class
jmethodID getNameMethod =
(*env)->GetMethodID(env, jlc, "getName", "()Ljava/lang/String;");
jstring className = (*env)->CallObjectMethod(env, clazz, getNameMethod);
/* get printable string */
const char* nameStr = (*env)->GetStringUTFChars(env, className, NULL);
if (nameStr == NULL) {
snprintf(buf, bufLen, "%s", "out of memory generating summary");
(*env)->ExceptionClear(env); // clear OOM
return;
}
/* if the exception has a message string, get that */
jmethodID getThrowableMessage =
(*env)->GetMethodID(env, clazz, "getMessage", "()Ljava/lang/String;");
jstring message = (*env)->CallObjectMethod(env, excep, getThrowableMessage);
if (message != NULL) {
const char* messageStr = (*env)->GetStringUTFChars(env, message, NULL);
snprintf(buf, bufLen, "%s: %s", nameStr, messageStr);
if (messageStr != NULL)
(*env)->ReleaseStringUTFChars(env, message, messageStr);
else
(*env)->ExceptionClear(env); // clear OOM
} else {
strncpy(buf, nameStr, bufLen);
buf[bufLen-1] = '\0';
}
(*env)->ReleaseStringUTFChars(env, className, nameStr);
}
/*
* Throw an exception with the specified class and an optional message.
*
* If an exception is currently pending, we log a warning message and
* clear it.
*
* Returns 0 if the specified exception was successfully thrown. (Some
* sort of exception will always be pending when this returns.)
*/
int jniThrowException(JNIEnv* env, const char* className, const char* msg)
{
jclass exceptionClass;
if ((*env)->ExceptionCheck(env)) {
/* TODO: consider creating the new exception with this as "cause" */
char buf[256];
jthrowable excep = (*env)->ExceptionOccurred(env);
(*env)->ExceptionClear(env);
getExceptionSummary(env, excep, buf, sizeof(buf));
LOGW("Discarding pending exception (%s) to throw %s\n",
buf, className);
}
exceptionClass = (*env)->FindClass(env, className);
if (exceptionClass == NULL) {
LOGE("Unable to find exception class %s\n", className);
/* ClassNotFoundException now pending */
return -1;
}
if ((*env)->ThrowNew(env, exceptionClass, msg) != JNI_OK) {
LOGE("Failed throwing '%s' '%s'\n", className, msg);
/* an exception, most likely OOM, will now be pending */
return -1;
}
return 0;
}
/*
* Throw a java.lang.NullPointerException, with an optional message.
*/
int jniThrowNullPointerException(JNIEnv* env, const char* msg)
{
return jniThrowException(env, "java/lang/NullPointerException", msg);
}
/*
* Throw a java.lang.RuntimeException, with an optional message.
*/
int jniThrowRuntimeException(JNIEnv* env, const char* msg)
{
return jniThrowException(env, "java/lang/RuntimeException", msg);
}
/*
* Throw a java.io.IOException, generating the message from errno.
*/
int jniThrowIOException(JNIEnv* env, int errnum)
{
char buffer[80];
const char* message = jniStrError(errnum, buffer, sizeof(buffer));
return jniThrowException(env, "java/io/IOException", message);
}
const char* jniStrError(int errnum, char* buf, size_t buflen)
{
// note: glibc has a nonstandard strerror_r that returns char* rather
// than POSIX's int.
// char *strerror_r(int errnum, char *buf, size_t n);
char* ret = (char*) strerror_r(errnum, buf, buflen);
if (((int)ret) == 0) {
//POSIX strerror_r, success
return buf;
} else if (((int)ret) == -1) {
//POSIX strerror_r, failure
// (Strictly, POSIX only guarantees a value other than 0. The safest
// way to implement this function is to use C++ and overload on the
// type of strerror_r to accurately distinguish GNU from POSIX. But
// realistic implementations will always return -1.)
snprintf(buf, buflen, "errno %d", errnum);
return buf;
} else {
//glibc strerror_r returning a string
return ret;
}
}