SQLite for android
This commit is contained in:
341
packages/swig/android/jni/sqlite/JNIHelp.cpp
Normal file
341
packages/swig/android/jni/sqlite/JNIHelp.cpp
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* 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 "JniConstants.h"
|
||||
#include "JNIHelp.h"
|
||||
#include "ALog-priv.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
|
||||
*/
|
||||
template<typename T>
|
||||
class scoped_local_ref {
|
||||
public:
|
||||
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* mEnv;
|
||||
T mLocalRef;
|
||||
|
||||
// Disallow copy and assignment.
|
||||
scoped_local_ref(const scoped_local_ref&);
|
||||
void operator=(const scoped_local_ref&);
|
||||
};
|
||||
|
||||
static jclass findClass(C_JNIEnv* env, const char* className) {
|
||||
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
|
||||
return (*env)->FindClass(e, className);
|
||||
}
|
||||
|
||||
extern "C" 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));
|
||||
if (c.get() == NULL) {
|
||||
char* msg;
|
||||
asprintf(&msg, "Native registration unable to find class '%s'; aborting...", className);
|
||||
e->FatalError(msg);
|
||||
}
|
||||
|
||||
if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
|
||||
char* msg;
|
||||
asprintf(&msg, "RegisterNatives failed for '%s'; aborting...", className);
|
||||
e->FatalError(msg);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
jobject printWriter =
|
||||
(*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get());
|
||||
if (printWriter == 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
extern "C" 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
|
||||
return jniThrowException(env, "java/lang/NullPointerException", msg);
|
||||
}
|
||||
|
||||
int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
|
||||
return jniThrowException(env, "java/lang/RuntimeException", msg);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
const char* jniStrError(int errnum, char* buf, size_t buflen) {
|
||||
#if __GLIBC__
|
||||
// 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);
|
||||
return strerror_r(errnum, buf, buflen);
|
||||
#else
|
||||
int rc = strerror_r(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;
|
||||
#endif
|
||||
}
|
||||
|
||||
jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
|
||||
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
|
||||
static jmethodID ctor = e->GetMethodID(JniConstants::fileDescriptorClass, "<init>", "()V");
|
||||
jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass, ctor);
|
||||
// NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java
|
||||
// caller if the alloc fails, so we just return NULL when that happens.
|
||||
if (fileDescriptor != NULL) {
|
||||
jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
|
||||
}
|
||||
return fileDescriptor;
|
||||
}
|
||||
|
||||
int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
|
||||
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
|
||||
static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
|
||||
if (fileDescriptor != NULL) {
|
||||
return (*env)->GetIntField(e, fileDescriptor, fid);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
|
||||
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
|
||||
static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
|
||||
(*env)->SetIntField(e, fileDescriptor, fid, value);
|
||||
}
|
||||
|
||||
jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
|
||||
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
|
||||
static jmethodID get = e->GetMethodID(JniConstants::referenceClass, "get", "()Ljava/lang/Object;");
|
||||
return (*env)->CallObjectMethod(e, ref, get);
|
||||
}
|
||||
|
Reference in New Issue
Block a user