You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
407 lines
14 KiB
407 lines
14 KiB
/* |
|
* 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. |
|
*/ |
|
|
|
#define LOG_TAG "JNIHelp" |
|
|
|
#include "nativehelper/JNIHelp.h" |
|
|
|
#include "ALog-priv.h" |
|
|
|
#include <string> |
|
|
|
#include "JniConstants.h" |
|
#include "nativehelper/ScopedLocalRef.h" |
|
|
|
/** |
|
* Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.) |
|
*/ |
|
template<typename T> |
|
class scoped_local_ref { |
|
public: |
|
explicit scoped_local_ref(C_JNIEnv* env, T localRef = NULL) |
|
: mEnv(env), mLocalRef(localRef) |
|
{ |
|
} |
|
|
|
~scoped_local_ref() { |
|
reset(); |
|
} |
|
|
|
void reset(T localRef = NULL) { |
|
if (mLocalRef != NULL) { |
|
(*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef); |
|
mLocalRef = localRef; |
|
} |
|
} |
|
|
|
T get() const { |
|
return mLocalRef; |
|
} |
|
|
|
private: |
|
C_JNIEnv* const mEnv; |
|
T mLocalRef; |
|
|
|
DISALLOW_COPY_AND_ASSIGN(scoped_local_ref); |
|
}; |
|
|
|
static jclass findClass(C_JNIEnv* env, const char* className) { |
|
JNIEnv* e = reinterpret_cast<JNIEnv*>(env); |
|
return (*env)->FindClass(e, className); |
|
} |
|
|
|
MODULE_API int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, |
|
const JNINativeMethod* gMethods, int numMethods) |
|
{ |
|
JNIEnv* e = reinterpret_cast<JNIEnv*>(env); |
|
|
|
ALOGV("Registering %s's %d native methods...", className, numMethods); |
|
|
|
scoped_local_ref<jclass> c(env, findClass(env, className)); |
|
ALOG_ALWAYS_FATAL_IF(c.get() == NULL, |
|
"Native registration unable to find class '%s'; aborting...", |
|
className); |
|
|
|
int result = e->RegisterNatives(c.get(), gMethods, numMethods); |
|
ALOG_ALWAYS_FATAL_IF(result < 0, "RegisterNatives failed for '%s'; aborting...", |
|
className); |
|
|
|
return 0; |
|
} |
|
|
|
/* |
|
* Returns 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 bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) { |
|
JNIEnv* e = reinterpret_cast<JNIEnv*>(env); |
|
|
|
/* get the name of the exception's class */ |
|
scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail |
|
scoped_local_ref<jclass> classClass(env, |
|
(*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail |
|
jmethodID classGetNameMethod = |
|
(*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;"); |
|
scoped_local_ref<jstring> classNameStr(env, |
|
(jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod)); |
|
if (classNameStr.get() == NULL) { |
|
(*env)->ExceptionClear(e); |
|
result = "<error getting class name>"; |
|
return false; |
|
} |
|
const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL); |
|
if (classNameChars == NULL) { |
|
(*env)->ExceptionClear(e); |
|
result = "<error getting class name UTF-8>"; |
|
return false; |
|
} |
|
result += classNameChars; |
|
(*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars); |
|
|
|
/* if the exception has a detail message, get that */ |
|
jmethodID getMessage = |
|
(*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;"); |
|
scoped_local_ref<jstring> messageStr(env, |
|
(jstring) (*env)->CallObjectMethod(e, exception, getMessage)); |
|
if (messageStr.get() == NULL) { |
|
return true; |
|
} |
|
|
|
result += ": "; |
|
|
|
const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL); |
|
if (messageChars != NULL) { |
|
result += messageChars; |
|
(*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars); |
|
} else { |
|
result += "<error getting message>"; |
|
(*env)->ExceptionClear(e); // clear OOM |
|
} |
|
|
|
return true; |
|
} |
|
|
|
/* |
|
* Returns an exception (with stack trace) as a string. |
|
*/ |
|
static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) { |
|
JNIEnv* e = reinterpret_cast<JNIEnv*>(env); |
|
|
|
scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter")); |
|
if (stringWriterClass.get() == NULL) { |
|
return false; |
|
} |
|
|
|
jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V"); |
|
jmethodID stringWriterToStringMethod = |
|
(*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;"); |
|
|
|
scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter")); |
|
if (printWriterClass.get() == NULL) { |
|
return false; |
|
} |
|
|
|
jmethodID printWriterCtor = |
|
(*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V"); |
|
|
|
scoped_local_ref<jobject> stringWriter(env, |
|
(*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor)); |
|
if (stringWriter.get() == NULL) { |
|
return false; |
|
} |
|
|
|
scoped_local_ref<jobject> printWriter(env, |
|
(*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get())); |
|
if (printWriter.get() == NULL) { |
|
return false; |
|
} |
|
|
|
scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail |
|
jmethodID printStackTraceMethod = |
|
(*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V"); |
|
(*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter.get()); |
|
|
|
if ((*env)->ExceptionCheck(e)) { |
|
return false; |
|
} |
|
|
|
scoped_local_ref<jstring> messageStr(env, |
|
(jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod)); |
|
if (messageStr.get() == NULL) { |
|
return false; |
|
} |
|
|
|
const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL); |
|
if (utfChars == NULL) { |
|
return false; |
|
} |
|
|
|
result = utfChars; |
|
|
|
(*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars); |
|
return true; |
|
} |
|
|
|
MODULE_API int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) { |
|
JNIEnv* e = reinterpret_cast<JNIEnv*>(env); |
|
|
|
if ((*env)->ExceptionCheck(e)) { |
|
/* TODO: consider creating the new exception with this as "cause" */ |
|
scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e)); |
|
(*env)->ExceptionClear(e); |
|
|
|
if (exception.get() != NULL) { |
|
std::string text; |
|
getExceptionSummary(env, exception.get(), text); |
|
ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className); |
|
} |
|
} |
|
|
|
scoped_local_ref<jclass> exceptionClass(env, findClass(env, className)); |
|
if (exceptionClass.get() == NULL) { |
|
ALOGE("Unable to find exception class %s", className); |
|
/* ClassNotFoundException now pending */ |
|
return -1; |
|
} |
|
|
|
if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) { |
|
ALOGE("Failed throwing '%s' '%s'", className, msg); |
|
/* an exception, most likely OOM, will now be pending */ |
|
return -1; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
MODULE_API int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) { |
|
char msgBuf[512]; |
|
vsnprintf(msgBuf, sizeof(msgBuf), fmt, args); |
|
return jniThrowException(env, className, msgBuf); |
|
} |
|
|
|
MODULE_API int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) { |
|
return jniThrowException(env, "java/lang/NullPointerException", msg); |
|
} |
|
|
|
MODULE_API int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) { |
|
return jniThrowException(env, "java/lang/RuntimeException", msg); |
|
} |
|
|
|
MODULE_API int jniThrowIOException(C_JNIEnv* env, int errnum) { |
|
char buffer[80]; |
|
const char* message = jniStrError(errnum, buffer, sizeof(buffer)); |
|
return jniThrowException(env, "java/io/IOException", message); |
|
} |
|
|
|
static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) { |
|
JNIEnv* e = reinterpret_cast<JNIEnv*>(env); |
|
|
|
scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e)); |
|
if (exception == NULL) { |
|
exception = currentException.get(); |
|
if (exception == NULL) { |
|
return "<no pending exception>"; |
|
} |
|
} |
|
|
|
if (currentException.get() != NULL) { |
|
(*env)->ExceptionClear(e); |
|
} |
|
|
|
std::string trace; |
|
if (!getStackTrace(env, exception, trace)) { |
|
(*env)->ExceptionClear(e); |
|
getExceptionSummary(env, exception, trace); |
|
} |
|
|
|
if (currentException.get() != NULL) { |
|
(*env)->Throw(e, currentException.get()); // rethrow |
|
} |
|
|
|
return trace; |
|
} |
|
|
|
MODULE_API void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) { |
|
std::string trace(jniGetStackTrace(env, exception)); |
|
__android_log_write(priority, tag, trace.c_str()); |
|
} |
|
|
|
// 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); |
|
// |
|
// Some versions of bionic support the glibc style call. Since the set of defines that determine |
|
// which version is used is byzantine in its complexity we will just use this C++ template hack to |
|
// select the correct jniStrError implementation based on the libc being used. |
|
namespace impl { |
|
|
|
using GNUStrError = char* (*)(int,char*,size_t); |
|
using POSIXStrError = int (*)(int,char*,size_t); |
|
|
|
inline const char* realJniStrError(GNUStrError func, int errnum, char* buf, size_t buflen) { |
|
return func(errnum, buf, buflen); |
|
} |
|
|
|
inline const char* realJniStrError(POSIXStrError func, int errnum, char* buf, size_t buflen) { |
|
int rc = func(errnum, buf, buflen); |
|
if (rc != 0) { |
|
// (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.) |
|
snprintf(buf, buflen, "errno %d", errnum); |
|
} |
|
return buf; |
|
} |
|
|
|
} // namespace impl |
|
|
|
MODULE_API const char* jniStrError(int errnum, char* buf, size_t buflen) { |
|
#ifdef _WIN32 |
|
strerror_s(buf, buflen, errnum); |
|
return buf; |
|
#else |
|
// The magic of C++ overloading selects the correct implementation based on the declared type of |
|
// strerror_r. The inline will ensure that we don't have any indirect calls. |
|
return impl::realJniStrError(strerror_r, errnum, buf, buflen); |
|
#endif |
|
} |
|
|
|
MODULE_API jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) { |
|
JNIEnv* e = reinterpret_cast<JNIEnv*>(env); |
|
jobject fileDescriptor = e->NewObject(JniConstants::GetFileDescriptorClass(e), |
|
JniConstants::GetFileDescriptorInitMethod(e)); |
|
// NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java |
|
// caller if the alloc fails, so we just return nullptr when that happens. |
|
if (fileDescriptor != nullptr) { |
|
jniSetFileDescriptorOfFD(env, fileDescriptor, fd); |
|
} |
|
return fileDescriptor; |
|
} |
|
|
|
MODULE_API int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) { |
|
JNIEnv* e = reinterpret_cast<JNIEnv*>(env); |
|
if (fileDescriptor != nullptr) { |
|
return e->GetIntField(fileDescriptor, |
|
JniConstants::GetFileDescriptorDescriptorField(e)); |
|
} else { |
|
return -1; |
|
} |
|
} |
|
|
|
MODULE_API void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) { |
|
JNIEnv* e = reinterpret_cast<JNIEnv*>(env); |
|
if (fileDescriptor == nullptr) { |
|
jniThrowNullPointerException(e, "null FileDescriptor"); |
|
} else { |
|
e->SetIntField(fileDescriptor, JniConstants::GetFileDescriptorDescriptorField(e), value); |
|
} |
|
} |
|
|
|
MODULE_API jlong jniGetOwnerIdFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) { |
|
JNIEnv* e = reinterpret_cast<JNIEnv*>(env); |
|
return e->GetLongField(fileDescriptor, JniConstants::GetFileDescriptorOwnerIdField(e)); |
|
} |
|
|
|
MODULE_API jarray jniGetNioBufferBaseArray(C_JNIEnv* env, jobject nioBuffer) { |
|
JNIEnv* e = reinterpret_cast<JNIEnv*>(env); |
|
jclass nioAccessClass = JniConstants::GetNioAccessClass(e); |
|
jmethodID getBaseArrayMethod = JniConstants::GetNioAccessGetBaseArrayMethod(e); |
|
jobject object = e->CallStaticObjectMethod(nioAccessClass, getBaseArrayMethod, nioBuffer); |
|
return static_cast<jarray>(object); |
|
} |
|
|
|
MODULE_API int jniGetNioBufferBaseArrayOffset(C_JNIEnv* env, jobject nioBuffer) { |
|
JNIEnv* e = reinterpret_cast<JNIEnv*>(env); |
|
jclass nioAccessClass = JniConstants::GetNioAccessClass(e); |
|
jmethodID getBaseArrayOffsetMethod = JniConstants::GetNioAccessGetBaseArrayOffsetMethod(e); |
|
return e->CallStaticIntMethod(nioAccessClass, getBaseArrayOffsetMethod, nioBuffer); |
|
} |
|
|
|
MODULE_API jlong jniGetNioBufferPointer(C_JNIEnv* env, jobject nioBuffer) { |
|
JNIEnv* e = reinterpret_cast<JNIEnv*>(env); |
|
jlong baseAddress = e->GetLongField(nioBuffer, JniConstants::GetNioBufferAddressField(e)); |
|
if (baseAddress != 0) { |
|
const int position = e->GetIntField(nioBuffer, JniConstants::GetNioBufferPositionField(e)); |
|
const int shift = |
|
e->GetIntField(nioBuffer, JniConstants::GetNioBufferElementSizeShiftField(e)); |
|
baseAddress += position << shift; |
|
} |
|
return baseAddress; |
|
} |
|
|
|
MODULE_API jlong jniGetNioBufferFields(C_JNIEnv* env, jobject nioBuffer, |
|
jint* position, jint* limit, jint* elementSizeShift) { |
|
JNIEnv* e = reinterpret_cast<JNIEnv*>(env); |
|
*position = e->GetIntField(nioBuffer, JniConstants::GetNioBufferPositionField(e)); |
|
*limit = e->GetIntField(nioBuffer, JniConstants::GetNioBufferLimitField(e)); |
|
*elementSizeShift = |
|
e->GetIntField(nioBuffer, JniConstants::GetNioBufferElementSizeShiftField(e)); |
|
return e->GetLongField(nioBuffer, JniConstants::GetNioBufferAddressField(e)); |
|
} |
|
|
|
MODULE_API jobject jniGetReferent(C_JNIEnv* env, jobject ref) { |
|
JNIEnv* e = reinterpret_cast<JNIEnv*>(env); |
|
return e->CallObjectMethod(ref, JniConstants::GetReferenceGetMethod(e)); |
|
} |
|
|
|
MODULE_API jstring jniCreateString(C_JNIEnv* env, const jchar* unicodeChars, jsize len) { |
|
JNIEnv* e = reinterpret_cast<JNIEnv*>(env); |
|
return e->NewString(unicodeChars, len); |
|
} |
|
|
|
MODULE_API void jniUninitializeConstants() { |
|
JniConstants::Uninitialize(); |
|
}
|
|
|