SQLite for android

This commit is contained in:
Vítor Santos Costa
2014-07-15 18:57:48 -05:00
parent 369c64d3d3
commit e918226918
65 changed files with 165611 additions and 4 deletions

View 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

View 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)

View 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);
}

View 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");
}

View 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.

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View 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_ */

View 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

View File

@@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff