SQLite for android
This commit is contained in:
76
packages/swig/android/jni/sqlite/ALog-priv.h
Normal file
76
packages/swig/android/jni/sqlite/ALog-priv.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2013 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.
|
||||
*/
|
||||
/*
|
||||
** Modified to support SQLite extensions by the SQLite developers:
|
||||
** sqlite-dev@sqlite.org.
|
||||
*/
|
||||
|
||||
#ifndef NATIVEHELPER_ALOGPRIV_H_
|
||||
#define NATIVEHELPER_ALOGPRIV_H_
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#ifndef LOG_NDEBUG
|
||||
#ifdef NDEBUG
|
||||
#define LOG_NDEBUG 1
|
||||
#else
|
||||
#define LOG_NDEBUG 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Basic log message macros intended to emulate the behavior of log/log.h
|
||||
* in system core. This should be dependent only on ndk exposed logging
|
||||
* functionality.
|
||||
*/
|
||||
|
||||
#ifndef ALOG
|
||||
#define ALOG(priority, tag, fmt...) \
|
||||
__android_log_print(ANDROID_##priority, tag, fmt)
|
||||
#endif
|
||||
|
||||
#ifndef ALOGV
|
||||
#if LOG_NDEBUG
|
||||
#define ALOGV(...) ((void)0)
|
||||
#else
|
||||
#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ALOGD
|
||||
#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ALOGI
|
||||
#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ALOGW
|
||||
#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ALOGE
|
||||
#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Not quite the same as the core android LOG_FATAL_IF (which also
|
||||
** sends a SIGTRAP), but close enough.
|
||||
*/
|
||||
#define LOG_FATAL_IF(bCond, zErr) if( bCond ) ALOGE(zErr);
|
||||
|
||||
#endif
|
37
packages/swig/android/jni/sqlite/Android.mk
Normal file
37
packages/swig/android/jni/sqlite/Android.mk
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
# If using SEE, uncomment the following:
|
||||
# LOCAL_CFLAGS += -DSQLITE_HAS_CODEC
|
||||
|
||||
LOCAL_CFLAGS += -DHAVE_CONFIG_H -DKHTML_NO_EXCEPTIONS -DGKWQ_NO_JAVA
|
||||
LOCAL_CFLAGS += -DNO_SUPPORT_JS_BINDING -DQT_NO_WHEELEVENT -DKHTML_NO_XBL
|
||||
LOCAL_CFLAGS += -U__APPLE__
|
||||
LOCAL_CFLAGS += -Wno-unused-parameter -Wno-int-to-pointer-cast
|
||||
LOCAL_CFLAGS += -Wno-maybe-uninitialized -Wno-parentheses
|
||||
LOCAL_CPPFLAGS += -Wno-conversion-null
|
||||
|
||||
|
||||
ifeq ($(TARGET_ARCH), arm)
|
||||
LOCAL_CFLAGS += -DPACKED="__attribute__ ((packed))"
|
||||
else
|
||||
LOCAL_CFLAGS += -DPACKED=""
|
||||
endif
|
||||
|
||||
LOCAL_SRC_FILES:= \
|
||||
android_database_SQLiteCommon.cpp \
|
||||
android_database_SQLiteConnection.cpp \
|
||||
android_database_SQLiteGlobal.cpp \
|
||||
android_database_SQLiteDebug.cpp \
|
||||
JNIHelp.cpp JniConstants.cpp
|
||||
|
||||
LOCAL_SRC_FILES += sqlite3.c
|
||||
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH) $(LOCAL_PATH)/nativehelper/
|
||||
|
||||
LOCAL_MODULE:= libsqliteX
|
||||
LOCAL_LDLIBS += -ldl -llog
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
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);
|
||||
}
|
||||
|
139
packages/swig/android/jni/sqlite/JniConstants.cpp
Normal file
139
packages/swig/android/jni/sqlite/JniConstants.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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 "JniConstants"
|
||||
|
||||
#include "ALog-priv.h"
|
||||
#include "JniConstants.h"
|
||||
#include "ScopedLocalRef.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
jclass JniConstants::bidiRunClass;
|
||||
jclass JniConstants::bigDecimalClass;
|
||||
jclass JniConstants::booleanClass;
|
||||
jclass JniConstants::byteArrayClass;
|
||||
jclass JniConstants::byteClass;
|
||||
jclass JniConstants::calendarClass;
|
||||
jclass JniConstants::characterClass;
|
||||
jclass JniConstants::charsetICUClass;
|
||||
jclass JniConstants::constructorClass;
|
||||
jclass JniConstants::deflaterClass;
|
||||
jclass JniConstants::doubleClass;
|
||||
jclass JniConstants::errnoExceptionClass;
|
||||
jclass JniConstants::fieldClass;
|
||||
jclass JniConstants::fieldPositionIteratorClass;
|
||||
jclass JniConstants::fileDescriptorClass;
|
||||
jclass JniConstants::floatClass;
|
||||
jclass JniConstants::gaiExceptionClass;
|
||||
jclass JniConstants::inet6AddressClass;
|
||||
jclass JniConstants::inetAddressClass;
|
||||
jclass JniConstants::inetSocketAddressClass;
|
||||
jclass JniConstants::inetUnixAddressClass;
|
||||
jclass JniConstants::inflaterClass;
|
||||
jclass JniConstants::inputStreamClass;
|
||||
jclass JniConstants::integerClass;
|
||||
jclass JniConstants::localeDataClass;
|
||||
jclass JniConstants::longClass;
|
||||
jclass JniConstants::methodClass;
|
||||
jclass JniConstants::mutableIntClass;
|
||||
jclass JniConstants::mutableLongClass;
|
||||
jclass JniConstants::objectClass;
|
||||
jclass JniConstants::objectArrayClass;
|
||||
jclass JniConstants::outputStreamClass;
|
||||
jclass JniConstants::parsePositionClass;
|
||||
jclass JniConstants::patternSyntaxExceptionClass;
|
||||
jclass JniConstants::realToStringClass;
|
||||
jclass JniConstants::referenceClass;
|
||||
jclass JniConstants::shortClass;
|
||||
jclass JniConstants::socketClass;
|
||||
jclass JniConstants::socketImplClass;
|
||||
jclass JniConstants::stringClass;
|
||||
jclass JniConstants::structAddrinfoClass;
|
||||
jclass JniConstants::structFlockClass;
|
||||
jclass JniConstants::structGroupReqClass;
|
||||
jclass JniConstants::structLingerClass;
|
||||
jclass JniConstants::structPasswdClass;
|
||||
jclass JniConstants::structPollfdClass;
|
||||
jclass JniConstants::structStatClass;
|
||||
jclass JniConstants::structStatVfsClass;
|
||||
jclass JniConstants::structTimevalClass;
|
||||
jclass JniConstants::structUcredClass;
|
||||
jclass JniConstants::structUtsnameClass;
|
||||
|
||||
static jclass findClass(JNIEnv* env, const char* name) {
|
||||
ScopedLocalRef<jclass> localClass(env, env->FindClass(name));
|
||||
jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get()));
|
||||
if (result == NULL) {
|
||||
ALOGE("failed to find class '%s'", name);
|
||||
abort();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void JniConstants::init(JNIEnv* env) {
|
||||
bidiRunClass = findClass(env, "java/text/Bidi$Run");
|
||||
bigDecimalClass = findClass(env, "java/math/BigDecimal");
|
||||
booleanClass = findClass(env, "java/lang/Boolean");
|
||||
byteClass = findClass(env, "java/lang/Byte");
|
||||
byteArrayClass = findClass(env, "[B");
|
||||
calendarClass = findClass(env, "java/util/Calendar");
|
||||
characterClass = findClass(env, "java/lang/Character");
|
||||
charsetICUClass = findClass(env, "java/nio/charset/CharsetICU");
|
||||
constructorClass = findClass(env, "java/lang/reflect/Constructor");
|
||||
floatClass = findClass(env, "java/lang/Float");
|
||||
deflaterClass = findClass(env, "java/util/zip/Deflater");
|
||||
doubleClass = findClass(env, "java/lang/Double");
|
||||
errnoExceptionClass = findClass(env, "libcore/io/ErrnoException");
|
||||
fieldClass = findClass(env, "java/lang/reflect/Field");
|
||||
fieldPositionIteratorClass = findClass(env, "libcore/icu/NativeDecimalFormat$FieldPositionIterator");
|
||||
fileDescriptorClass = findClass(env, "java/io/FileDescriptor");
|
||||
gaiExceptionClass = findClass(env, "libcore/io/GaiException");
|
||||
inet6AddressClass = findClass(env, "java/net/Inet6Address");
|
||||
inetAddressClass = findClass(env, "java/net/InetAddress");
|
||||
inetSocketAddressClass = findClass(env, "java/net/InetSocketAddress");
|
||||
inetUnixAddressClass = findClass(env, "java/net/InetUnixAddress");
|
||||
inflaterClass = findClass(env, "java/util/zip/Inflater");
|
||||
inputStreamClass = findClass(env, "java/io/InputStream");
|
||||
integerClass = findClass(env, "java/lang/Integer");
|
||||
localeDataClass = findClass(env, "libcore/icu/LocaleData");
|
||||
longClass = findClass(env, "java/lang/Long");
|
||||
methodClass = findClass(env, "java/lang/reflect/Method");
|
||||
mutableIntClass = findClass(env, "libcore/util/MutableInt");
|
||||
mutableLongClass = findClass(env, "libcore/util/MutableLong");
|
||||
objectClass = findClass(env, "java/lang/Object");
|
||||
objectArrayClass = findClass(env, "[Ljava/lang/Object;");
|
||||
outputStreamClass = findClass(env, "java/io/OutputStream");
|
||||
parsePositionClass = findClass(env, "java/text/ParsePosition");
|
||||
patternSyntaxExceptionClass = findClass(env, "java/util/regex/PatternSyntaxException");
|
||||
realToStringClass = findClass(env, "java/lang/RealToString");
|
||||
referenceClass = findClass(env, "java/lang/ref/Reference");
|
||||
shortClass = findClass(env, "java/lang/Short");
|
||||
socketClass = findClass(env, "java/net/Socket");
|
||||
socketImplClass = findClass(env, "java/net/SocketImpl");
|
||||
stringClass = findClass(env, "java/lang/String");
|
||||
structAddrinfoClass = findClass(env, "libcore/io/StructAddrinfo");
|
||||
structFlockClass = findClass(env, "libcore/io/StructFlock");
|
||||
structGroupReqClass = findClass(env, "libcore/io/StructGroupReq");
|
||||
structLingerClass = findClass(env, "libcore/io/StructLinger");
|
||||
structPasswdClass = findClass(env, "libcore/io/StructPasswd");
|
||||
structPollfdClass = findClass(env, "libcore/io/StructPollfd");
|
||||
structStatClass = findClass(env, "libcore/io/StructStat");
|
||||
structStatVfsClass = findClass(env, "libcore/io/StructStatVfs");
|
||||
structTimevalClass = findClass(env, "libcore/io/StructTimeval");
|
||||
structUcredClass = findClass(env, "libcore/io/StructUcred");
|
||||
structUtsnameClass = findClass(env, "libcore/io/StructUtsname");
|
||||
}
|
40
packages/swig/android/jni/sqlite/README
Normal file
40
packages/swig/android/jni/sqlite/README
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
All the files in this directory are copied from stock android. The following
|
||||
files:
|
||||
|
||||
JniConstants.cpp
|
||||
JNIHelp.cpp
|
||||
ALog-priv.h
|
||||
|
||||
are copied in from Android's libnativehelper module (altogether less than 1000
|
||||
lines of code). The remainder are from the core framework (directory
|
||||
/frameworks/base/core/jni).
|
||||
|
||||
Notes on changes:
|
||||
|
||||
The ashmem_XXX() interfaces are used for the various "xxxForBlobDescriptor()"
|
||||
API functions. The code in libcutils for this seems to be platform
|
||||
dependent - some platforms have kernel support, others have a user space
|
||||
implementation. So these functions are not supported for now.
|
||||
|
||||
The original SQLiteConnection.cpp uses AndroidRuntime::genJNIEnv() to obtain a
|
||||
pointer to the current threads environment. Changed to store a pointer to the
|
||||
process JavaVM (Android allows only one) as a global variable. Then retrieve
|
||||
the JNIEnv as needed using GetEnv().
|
||||
|
||||
Replaced uses of class String8 with std::string in SQLiteConnection.cpp and a
|
||||
few other places.
|
||||
|
||||
The stock Android code to populate CursorWindow containers with the results of
|
||||
a SELECT statement uses a C++ interface that is not available to NDK builds. So
|
||||
this code is rewritten to call the CursorWindow java interface via JNI methods.
|
||||
This is the largest source code change. See function
|
||||
nativeExecuteForCursorWindow() in file android_database_SQLiteConnection.cpp
|
||||
for details.
|
||||
|
||||
The "LOCALIZED" collation and some miscellaneous user-functions added by the
|
||||
sqlite3_android.cpp module are not included. A collation called LOCALIZED
|
||||
that is equivalent to BINARY is added instead to keep various things working.
|
||||
This should not cause serious problems - class SQLiteConnection always
|
||||
runs "REINDEX LOCALIZED" immediately after opening a connection.
|
||||
|
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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.
|
||||
*/
|
||||
/*
|
||||
** Modified to support SQLite extensions by the SQLite developers:
|
||||
** sqlite-dev@sqlite.org.
|
||||
*/
|
||||
|
||||
#include "android_database_SQLiteCommon.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
/* throw a SQLiteException with a message appropriate for the error in handle */
|
||||
void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) {
|
||||
throw_sqlite3_exception(env, handle, NULL);
|
||||
}
|
||||
|
||||
/* throw a SQLiteException with the given message */
|
||||
void throw_sqlite3_exception(JNIEnv* env, const char* message) {
|
||||
throw_sqlite3_exception(env, NULL, message);
|
||||
}
|
||||
|
||||
/* throw a SQLiteException with a message appropriate for the error in handle
|
||||
concatenated with the given message
|
||||
*/
|
||||
void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message) {
|
||||
if (handle) {
|
||||
// get the error code and message from the SQLite connection
|
||||
// the error message may contain more information than the error code
|
||||
// because it is based on the extended error code rather than the simplified
|
||||
// error code that SQLite normally returns.
|
||||
throw_sqlite3_exception(env, sqlite3_extended_errcode(handle),
|
||||
sqlite3_errmsg(handle), message);
|
||||
} else {
|
||||
// we use SQLITE_OK so that a generic SQLiteException is thrown;
|
||||
// any code not specified in the switch statement below would do.
|
||||
throw_sqlite3_exception(env, SQLITE_OK, "unknown error", message);
|
||||
}
|
||||
}
|
||||
|
||||
/* throw a SQLiteException for a given error code
|
||||
* should only be used when the database connection is not available because the
|
||||
* error information will not be quite as rich */
|
||||
void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message) {
|
||||
throw_sqlite3_exception(env, errcode, "unknown error", message);
|
||||
}
|
||||
|
||||
/* throw a SQLiteException for a given error code, sqlite3message, and
|
||||
user message
|
||||
*/
|
||||
void throw_sqlite3_exception(JNIEnv* env, int errcode,
|
||||
const char* sqlite3Message, const char* message) {
|
||||
const char* exceptionClass;
|
||||
switch (errcode & 0xff) { /* mask off extended error code */
|
||||
case SQLITE_IOERR:
|
||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteDiskIOException";
|
||||
break;
|
||||
case SQLITE_CORRUPT:
|
||||
case SQLITE_NOTADB: // treat "unsupported file format" error as corruption also
|
||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteDatabaseCorruptException";
|
||||
break;
|
||||
case SQLITE_CONSTRAINT:
|
||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteConstraintException";
|
||||
break;
|
||||
case SQLITE_ABORT:
|
||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteAbortException";
|
||||
break;
|
||||
case SQLITE_DONE:
|
||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteDoneException";
|
||||
sqlite3Message = NULL; // SQLite error message is irrelevant in this case
|
||||
break;
|
||||
case SQLITE_FULL:
|
||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteFullException";
|
||||
break;
|
||||
case SQLITE_MISUSE:
|
||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteMisuseException";
|
||||
break;
|
||||
case SQLITE_PERM:
|
||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteAccessPermException";
|
||||
break;
|
||||
case SQLITE_BUSY:
|
||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteDatabaseLockedException";
|
||||
break;
|
||||
case SQLITE_LOCKED:
|
||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteTableLockedException";
|
||||
break;
|
||||
case SQLITE_READONLY:
|
||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteReadOnlyDatabaseException";
|
||||
break;
|
||||
case SQLITE_CANTOPEN:
|
||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteCantOpenDatabaseException";
|
||||
break;
|
||||
case SQLITE_TOOBIG:
|
||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteBlobTooBigException";
|
||||
break;
|
||||
case SQLITE_RANGE:
|
||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException";
|
||||
break;
|
||||
case SQLITE_NOMEM:
|
||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteOutOfMemoryException";
|
||||
break;
|
||||
case SQLITE_MISMATCH:
|
||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteDatatypeMismatchException";
|
||||
break;
|
||||
case SQLITE_INTERRUPT:
|
||||
exceptionClass = "android/os/OperationCanceledException";
|
||||
break;
|
||||
default:
|
||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteException";
|
||||
break;
|
||||
}
|
||||
|
||||
if (sqlite3Message) {
|
||||
char *zFullmsg = sqlite3_mprintf(
|
||||
"%s (code %d)%s%s", sqlite3Message, errcode,
|
||||
(message ? ": " : ""), (message ? message : "")
|
||||
);
|
||||
jniThrowException(env, exceptionClass, zFullmsg);
|
||||
sqlite3_free(zFullmsg);
|
||||
} else {
|
||||
jniThrowException(env, exceptionClass, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace android
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
/*
|
||||
** Modified to support SQLite extensions by the SQLite developers:
|
||||
** sqlite-dev@sqlite.org.
|
||||
*/
|
||||
|
||||
#ifndef _ANDROID_DATABASE_SQLITE_COMMON_H
|
||||
#define _ANDROID_DATABASE_SQLITE_COMMON_H
|
||||
|
||||
#include <jni.h>
|
||||
#include <JNIHelp.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
// Special log tags defined in SQLiteDebug.java.
|
||||
#define SQLITE_LOG_TAG "SQLiteLog"
|
||||
#define SQLITE_TRACE_TAG "SQLiteStatements"
|
||||
#define SQLITE_PROFILE_TAG "SQLiteTime"
|
||||
|
||||
namespace android {
|
||||
|
||||
/* throw a SQLiteException with a message appropriate for the error in handle */
|
||||
void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle);
|
||||
|
||||
/* throw a SQLiteException with the given message */
|
||||
void throw_sqlite3_exception(JNIEnv* env, const char* message);
|
||||
|
||||
/* throw a SQLiteException with a message appropriate for the error in handle
|
||||
concatenated with the given message
|
||||
*/
|
||||
void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message);
|
||||
|
||||
/* throw a SQLiteException for a given error code */
|
||||
void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message);
|
||||
|
||||
void throw_sqlite3_exception(JNIEnv* env, int errcode,
|
||||
const char* sqlite3Message, const char* message);
|
||||
|
||||
}
|
||||
|
||||
#endif // _ANDROID_DATABASE_SQLITE_COMMON_H
|
@@ -0,0 +1,980 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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.
|
||||
*/
|
||||
/*
|
||||
** Modified to support SQLite extensions by the SQLite developers:
|
||||
** sqlite-dev@sqlite.org.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "SQLiteConnection"
|
||||
|
||||
#include <jni.h>
|
||||
#include <JNIHelp.h>
|
||||
#include "ALog-priv.h"
|
||||
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#if 0
|
||||
#include <androidfw/CursorWindow.h>
|
||||
#endif
|
||||
|
||||
#include <sqlite3.h>
|
||||
#if 0
|
||||
#include <sqlite3_android.h>
|
||||
#endif
|
||||
|
||||
#include "android_database_SQLiteCommon.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
// Set to 1 to use UTF16 storage for localized indexes.
|
||||
#define UTF16_STORAGE 0
|
||||
|
||||
namespace android {
|
||||
|
||||
/* Busy timeout in milliseconds.
|
||||
* If another connection (possibly in another process) has the database locked for
|
||||
* longer than this amount of time then SQLite will generate a SQLITE_BUSY error.
|
||||
* The SQLITE_BUSY error is then raised as a SQLiteDatabaseLockedException.
|
||||
*
|
||||
* In ordinary usage, busy timeouts are quite rare. Most databases only ever
|
||||
* have a single open connection at a time unless they are using WAL. When using
|
||||
* WAL, a timeout could occur if one connection is busy performing an auto-checkpoint
|
||||
* operation. The busy timeout needs to be long enough to tolerate slow I/O write
|
||||
* operations but not so long as to cause the application to hang indefinitely if
|
||||
* there is a problem acquiring a database lock.
|
||||
*/
|
||||
static const int BUSY_TIMEOUT_MS = 2500;
|
||||
|
||||
static JavaVM *gpJavaVM = 0;
|
||||
|
||||
static struct {
|
||||
jfieldID name;
|
||||
jfieldID numArgs;
|
||||
jmethodID dispatchCallback;
|
||||
} gSQLiteCustomFunctionClassInfo;
|
||||
|
||||
static struct {
|
||||
jclass clazz;
|
||||
} gStringClassInfo;
|
||||
|
||||
struct SQLiteConnection {
|
||||
// Open flags.
|
||||
// Must be kept in sync with the constants defined in SQLiteDatabase.java.
|
||||
enum {
|
||||
OPEN_READWRITE = 0x00000000,
|
||||
OPEN_READONLY = 0x00000001,
|
||||
OPEN_READ_MASK = 0x00000001,
|
||||
NO_LOCALIZED_COLLATORS = 0x00000010,
|
||||
CREATE_IF_NECESSARY = 0x10000000,
|
||||
};
|
||||
|
||||
sqlite3* const db;
|
||||
const int openFlags;
|
||||
std::string path;
|
||||
std::string label;
|
||||
|
||||
volatile bool canceled;
|
||||
|
||||
SQLiteConnection(sqlite3* db, int openFlags, const std::string& path, const std::string& label) :
|
||||
db(db), openFlags(openFlags), path(path), label(label), canceled(false) { }
|
||||
};
|
||||
|
||||
// Called each time a statement begins execution, when tracing is enabled.
|
||||
static void sqliteTraceCallback(void *data, const char *sql) {
|
||||
SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
|
||||
ALOG(LOG_VERBOSE, SQLITE_TRACE_TAG, "%s: \"%s\"\n",
|
||||
connection->label.c_str(), sql);
|
||||
}
|
||||
|
||||
// Called each time a statement finishes execution, when profiling is enabled.
|
||||
static void sqliteProfileCallback(void *data, const char *sql, sqlite3_uint64 tm) {
|
||||
SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
|
||||
ALOG(LOG_VERBOSE, SQLITE_PROFILE_TAG, "%s: \"%s\" took %0.3f ms\n",
|
||||
connection->label.c_str(), sql, tm * 0.000001f);
|
||||
}
|
||||
|
||||
// Called after each SQLite VM instruction when cancelation is enabled.
|
||||
static int sqliteProgressHandlerCallback(void* data) {
|
||||
SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
|
||||
return connection->canceled;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is a collation sequence callback equivalent to the built-in
|
||||
** BINARY sequence.
|
||||
**
|
||||
** Stock Android uses a modified version of sqlite3.c that calls out to a module
|
||||
** named "sqlite3_android" to add extra built-in collations and functions to
|
||||
** all database handles. Specifically, collation sequence "LOCALIZED". For now,
|
||||
** this module does not include sqlite3_android (since it is difficult to build
|
||||
** with the NDK only). Instead, this function is registered as "LOCALIZED" for all
|
||||
** new database handles.
|
||||
*/
|
||||
static int coll_localized(
|
||||
void *not_used,
|
||||
int nKey1, const void *pKey1,
|
||||
int nKey2, const void *pKey2
|
||||
){
|
||||
int rc, n;
|
||||
n = nKey1<nKey2 ? nKey1 : nKey2;
|
||||
rc = memcmp(pKey1, pKey2, n);
|
||||
if( rc==0 ){
|
||||
rc = nKey1 - nKey2;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static jint nativeOpen(JNIEnv* env, jclass clazz, jstring pathStr, jint openFlags,
|
||||
jstring labelStr, jboolean enableTrace, jboolean enableProfile) {
|
||||
int sqliteFlags;
|
||||
if (openFlags & SQLiteConnection::CREATE_IF_NECESSARY) {
|
||||
sqliteFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
|
||||
} else if (openFlags & SQLiteConnection::OPEN_READONLY) {
|
||||
sqliteFlags = SQLITE_OPEN_READONLY;
|
||||
} else {
|
||||
sqliteFlags = SQLITE_OPEN_READWRITE;
|
||||
}
|
||||
|
||||
const char* pathChars = env->GetStringUTFChars(pathStr, NULL);
|
||||
std::string path(pathChars);
|
||||
env->ReleaseStringUTFChars(pathStr, pathChars);
|
||||
|
||||
const char* labelChars = env->GetStringUTFChars(labelStr, NULL);
|
||||
std::string label(labelChars);
|
||||
env->ReleaseStringUTFChars(labelStr, labelChars);
|
||||
|
||||
sqlite3* db;
|
||||
int err = sqlite3_open_v2(path.c_str(), &db, sqliteFlags, NULL);
|
||||
if (err != SQLITE_OK) {
|
||||
throw_sqlite3_exception_errcode(env, err, "Could not open database");
|
||||
return 0;
|
||||
}
|
||||
err = sqlite3_create_collation(db, "localized", SQLITE_UTF8, 0, coll_localized);
|
||||
if (err != SQLITE_OK) {
|
||||
throw_sqlite3_exception_errcode(env, err, "Could not register collation");
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check that the database is really read/write when that is what we asked for.
|
||||
if ((sqliteFlags & SQLITE_OPEN_READWRITE) && sqlite3_db_readonly(db, NULL)) {
|
||||
throw_sqlite3_exception(env, db, "Could not open the database in read/write mode.");
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set the default busy handler to retry automatically before returning SQLITE_BUSY.
|
||||
err = sqlite3_busy_timeout(db, BUSY_TIMEOUT_MS);
|
||||
if (err != SQLITE_OK) {
|
||||
throw_sqlite3_exception(env, db, "Could not set busy timeout");
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Register custom Android functions.
|
||||
#if 0
|
||||
err = register_android_functions(db, UTF16_STORAGE);
|
||||
if (err) {
|
||||
throw_sqlite3_exception(env, db, "Could not register Android SQL functions.");
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Create wrapper object.
|
||||
SQLiteConnection* connection = new SQLiteConnection(db, openFlags, path, label);
|
||||
|
||||
// Enable tracing and profiling if requested.
|
||||
if (enableTrace) {
|
||||
sqlite3_trace(db, &sqliteTraceCallback, connection);
|
||||
}
|
||||
if (enableProfile) {
|
||||
sqlite3_profile(db, &sqliteProfileCallback, connection);
|
||||
}
|
||||
|
||||
ALOGV("Opened connection %p with label '%s'", db, label.c_str());
|
||||
return reinterpret_cast<jint>(connection);
|
||||
}
|
||||
|
||||
static void nativeClose(JNIEnv* env, jclass clazz, jint connectionPtr) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
|
||||
if (connection) {
|
||||
ALOGV("Closing connection %p", connection->db);
|
||||
int err = sqlite3_close(connection->db);
|
||||
if (err != SQLITE_OK) {
|
||||
// This can happen if sub-objects aren't closed first. Make sure the caller knows.
|
||||
ALOGE("sqlite3_close(%p) failed: %d", connection->db, err);
|
||||
throw_sqlite3_exception(env, connection->db, "Count not close db.");
|
||||
return;
|
||||
}
|
||||
|
||||
delete connection;
|
||||
}
|
||||
}
|
||||
|
||||
// Called each time a custom function is evaluated.
|
||||
static void sqliteCustomFunctionCallback(sqlite3_context *context,
|
||||
int argc, sqlite3_value **argv) {
|
||||
|
||||
JNIEnv* env = 0;
|
||||
gpJavaVM->GetEnv((void**)&env, JNI_VERSION_1_4);
|
||||
|
||||
// Get the callback function object.
|
||||
// Create a new local reference to it in case the callback tries to do something
|
||||
// dumb like unregister the function (thereby destroying the global ref) while it is running.
|
||||
jobject functionObjGlobal = reinterpret_cast<jobject>(sqlite3_user_data(context));
|
||||
jobject functionObj = env->NewLocalRef(functionObjGlobal);
|
||||
|
||||
jobjectArray argsArray = env->NewObjectArray(argc, gStringClassInfo.clazz, NULL);
|
||||
if (argsArray) {
|
||||
for (int i = 0; i < argc; i++) {
|
||||
const jchar* arg = static_cast<const jchar*>(sqlite3_value_text16(argv[i]));
|
||||
if (!arg) {
|
||||
ALOGW("NULL argument in custom_function_callback. This should not happen.");
|
||||
} else {
|
||||
size_t argLen = sqlite3_value_bytes16(argv[i]) / sizeof(jchar);
|
||||
jstring argStr = env->NewString(arg, argLen);
|
||||
if (!argStr) {
|
||||
goto error; // out of memory error
|
||||
}
|
||||
env->SetObjectArrayElement(argsArray, i, argStr);
|
||||
env->DeleteLocalRef(argStr);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Support functions that return values.
|
||||
env->CallVoidMethod(functionObj,
|
||||
gSQLiteCustomFunctionClassInfo.dispatchCallback, argsArray);
|
||||
|
||||
error:
|
||||
env->DeleteLocalRef(argsArray);
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(functionObj);
|
||||
|
||||
if (env->ExceptionCheck()) {
|
||||
ALOGE("An exception was thrown by custom SQLite function.");
|
||||
/* LOGE_EX(env); */
|
||||
env->ExceptionClear();
|
||||
}
|
||||
}
|
||||
|
||||
// Called when a custom function is destroyed.
|
||||
static void sqliteCustomFunctionDestructor(void* data) {
|
||||
jobject functionObjGlobal = reinterpret_cast<jobject>(data);
|
||||
JNIEnv* env = 0;
|
||||
gpJavaVM->GetEnv((void**)&env, JNI_VERSION_1_4);
|
||||
env->DeleteGlobalRef(functionObjGlobal);
|
||||
}
|
||||
|
||||
static void nativeRegisterCustomFunction(JNIEnv* env, jclass clazz, jint connectionPtr,
|
||||
jobject functionObj) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
|
||||
jstring nameStr = jstring(env->GetObjectField(
|
||||
functionObj, gSQLiteCustomFunctionClassInfo.name));
|
||||
jint numArgs = env->GetIntField(functionObj, gSQLiteCustomFunctionClassInfo.numArgs);
|
||||
|
||||
jobject functionObjGlobal = env->NewGlobalRef(functionObj);
|
||||
|
||||
const char* name = env->GetStringUTFChars(nameStr, NULL);
|
||||
int err = sqlite3_create_function_v2(connection->db, name, numArgs, SQLITE_UTF16,
|
||||
reinterpret_cast<void*>(functionObjGlobal),
|
||||
&sqliteCustomFunctionCallback, NULL, NULL, &sqliteCustomFunctionDestructor);
|
||||
env->ReleaseStringUTFChars(nameStr, name);
|
||||
|
||||
if (err != SQLITE_OK) {
|
||||
ALOGE("sqlite3_create_function returned %d", err);
|
||||
env->DeleteGlobalRef(functionObjGlobal);
|
||||
throw_sqlite3_exception(env, connection->db);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void nativeRegisterLocalizedCollators(JNIEnv* env, jclass clazz, jint connectionPtr,
|
||||
jstring localeStr) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
|
||||
const char* locale = env->GetStringUTFChars(localeStr, NULL);
|
||||
#if 0
|
||||
int err = register_localized_collators(connection->db, locale, UTF16_STORAGE);
|
||||
env->ReleaseStringUTFChars(localeStr, locale);
|
||||
|
||||
if (err != SQLITE_OK) {
|
||||
throw_sqlite3_exception(env, connection->db);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static jint nativePrepareStatement(JNIEnv* env, jclass clazz, jint connectionPtr,
|
||||
jstring sqlString) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
|
||||
jsize sqlLength = env->GetStringLength(sqlString);
|
||||
const jchar* sql = env->GetStringCritical(sqlString, NULL);
|
||||
sqlite3_stmt* statement;
|
||||
int err = sqlite3_prepare16_v2(connection->db,
|
||||
sql, sqlLength * sizeof(jchar), &statement, NULL);
|
||||
env->ReleaseStringCritical(sqlString, sql);
|
||||
|
||||
if (err != SQLITE_OK) {
|
||||
// Error messages like 'near ")": syntax error' are not
|
||||
// always helpful enough, so construct an error string that
|
||||
// includes the query itself.
|
||||
const char *query = env->GetStringUTFChars(sqlString, NULL);
|
||||
char *message = (char*) malloc(strlen(query) + 50);
|
||||
if (message) {
|
||||
strcpy(message, ", while compiling: "); // less than 50 chars
|
||||
strcat(message, query);
|
||||
}
|
||||
env->ReleaseStringUTFChars(sqlString, query);
|
||||
throw_sqlite3_exception(env, connection->db, message);
|
||||
free(message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ALOGV("Prepared statement %p on connection %p", statement, connection->db);
|
||||
return reinterpret_cast<jint>(statement);
|
||||
}
|
||||
|
||||
static void nativeFinalizeStatement(JNIEnv* env, jclass clazz, jint connectionPtr,
|
||||
jint statementPtr) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
// We ignore the result of sqlite3_finalize because it is really telling us about
|
||||
// whether any errors occurred while executing the statement. The statement itself
|
||||
// is always finalized regardless.
|
||||
ALOGV("Finalized statement %p on connection %p", statement, connection->db);
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
|
||||
static jint nativeGetParameterCount(JNIEnv* env, jclass clazz, jint connectionPtr,
|
||||
jint statementPtr) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
return sqlite3_bind_parameter_count(statement);
|
||||
}
|
||||
|
||||
static jboolean nativeIsReadOnly(JNIEnv* env, jclass clazz, jint connectionPtr,
|
||||
jint statementPtr) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
return sqlite3_stmt_readonly(statement) != 0;
|
||||
}
|
||||
|
||||
static jint nativeGetColumnCount(JNIEnv* env, jclass clazz, jint connectionPtr,
|
||||
jint statementPtr) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
return sqlite3_column_count(statement);
|
||||
}
|
||||
|
||||
static jstring nativeGetColumnName(JNIEnv* env, jclass clazz, jint connectionPtr,
|
||||
jint statementPtr, jint index) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
const jchar* name = static_cast<const jchar*>(sqlite3_column_name16(statement, index));
|
||||
if (name) {
|
||||
size_t length = 0;
|
||||
while (name[length]) {
|
||||
length += 1;
|
||||
}
|
||||
return env->NewString(name, length);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void nativeBindNull(JNIEnv* env, jclass clazz, jint connectionPtr,
|
||||
jint statementPtr, jint index) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
int err = sqlite3_bind_null(statement, index);
|
||||
if (err != SQLITE_OK) {
|
||||
throw_sqlite3_exception(env, connection->db, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void nativeBindLong(JNIEnv* env, jclass clazz, jint connectionPtr,
|
||||
jint statementPtr, jint index, jlong value) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
int err = sqlite3_bind_int64(statement, index, value);
|
||||
if (err != SQLITE_OK) {
|
||||
throw_sqlite3_exception(env, connection->db, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void nativeBindDouble(JNIEnv* env, jclass clazz, jint connectionPtr,
|
||||
jint statementPtr, jint index, jdouble value) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
int err = sqlite3_bind_double(statement, index, value);
|
||||
if (err != SQLITE_OK) {
|
||||
throw_sqlite3_exception(env, connection->db, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void nativeBindString(JNIEnv* env, jclass clazz, jint connectionPtr,
|
||||
jint statementPtr, jint index, jstring valueString) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
jsize valueLength = env->GetStringLength(valueString);
|
||||
const jchar* value = env->GetStringCritical(valueString, NULL);
|
||||
int err = sqlite3_bind_text16(statement, index, value, valueLength * sizeof(jchar),
|
||||
SQLITE_TRANSIENT);
|
||||
env->ReleaseStringCritical(valueString, value);
|
||||
if (err != SQLITE_OK) {
|
||||
throw_sqlite3_exception(env, connection->db, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void nativeBindBlob(JNIEnv* env, jclass clazz, jint connectionPtr,
|
||||
jint statementPtr, jint index, jbyteArray valueArray) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
jsize valueLength = env->GetArrayLength(valueArray);
|
||||
jbyte* value = static_cast<jbyte*>(env->GetPrimitiveArrayCritical(valueArray, NULL));
|
||||
int err = sqlite3_bind_blob(statement, index, value, valueLength, SQLITE_TRANSIENT);
|
||||
env->ReleasePrimitiveArrayCritical(valueArray, value, JNI_ABORT);
|
||||
if (err != SQLITE_OK) {
|
||||
throw_sqlite3_exception(env, connection->db, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void nativeResetStatementAndClearBindings(JNIEnv* env, jclass clazz, jint connectionPtr,
|
||||
jint statementPtr) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
int err = sqlite3_reset(statement);
|
||||
if (err == SQLITE_OK) {
|
||||
err = sqlite3_clear_bindings(statement);
|
||||
}
|
||||
if (err != SQLITE_OK) {
|
||||
throw_sqlite3_exception(env, connection->db, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int executeNonQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement) {
|
||||
int err = sqlite3_step(statement);
|
||||
if (err == SQLITE_ROW) {
|
||||
throw_sqlite3_exception(env,
|
||||
"Queries can be performed using SQLiteDatabase query or rawQuery methods only.");
|
||||
} else if (err != SQLITE_DONE) {
|
||||
throw_sqlite3_exception(env, connection->db);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void nativeExecute(JNIEnv* env, jclass clazz, jint connectionPtr,
|
||||
jint statementPtr) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
executeNonQuery(env, connection, statement);
|
||||
}
|
||||
|
||||
static jint nativeExecuteForChangedRowCount(JNIEnv* env, jclass clazz,
|
||||
jint connectionPtr, jint statementPtr) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
int err = executeNonQuery(env, connection, statement);
|
||||
return err == SQLITE_DONE ? sqlite3_changes(connection->db) : -1;
|
||||
}
|
||||
|
||||
static jlong nativeExecuteForLastInsertedRowId(JNIEnv* env, jclass clazz,
|
||||
jint connectionPtr, jint statementPtr) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
int err = executeNonQuery(env, connection, statement);
|
||||
return err == SQLITE_DONE && sqlite3_changes(connection->db) > 0
|
||||
? sqlite3_last_insert_rowid(connection->db) : -1;
|
||||
}
|
||||
|
||||
static int executeOneRowQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement) {
|
||||
int err = sqlite3_step(statement);
|
||||
if (err != SQLITE_ROW) {
|
||||
throw_sqlite3_exception(env, connection->db);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static jlong nativeExecuteForLong(JNIEnv* env, jclass clazz,
|
||||
jint connectionPtr, jint statementPtr) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
int err = executeOneRowQuery(env, connection, statement);
|
||||
if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
|
||||
return sqlite3_column_int64(statement, 0);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static jstring nativeExecuteForString(JNIEnv* env, jclass clazz,
|
||||
jint connectionPtr, jint statementPtr) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
int err = executeOneRowQuery(env, connection, statement);
|
||||
if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
|
||||
const jchar* text = static_cast<const jchar*>(sqlite3_column_text16(statement, 0));
|
||||
if (text) {
|
||||
size_t length = sqlite3_column_bytes16(statement, 0) / sizeof(jchar);
|
||||
return env->NewString(text, length);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int createAshmemRegionWithData(JNIEnv* env, const void* data, size_t length) {
|
||||
#if 0
|
||||
int error = 0;
|
||||
int fd = ashmem_create_region(NULL, length);
|
||||
if (fd < 0) {
|
||||
error = errno;
|
||||
ALOGE("ashmem_create_region failed: %s", strerror(error));
|
||||
} else {
|
||||
if (length > 0) {
|
||||
void* ptr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (ptr == MAP_FAILED) {
|
||||
error = errno;
|
||||
ALOGE("mmap failed: %s", strerror(error));
|
||||
} else {
|
||||
memcpy(ptr, data, length);
|
||||
munmap(ptr, length);
|
||||
}
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
|
||||
error = errno;
|
||||
ALOGE("ashmem_set_prot_region failed: %s", strerror(errno));
|
||||
} else {
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
#endif
|
||||
jniThrowIOException(env, -1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static jint nativeExecuteForBlobFileDescriptor(JNIEnv* env, jclass clazz,
|
||||
jint connectionPtr, jint statementPtr) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
int err = executeOneRowQuery(env, connection, statement);
|
||||
if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
|
||||
const void* blob = sqlite3_column_blob(statement, 0);
|
||||
if (blob) {
|
||||
int length = sqlite3_column_bytes(statement, 0);
|
||||
if (length >= 0) {
|
||||
return createAshmemRegionWithData(env, blob, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Note: The following symbols must be in the same order as the corresponding
|
||||
** elements in the aMethod[] array in function nativeExecuteForCursorWindow().
|
||||
*/
|
||||
enum CWMethodNames {
|
||||
CW_CLEAR = 0,
|
||||
CW_SETNUMCOLUMNS = 1,
|
||||
CW_ALLOCROW = 2,
|
||||
CW_FREELASTROW = 3,
|
||||
CW_PUTNULL = 4,
|
||||
CW_PUTLONG = 5,
|
||||
CW_PUTDOUBLE = 6,
|
||||
CW_PUTSTRING = 7,
|
||||
CW_PUTBLOB = 8
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of this structure represents a single CursorWindow java method.
|
||||
*/
|
||||
struct CWMethod {
|
||||
jmethodID id; /* Method id */
|
||||
const char *zName; /* Method name */
|
||||
const char *zSig; /* Method JNI signature */
|
||||
};
|
||||
|
||||
/*
|
||||
** Append the contents of the row that SQL statement pStmt currently points to
|
||||
** to the CursorWindow object passed as the second argument. The CursorWindow
|
||||
** currently contains iRow rows. Return true on success or false if an error
|
||||
** occurs.
|
||||
*/
|
||||
static jboolean copyRowToWindow(
|
||||
JNIEnv *pEnv,
|
||||
jobject win,
|
||||
int iRow,
|
||||
sqlite3_stmt *pStmt,
|
||||
CWMethod *aMethod
|
||||
){
|
||||
int nCol = sqlite3_column_count(pStmt);
|
||||
int i;
|
||||
jboolean bOk;
|
||||
|
||||
bOk = pEnv->CallBooleanMethod(win, aMethod[CW_ALLOCROW].id);
|
||||
for(i=0; bOk && i<nCol; i++){
|
||||
switch( sqlite3_column_type(pStmt, i) ){
|
||||
case SQLITE_NULL: {
|
||||
bOk = pEnv->CallBooleanMethod(win, aMethod[CW_PUTNULL].id, iRow, i);
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_INTEGER: {
|
||||
jlong val = sqlite3_column_int64(pStmt, i);
|
||||
bOk = pEnv->CallBooleanMethod(win, aMethod[CW_PUTLONG].id, val, iRow, i);
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_FLOAT: {
|
||||
jdouble val = sqlite3_column_double(pStmt, i);
|
||||
bOk = pEnv->CallBooleanMethod(win, aMethod[CW_PUTDOUBLE].id, val, iRow, i);
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_TEXT: {
|
||||
const char *zVal = (const char*)sqlite3_column_text(pStmt, i);
|
||||
jstring val = pEnv->NewStringUTF(zVal);
|
||||
bOk = pEnv->CallBooleanMethod(win, aMethod[CW_PUTSTRING].id, val, iRow, i);
|
||||
pEnv->DeleteLocalRef(val);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
assert( sqlite3_column_type(pStmt, i)==SQLITE_BLOB );
|
||||
const jbyte *p = (const jbyte*)sqlite3_column_blob(pStmt, i);
|
||||
int n = sqlite3_column_bytes(pStmt, i);
|
||||
jbyteArray val = pEnv->NewByteArray(n);
|
||||
pEnv->SetByteArrayRegion(val, 0, n, p);
|
||||
bOk = pEnv->CallBooleanMethod(win, aMethod[CW_PUTBLOB].id, val, iRow, i);
|
||||
pEnv->DeleteLocalRef(val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( bOk==0 ){
|
||||
pEnv->CallVoidMethod(win, aMethod[CW_FREELASTROW].id);
|
||||
}
|
||||
}
|
||||
|
||||
return bOk;
|
||||
}
|
||||
|
||||
static jboolean setWindowNumColumns(
|
||||
JNIEnv *pEnv,
|
||||
jobject win,
|
||||
sqlite3_stmt *pStmt,
|
||||
CWMethod *aMethod
|
||||
){
|
||||
int nCol;
|
||||
|
||||
pEnv->CallVoidMethod(win, aMethod[CW_CLEAR].id);
|
||||
nCol = sqlite3_column_count(pStmt);
|
||||
return pEnv->CallBooleanMethod(win, aMethod[CW_SETNUMCOLUMNS].id, (jint)nCol);
|
||||
}
|
||||
|
||||
/*
|
||||
** This method has been rewritten for org.sqlite.database.*. The original
|
||||
** android implementation used the C++ interface to populate a CursorWindow
|
||||
** object. Since the NDK does not export this interface, we invoke the Java
|
||||
** interface using standard JNI methods to do the same thing.
|
||||
**
|
||||
** This function executes the SQLite statement object passed as the 4th
|
||||
** argument and copies one or more returned rows into the CursorWindow
|
||||
** object passed as the 5th argument. The set of rows copied into the
|
||||
** CursorWindow is always contiguous.
|
||||
**
|
||||
** The only row that *must* be copied into the CursorWindow is row
|
||||
** iRowRequired. Ideally, all rows from iRowStart through to the end
|
||||
** of the query are copied into the CursorWindow. If this is not possible
|
||||
** (CursorWindow objects have a finite capacity), some compromise position
|
||||
** is found (see comments embedded in the code below for details).
|
||||
**
|
||||
** The return value is a 64-bit integer calculated as follows:
|
||||
**
|
||||
** (iStart << 32) | nRow
|
||||
**
|
||||
** where iStart is the index of the first row copied into the CursorWindow.
|
||||
** If the countAllRows argument is true, nRow is the total number of rows
|
||||
** returned by the query. Otherwise, nRow is one greater than the index of
|
||||
** the last row copied into the CursorWindow.
|
||||
*/
|
||||
static jlong nativeExecuteForCursorWindow(
|
||||
JNIEnv *pEnv,
|
||||
jclass clazz,
|
||||
jint connectionPtr, /* Pointer to SQLiteConnection C++ object */
|
||||
jint statementPtr, /* Pointer to sqlite3_stmt object */
|
||||
jobject win, /* The CursorWindow object to populate */
|
||||
jint startPos, /* First row to add (advisory) */
|
||||
jint iRowRequired, /* Required row */
|
||||
jboolean countAllRows
|
||||
) {
|
||||
SQLiteConnection *pConnection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
sqlite3_stmt *pStmt = reinterpret_cast<sqlite3_stmt*>(statementPtr);
|
||||
|
||||
CWMethod aMethod[] = {
|
||||
{0, "clear", "()V"},
|
||||
{0, "setNumColumns", "(I)Z"},
|
||||
{0, "allocRow", "()Z"},
|
||||
{0, "freeLastRow", "()V"},
|
||||
{0, "putNull", "(II)Z"},
|
||||
{0, "putLong", "(JII)Z"},
|
||||
{0, "putDouble", "(DII)Z"},
|
||||
{0, "putString", "(Ljava/lang/String;II)Z"},
|
||||
{0, "putBlob", "([BII)Z"},
|
||||
};
|
||||
jclass cls; /* Class android.database.CursorWindow */
|
||||
int i; /* Iterator variable */
|
||||
int nCol; /* Number of columns returned by pStmt */
|
||||
int nRow;
|
||||
jboolean bOk;
|
||||
int iStart; /* First row copied to CursorWindow */
|
||||
|
||||
/* Locate all required CursorWindow methods. */
|
||||
cls = pEnv->FindClass("android/database/CursorWindow");
|
||||
for(i=0; i<(sizeof(aMethod)/sizeof(struct CWMethod)); i++){
|
||||
aMethod[i].id = pEnv->GetMethodID(cls, aMethod[i].zName, aMethod[i].zSig);
|
||||
if( aMethod[i].id==NULL ){
|
||||
jniThrowExceptionFmt(pEnv, "java/lang/Exception",
|
||||
"Failed to find method CursorWindow.%s()", aMethod[i].zName
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Set the number of columns in the window */
|
||||
bOk = setWindowNumColumns(pEnv, win, pStmt, aMethod);
|
||||
if( bOk==0 ) return 0;
|
||||
|
||||
nRow = 0;
|
||||
iStart = startPos;
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
/* Only copy in rows that occur at or after row index iStart. */
|
||||
if( nRow>=iStart && bOk ){
|
||||
bOk = copyRowToWindow(pEnv, win, (nRow - iStart), pStmt, aMethod);
|
||||
if( bOk==0 ){
|
||||
/* The CursorWindow object ran out of memory. If row iRowRequired was
|
||||
** not successfully added before this happened, clear the CursorWindow
|
||||
** and try to add the current row again. */
|
||||
if( nRow<=iRowRequired ){
|
||||
bOk = setWindowNumColumns(pEnv, win, pStmt, aMethod);
|
||||
if( bOk==0 ){
|
||||
sqlite3_reset(pStmt);
|
||||
return 0;
|
||||
}
|
||||
iStart = nRow;
|
||||
bOk = copyRowToWindow(pEnv, win, (nRow - iStart), pStmt, aMethod);
|
||||
}
|
||||
|
||||
/* If the CursorWindow is still full and the countAllRows flag is not
|
||||
** set, break out of the loop here. If countAllRows is set, continue
|
||||
** so as to set variable nRow correctly. */
|
||||
if( bOk==0 && countAllRows==0 ) break;
|
||||
}
|
||||
}
|
||||
|
||||
nRow++;
|
||||
}
|
||||
|
||||
/* Finalize the statement. If this indicates an error occurred, throw an
|
||||
** SQLiteException exception. */
|
||||
int rc = sqlite3_reset(pStmt);
|
||||
if( rc!=SQLITE_OK ){
|
||||
throw_sqlite3_exception(pEnv, sqlite3_db_handle(pStmt));
|
||||
return 0;
|
||||
}
|
||||
|
||||
jlong lRet = jlong(iStart) << 32 | jlong(nRow);
|
||||
return lRet;
|
||||
}
|
||||
|
||||
static jint nativeGetDbLookaside(JNIEnv* env, jobject clazz, jint connectionPtr) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
|
||||
int cur = -1;
|
||||
int unused;
|
||||
sqlite3_db_status(connection->db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &unused, 0);
|
||||
return cur;
|
||||
}
|
||||
|
||||
static void nativeCancel(JNIEnv* env, jobject clazz, jint connectionPtr) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
connection->canceled = true;
|
||||
}
|
||||
|
||||
static void nativeResetCancel(JNIEnv* env, jobject clazz, jint connectionPtr,
|
||||
jboolean cancelable) {
|
||||
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
|
||||
connection->canceled = false;
|
||||
|
||||
if (cancelable) {
|
||||
sqlite3_progress_handler(connection->db, 4, sqliteProgressHandlerCallback,
|
||||
connection);
|
||||
} else {
|
||||
sqlite3_progress_handler(connection->db, 0, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static jboolean nativeHasCodec(JNIEnv* env, jobject clazz){
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static JNINativeMethod sMethods[] =
|
||||
{
|
||||
/* name, signature, funcPtr */
|
||||
{ "nativeOpen", "(Ljava/lang/String;ILjava/lang/String;ZZ)I",
|
||||
(void*)nativeOpen },
|
||||
{ "nativeClose", "(I)V",
|
||||
(void*)nativeClose },
|
||||
{ "nativeRegisterCustomFunction", "(ILorg/sqlite/database/sqlite/SQLiteCustomFunction;)V",
|
||||
(void*)nativeRegisterCustomFunction },
|
||||
{ "nativeRegisterLocalizedCollators", "(ILjava/lang/String;)V",
|
||||
(void*)nativeRegisterLocalizedCollators },
|
||||
{ "nativePrepareStatement", "(ILjava/lang/String;)I",
|
||||
(void*)nativePrepareStatement },
|
||||
{ "nativeFinalizeStatement", "(II)V",
|
||||
(void*)nativeFinalizeStatement },
|
||||
{ "nativeGetParameterCount", "(II)I",
|
||||
(void*)nativeGetParameterCount },
|
||||
{ "nativeIsReadOnly", "(II)Z",
|
||||
(void*)nativeIsReadOnly },
|
||||
{ "nativeGetColumnCount", "(II)I",
|
||||
(void*)nativeGetColumnCount },
|
||||
{ "nativeGetColumnName", "(III)Ljava/lang/String;",
|
||||
(void*)nativeGetColumnName },
|
||||
{ "nativeBindNull", "(III)V",
|
||||
(void*)nativeBindNull },
|
||||
{ "nativeBindLong", "(IIIJ)V",
|
||||
(void*)nativeBindLong },
|
||||
{ "nativeBindDouble", "(IIID)V",
|
||||
(void*)nativeBindDouble },
|
||||
{ "nativeBindString", "(IIILjava/lang/String;)V",
|
||||
(void*)nativeBindString },
|
||||
{ "nativeBindBlob", "(III[B)V",
|
||||
(void*)nativeBindBlob },
|
||||
{ "nativeResetStatementAndClearBindings", "(II)V",
|
||||
(void*)nativeResetStatementAndClearBindings },
|
||||
{ "nativeExecute", "(II)V",
|
||||
(void*)nativeExecute },
|
||||
{ "nativeExecuteForLong", "(II)J",
|
||||
(void*)nativeExecuteForLong },
|
||||
{ "nativeExecuteForString", "(II)Ljava/lang/String;",
|
||||
(void*)nativeExecuteForString },
|
||||
{ "nativeExecuteForBlobFileDescriptor", "(II)I",
|
||||
(void*)nativeExecuteForBlobFileDescriptor },
|
||||
{ "nativeExecuteForChangedRowCount", "(II)I",
|
||||
(void*)nativeExecuteForChangedRowCount },
|
||||
{ "nativeExecuteForLastInsertedRowId", "(II)J",
|
||||
(void*)nativeExecuteForLastInsertedRowId },
|
||||
{ "nativeExecuteForCursorWindow", "(IILandroid/database/CursorWindow;IIZ)J",
|
||||
(void*)nativeExecuteForCursorWindow },
|
||||
{ "nativeGetDbLookaside", "(I)I",
|
||||
(void*)nativeGetDbLookaside },
|
||||
{ "nativeCancel", "(I)V",
|
||||
(void*)nativeCancel },
|
||||
{ "nativeResetCancel", "(IZ)V",
|
||||
(void*)nativeResetCancel },
|
||||
|
||||
{ "nativeHasCodec", "()Z", (void*)nativeHasCodec },
|
||||
};
|
||||
|
||||
#define FIND_CLASS(var, className) \
|
||||
var = env->FindClass(className); \
|
||||
LOG_FATAL_IF(! var, "Unable to find class " className);
|
||||
|
||||
#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
|
||||
var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
|
||||
LOG_FATAL_IF(! var, "Unable to find method" methodName);
|
||||
|
||||
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
|
||||
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
|
||||
LOG_FATAL_IF(! var, "Unable to find field " fieldName);
|
||||
|
||||
int register_android_database_SQLiteConnection(JNIEnv *env)
|
||||
{
|
||||
jclass clazz;
|
||||
FIND_CLASS(clazz, "org/sqlite/database/sqlite/SQLiteCustomFunction");
|
||||
|
||||
GET_FIELD_ID(gSQLiteCustomFunctionClassInfo.name, clazz,
|
||||
"name", "Ljava/lang/String;");
|
||||
GET_FIELD_ID(gSQLiteCustomFunctionClassInfo.numArgs, clazz,
|
||||
"numArgs", "I");
|
||||
GET_METHOD_ID(gSQLiteCustomFunctionClassInfo.dispatchCallback,
|
||||
clazz, "dispatchCallback", "([Ljava/lang/String;)V");
|
||||
|
||||
FIND_CLASS(clazz, "java/lang/String");
|
||||
gStringClassInfo.clazz = jclass(env->NewGlobalRef(clazz));
|
||||
|
||||
return jniRegisterNativeMethods(env,
|
||||
"org/sqlite/database/sqlite/SQLiteConnection",
|
||||
sMethods, NELEM(sMethods)
|
||||
);
|
||||
}
|
||||
|
||||
extern int register_android_database_SQLiteGlobal(JNIEnv *env);
|
||||
extern int register_android_database_SQLiteDebug(JNIEnv *env);
|
||||
|
||||
} // namespace android
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
JNIEnv *env = 0;
|
||||
|
||||
android::gpJavaVM = vm;
|
||||
vm->GetEnv((void**)&env, JNI_VERSION_1_4);
|
||||
|
||||
android::register_android_database_SQLiteConnection(env);
|
||||
android::register_android_database_SQLiteDebug(env);
|
||||
android::register_android_database_SQLiteGlobal(env);
|
||||
|
||||
return JNI_VERSION_1_4;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
/*
|
||||
** Modified to support SQLite extensions by the SQLite developers:
|
||||
** sqlite-dev@sqlite.org.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "SQLiteDebug"
|
||||
|
||||
#include <jni.h>
|
||||
#include <JNIHelp.h>
|
||||
#include <ALog-priv.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
static struct {
|
||||
jfieldID memoryUsed;
|
||||
jfieldID pageCacheOverflow;
|
||||
jfieldID largestMemAlloc;
|
||||
} gSQLiteDebugPagerStatsClassInfo;
|
||||
|
||||
static void nativeGetPagerStats(JNIEnv *env, jobject clazz, jobject statsObj)
|
||||
{
|
||||
int memoryUsed;
|
||||
int pageCacheOverflow;
|
||||
int largestMemAlloc;
|
||||
int unused;
|
||||
|
||||
sqlite3_status(SQLITE_STATUS_MEMORY_USED, &memoryUsed, &unused, 0);
|
||||
sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &unused, &largestMemAlloc, 0);
|
||||
sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &pageCacheOverflow, &unused, 0);
|
||||
env->SetIntField(statsObj, gSQLiteDebugPagerStatsClassInfo.memoryUsed, memoryUsed);
|
||||
env->SetIntField(statsObj, gSQLiteDebugPagerStatsClassInfo.pageCacheOverflow,
|
||||
pageCacheOverflow);
|
||||
env->SetIntField(statsObj, gSQLiteDebugPagerStatsClassInfo.largestMemAlloc, largestMemAlloc);
|
||||
}
|
||||
|
||||
/*
|
||||
* JNI registration.
|
||||
*/
|
||||
|
||||
static JNINativeMethod gMethods[] =
|
||||
{
|
||||
{ "nativeGetPagerStats", "(Lorg/sqlite/database/sqlite/SQLiteDebug$PagerStats;)V",
|
||||
(void*) nativeGetPagerStats },
|
||||
};
|
||||
|
||||
#define FIND_CLASS(var, className) \
|
||||
var = env->FindClass(className); \
|
||||
LOG_FATAL_IF(! var, "Unable to find class " className);
|
||||
|
||||
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
|
||||
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
|
||||
LOG_FATAL_IF(! var, "Unable to find field " fieldName);
|
||||
|
||||
int register_android_database_SQLiteDebug(JNIEnv *env)
|
||||
{
|
||||
jclass clazz;
|
||||
FIND_CLASS(clazz, "org/sqlite/database/sqlite/SQLiteDebug$PagerStats");
|
||||
|
||||
GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.memoryUsed, clazz,
|
||||
"memoryUsed", "I");
|
||||
GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.largestMemAlloc, clazz,
|
||||
"largestMemAlloc", "I");
|
||||
GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.pageCacheOverflow, clazz,
|
||||
"pageCacheOverflow", "I");
|
||||
|
||||
return jniRegisterNativeMethods(env, "org/sqlite/database/sqlite/SQLiteDebug",
|
||||
gMethods, NELEM(gMethods));
|
||||
}
|
||||
|
||||
} // namespace android
|
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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.
|
||||
*/
|
||||
/*
|
||||
** Modified to support SQLite extensions by the SQLite developers:
|
||||
** sqlite-dev@sqlite.org.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "SQLiteGlobal"
|
||||
|
||||
#include <jni.h>
|
||||
#include <JNIHelp.h>
|
||||
#include "ALog-priv.h"
|
||||
|
||||
|
||||
#include <sqlite3.h>
|
||||
#if 0
|
||||
#include <sqlite3_android.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "android_database_SQLiteCommon.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
// Limit heap to 8MB for now. This is 4 times the maximum cursor window
|
||||
// size, as has been used by the original code in SQLiteDatabase for
|
||||
// a long time.
|
||||
static const int SOFT_HEAP_LIMIT = 8 * 1024 * 1024;
|
||||
|
||||
|
||||
// Called each time a message is logged.
|
||||
static void sqliteLogCallback(void* data, int iErrCode, const char* zMsg) {
|
||||
bool verboseLog = !!data;
|
||||
if (iErrCode == 0 || iErrCode == SQLITE_CONSTRAINT || iErrCode == SQLITE_SCHEMA) {
|
||||
if (verboseLog) {
|
||||
ALOG(LOG_VERBOSE, SQLITE_LOG_TAG, "(%d) %s\n", iErrCode, zMsg);
|
||||
}
|
||||
} else {
|
||||
ALOG(LOG_ERROR, SQLITE_LOG_TAG, "(%d) %s\n", iErrCode, zMsg);
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the global SQLite configuration.
|
||||
// This must be called before any other SQLite functions are called.
|
||||
static void sqliteInitialize() {
|
||||
// Enable multi-threaded mode. In this mode, SQLite is safe to use by multiple
|
||||
// threads as long as no two threads use the same database connection at the same
|
||||
// time (which we guarantee in the SQLite database wrappers).
|
||||
sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
|
||||
|
||||
// Redirect SQLite log messages to the Android log.
|
||||
#if 0
|
||||
bool verboseLog = android_util_Log_isVerboseLogEnabled(SQLITE_LOG_TAG);
|
||||
#endif
|
||||
bool verboseLog = false;
|
||||
sqlite3_config(SQLITE_CONFIG_LOG, &sqliteLogCallback, verboseLog ? (void*)1 : NULL);
|
||||
|
||||
// The soft heap limit prevents the page cache allocations from growing
|
||||
// beyond the given limit, no matter what the max page cache sizes are
|
||||
// set to. The limit does not, as of 3.5.0, affect any other allocations.
|
||||
sqlite3_soft_heap_limit(SOFT_HEAP_LIMIT);
|
||||
|
||||
// Initialize SQLite.
|
||||
sqlite3_initialize();
|
||||
}
|
||||
|
||||
static jint nativeReleaseMemory(JNIEnv* env, jclass clazz) {
|
||||
return sqlite3_release_memory(SOFT_HEAP_LIMIT);
|
||||
}
|
||||
|
||||
static JNINativeMethod sMethods[] =
|
||||
{
|
||||
/* name, signature, funcPtr */
|
||||
{ "nativeReleaseMemory", "()I",
|
||||
(void*)nativeReleaseMemory },
|
||||
};
|
||||
|
||||
int register_android_database_SQLiteGlobal(JNIEnv *env)
|
||||
{
|
||||
sqliteInitialize();
|
||||
|
||||
return jniRegisterNativeMethods(env, "org/sqlite/database/sqlite/SQLiteGlobal",
|
||||
sMethods, NELEM(sMethods));
|
||||
}
|
||||
|
||||
} // namespace android
|
191
packages/swig/android/jni/sqlite/nativehelper/JNIHelp.h
Normal file
191
packages/swig/android/jni/sqlite/nativehelper/JNIHelp.h
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*
|
||||
* This file may be included by C or C++ code, which is trouble because jni.h
|
||||
* uses different typedefs for JNIEnv in each language.
|
||||
*
|
||||
* TODO: remove C support.
|
||||
*/
|
||||
#ifndef NATIVEHELPER_JNIHELP_H_
|
||||
#define NATIVEHELPER_JNIHELP_H_
|
||||
|
||||
#include "jni.h"
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef NELEM
|
||||
# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Register one or more native methods with a particular class.
|
||||
* "className" looks like "java/lang/String". Aborts on failure.
|
||||
* TODO: fix all callers and change the return type to void.
|
||||
*/
|
||||
int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods);
|
||||
|
||||
/*
|
||||
* Throw an exception with the specified class and an optional message.
|
||||
*
|
||||
* The "className" argument will be passed directly to FindClass, which
|
||||
* takes strings with slashes (e.g. "java/lang/Object").
|
||||
*
|
||||
* If an exception is currently pending, we log a warning message and
|
||||
* clear it.
|
||||
*
|
||||
* Returns 0 on success, nonzero if something failed (e.g. the exception
|
||||
* class couldn't be found, so *an* exception will still be pending).
|
||||
*
|
||||
* Currently aborts the VM if it can't throw the exception.
|
||||
*/
|
||||
int jniThrowException(C_JNIEnv* env, const char* className, const char* msg);
|
||||
|
||||
/*
|
||||
* Throw a java.lang.NullPointerException, with an optional message.
|
||||
*/
|
||||
int jniThrowNullPointerException(C_JNIEnv* env, const char* msg);
|
||||
|
||||
/*
|
||||
* Throw a java.lang.RuntimeException, with an optional message.
|
||||
*/
|
||||
int jniThrowRuntimeException(C_JNIEnv* env, const char* msg);
|
||||
|
||||
/*
|
||||
* Throw a java.io.IOException, generating the message from errno.
|
||||
*/
|
||||
int jniThrowIOException(C_JNIEnv* env, int errnum);
|
||||
|
||||
/*
|
||||
* Return a pointer to a locale-dependent error string explaining errno
|
||||
* value 'errnum'. The returned pointer may or may not be equal to 'buf'.
|
||||
* This function is thread-safe (unlike strerror) and portable (unlike
|
||||
* strerror_r).
|
||||
*/
|
||||
const char* jniStrError(int errnum, char* buf, size_t buflen);
|
||||
|
||||
/*
|
||||
* Returns a new java.io.FileDescriptor for the given int fd.
|
||||
*/
|
||||
jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd);
|
||||
|
||||
/*
|
||||
* Returns the int fd from a java.io.FileDescriptor.
|
||||
*/
|
||||
int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor);
|
||||
|
||||
/*
|
||||
* Sets the int fd in a java.io.FileDescriptor.
|
||||
*/
|
||||
void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value);
|
||||
|
||||
/*
|
||||
* Returns the reference from a java.lang.ref.Reference.
|
||||
*/
|
||||
jobject jniGetReferent(C_JNIEnv* env, jobject ref);
|
||||
|
||||
/*
|
||||
* Log a message and an exception.
|
||||
* If exception is NULL, logs the current exception in the JNI environment.
|
||||
*/
|
||||
void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* For C++ code, we provide inlines that map to the C functions. g++ always
|
||||
* inlines these, even on non-optimized builds.
|
||||
*/
|
||||
#if defined(__cplusplus)
|
||||
inline int jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods) {
|
||||
return jniRegisterNativeMethods(&env->functions, className, gMethods, numMethods);
|
||||
}
|
||||
|
||||
inline int jniThrowException(JNIEnv* env, const char* className, const char* msg) {
|
||||
return jniThrowException(&env->functions, className, msg);
|
||||
}
|
||||
|
||||
extern "C" int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args);
|
||||
|
||||
/*
|
||||
* Equivalent to jniThrowException but with a printf-like format string and
|
||||
* variable-length argument list. This is only available in C++.
|
||||
*/
|
||||
inline int jniThrowExceptionFmt(JNIEnv* env, const char* className, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
return jniThrowExceptionFmt(&env->functions, className, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
inline int jniThrowNullPointerException(JNIEnv* env, const char* msg) {
|
||||
return jniThrowNullPointerException(&env->functions, msg);
|
||||
}
|
||||
|
||||
inline int jniThrowRuntimeException(JNIEnv* env, const char* msg) {
|
||||
return jniThrowRuntimeException(&env->functions, msg);
|
||||
}
|
||||
|
||||
inline int jniThrowIOException(JNIEnv* env, int errnum) {
|
||||
return jniThrowIOException(&env->functions, errnum);
|
||||
}
|
||||
|
||||
inline jobject jniCreateFileDescriptor(JNIEnv* env, int fd) {
|
||||
return jniCreateFileDescriptor(&env->functions, fd);
|
||||
}
|
||||
|
||||
inline int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
|
||||
return jniGetFDFromFileDescriptor(&env->functions, fileDescriptor);
|
||||
}
|
||||
|
||||
inline void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) {
|
||||
jniSetFileDescriptorOfFD(&env->functions, fileDescriptor, value);
|
||||
}
|
||||
|
||||
inline jobject jniGetReferent(JNIEnv* env, jobject ref) {
|
||||
return jniGetReferent(&env->functions, ref);
|
||||
}
|
||||
|
||||
inline void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable exception = NULL) {
|
||||
jniLogException(&env->functions, priority, tag, exception);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TEMP_FAILURE_RETRY is defined by some, but not all, versions of
|
||||
* <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
|
||||
* not already defined, then define it here.
|
||||
*/
|
||||
#ifndef TEMP_FAILURE_RETRY
|
||||
/* Used to retry syscalls that can return EINTR. */
|
||||
#define TEMP_FAILURE_RETRY(exp) ({ \
|
||||
typeof (exp) _rc; \
|
||||
do { \
|
||||
_rc = (exp); \
|
||||
} while (_rc == -1 && errno == EINTR); \
|
||||
_rc; })
|
||||
#endif
|
||||
|
||||
#endif /* NATIVEHELPER_JNIHELP_H_ */
|
99
packages/swig/android/jni/sqlite/nativehelper/JniConstants.h
Normal file
99
packages/swig/android/jni/sqlite/nativehelper/JniConstants.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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.
|
||||
*/
|
||||
|
||||
#ifndef JNI_CONSTANTS_H_included
|
||||
#define JNI_CONSTANTS_H_included
|
||||
|
||||
#include "JNIHelp.h"
|
||||
|
||||
/**
|
||||
* A cache to avoid calling FindClass at runtime.
|
||||
*
|
||||
* Class lookup is relatively expensive (2.5us on passion-eng at the time of writing), so we do
|
||||
* all such lookups eagerly at startup. This means that code that never uses, say,
|
||||
* java.util.zip.Deflater still has to pay for the lookup, but it means that on a device the cost
|
||||
* is definitely paid during boot and amortized. A central cache also removes the temptation to
|
||||
* dynamically call FindClass rather than add a small cache to each file that needs one. Another
|
||||
* cost is that each class cached here requires a global reference, though in practice we save
|
||||
* enough by not having a global reference for each file that uses a class such as java.lang.String
|
||||
* which is used in several files.
|
||||
*
|
||||
* FindClass is still called in a couple of situations: when throwing exceptions, and in some of
|
||||
* the serialization code. The former is clearly not a performance case, and we're currently
|
||||
* assuming that neither is the latter.
|
||||
*
|
||||
* TODO: similar arguments hold for field and method IDs; we should cache them centrally too.
|
||||
*/
|
||||
struct JniConstants {
|
||||
static void init(JNIEnv* env);
|
||||
|
||||
static jclass bidiRunClass;
|
||||
static jclass bigDecimalClass;
|
||||
static jclass booleanClass;
|
||||
static jclass byteArrayClass;
|
||||
static jclass byteClass;
|
||||
static jclass calendarClass;
|
||||
static jclass characterClass;
|
||||
static jclass charsetICUClass;
|
||||
static jclass constructorClass;
|
||||
static jclass deflaterClass;
|
||||
static jclass doubleClass;
|
||||
static jclass errnoExceptionClass;
|
||||
static jclass fieldClass;
|
||||
static jclass fieldPositionIteratorClass;
|
||||
static jclass fileDescriptorClass;
|
||||
static jclass floatClass;
|
||||
static jclass gaiExceptionClass;
|
||||
static jclass inet6AddressClass;
|
||||
static jclass inetAddressClass;
|
||||
static jclass inetSocketAddressClass;
|
||||
static jclass inetUnixAddressClass;
|
||||
static jclass inflaterClass;
|
||||
static jclass inputStreamClass;
|
||||
static jclass integerClass;
|
||||
static jclass localeDataClass;
|
||||
static jclass longClass;
|
||||
static jclass methodClass;
|
||||
static jclass mutableIntClass;
|
||||
static jclass mutableLongClass;
|
||||
static jclass objectClass;
|
||||
static jclass objectArrayClass;
|
||||
static jclass outputStreamClass;
|
||||
static jclass parsePositionClass;
|
||||
static jclass patternSyntaxExceptionClass;
|
||||
static jclass realToStringClass;
|
||||
static jclass referenceClass;
|
||||
static jclass shortClass;
|
||||
static jclass socketClass;
|
||||
static jclass socketImplClass;
|
||||
static jclass stringClass;
|
||||
static jclass structAddrinfoClass;
|
||||
static jclass structFlockClass;
|
||||
static jclass structGroupReqClass;
|
||||
static jclass structLingerClass;
|
||||
static jclass structPasswdClass;
|
||||
static jclass structPollfdClass;
|
||||
static jclass structStatClass;
|
||||
static jclass structStatVfsClass;
|
||||
static jclass structTimevalClass;
|
||||
static jclass structUcredClass;
|
||||
static jclass structUtsnameClass;
|
||||
};
|
||||
|
||||
#define NATIVE_METHOD(className, functionName, signature) \
|
||||
{ #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName) }
|
||||
|
||||
#endif // JNI_CONSTANTS_H_included
|
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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.
|
||||
*/
|
||||
|
||||
#ifndef SCOPED_LOCAL_REF_H_included
|
||||
#define SCOPED_LOCAL_REF_H_included
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
// A smart pointer that deletes a JNI local reference when it goes out of scope.
|
||||
template<typename T>
|
||||
class ScopedLocalRef {
|
||||
public:
|
||||
ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) {
|
||||
}
|
||||
|
||||
~ScopedLocalRef() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset(T ptr = NULL) {
|
||||
if (ptr != mLocalRef) {
|
||||
if (mLocalRef != NULL) {
|
||||
mEnv->DeleteLocalRef(mLocalRef);
|
||||
}
|
||||
mLocalRef = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
T release() __attribute__((warn_unused_result)) {
|
||||
T localRef = mLocalRef;
|
||||
mLocalRef = NULL;
|
||||
return localRef;
|
||||
}
|
||||
|
||||
T get() const {
|
||||
return mLocalRef;
|
||||
}
|
||||
|
||||
private:
|
||||
JNIEnv* mEnv;
|
||||
T mLocalRef;
|
||||
|
||||
// Disallow copy and assignment.
|
||||
ScopedLocalRef(const ScopedLocalRef&);
|
||||
void operator=(const ScopedLocalRef&);
|
||||
};
|
||||
|
||||
#endif // SCOPED_LOCAL_REF_H_included
|
1141
packages/swig/android/jni/sqlite/nativehelper/jni.h
Normal file
1141
packages/swig/android/jni/sqlite/nativehelper/jni.h
Normal file
File diff suppressed because it is too large
Load Diff
144909
packages/swig/android/jni/sqlite/sqlite3.c
Normal file
144909
packages/swig/android/jni/sqlite/sqlite3.c
Normal file
File diff suppressed because it is too large
Load Diff
7382
packages/swig/android/jni/sqlite/sqlite3.h
Normal file
7382
packages/swig/android/jni/sqlite/sqlite3.h
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user