Rewrite SQLite database wrappers.

The main theme of this change is encapsulation.  This change
preserves all existing functionality but the implementation
is now much cleaner.

Instead of a "database lock", access to the database is treated
as a resource acquisition problem.  If a thread's owns a database
connection, then it can access the database; otherwise, it must
acquire a database connection first, and potentially wait for other
threads to give up theirs.  The SQLiteConnectionPool encapsulates
the details of how connections are created, configured, acquired,
released and disposed.

One new feature is that SQLiteConnectionPool can make scheduling
decisions about which thread should next acquire a database
connection when there is contention among threads.  The factors
considered include wait queue ordering (fairness among peers),
whether the connection is needed for an interactive operation
(unfairness on behalf of the UI), and whether the primary connection
is needed or if any old connection will do.  Thus one goal of the
new SQLiteConnectionPool is to improve the utilization of
database connections.

To emulate some quirks of the old "database lock," we introduce
the concept of the primary database connection.  The primary
database connection is the one that is typically used to perform
write operations to the database.  When a thread holds the primary
database connection, it effectively prevents other threads from
modifying the database (although they can still read).  What's
more, those threads will block when they try to acquire the primary
connection, which provides the same kind of mutual exclusion
features that the old "database lock" had.  (In truth, we
probably don't need to be requiring use of the primary database
connection in as many places as we do now, but we can seek to refine
that behavior in future patches.)

Another significant change is that native sqlite3_stmt objects
(prepared statements) are fully encapsulated by the SQLiteConnection
object that owns them.  This ensures that the connection can
finalize (destroy) all extant statements that belong to a database
connection when the connection is closed.  (In the original code,
this was very complicated because the sqlite3_stmt objects were
managed by SQLiteCompiledSql objects which had different lifetime
from the original SQLiteDatabase that created them.  Worse, the
SQLiteCompiledSql finalizer method couldn't actually destroy the
sqlite3_stmt objects because it ran on the finalizer thread and
therefore could not guarantee that it could acquire the database
lock in order to do the work.  This resulted in some rather
tortured logic involving a list of pending finalizable statements
and a high change of deadlocks or leaks.)

Because sqlite3_stmt objects never escape the confines of the
SQLiteConnection that owns them, we can also greatly simplify
the design of the SQLiteProgram, SQLiteQuery and SQLiteStatement
objects.  They no longer have to wrangle a native sqlite3_stmt
object pointer and manage its lifecycle.  So now all they do
is hold bind arguments and provide a fancy API.

All of the JNI glue related to managing database connections
and performing transactions is now bound to SQLiteConnection
(rather than being scattered everywhere).  This makes sense because
SQLiteConnection owns the native sqlite3 object, so it is the
only class in the system that can interact with the native
SQLite database directly.  Encapsulation for the win.

One particularly tricky part of this change is managing the
ownership of SQLiteConnection objects.  At any given time,
a SQLiteConnection is either owned by a SQLiteConnectionPool
or by a SQLiteSession.  SQLiteConnections should never be leaked,
but we handle that case too (and yell about it with CloseGuard).

A SQLiteSession object is responsible for acquiring and releasing
a SQLiteConnection object on behalf of a single thread as needed.
For example, the session acquires a connection when a transaction
begins and releases it when finished.  If the session cannot
acquire a connection immediately, then the requested operation
blocks until a connection becomes available.

SQLiteSessions are thread-local.  A SQLiteDatabase assigns a
distinct session to each thread that performs database operations.
This is very very important.  First, it prevents two threads
from trying to use the same SQLiteConnection at the same time
(because two threads can't share the same session).
Second, it prevents a single thread from trying to acquire two
SQLiteConnections simultaneously from the same database (because
a single thread can't have two sessions for the same database which,
in addition to being greedy, could result in a deadlock).

There is strict layering between the various database objects,
objects at lower layers are not aware of objects at higher layers.
Moreover, objects at higher layers generally own objects at lower
layers and are responsible for ensuring they are properly disposed
when no longer needed (good for the environment).

API layer: SQLiteDatabase, SQLiteProgram, SQLiteQuery, SQLiteStatement.
Session layer: SQLiteSession.
Connection layer: SQLiteConnectionPool, SQLiteConnection.
Native layer: JNI glue.

By avoiding cyclic dependencies between layers, we make the
architecture much more intelligible, maintainable and robust.

Finally, this change adds a great deal of new debugging information.
It is now possible to view a list of the most recent database
operations including how long they took to run using
"adb shell dumpsys dbinfo".  (Because most of the interesting
work happens in SQLiteConnection, it is easy to add debugging
instrumentation to track all database operations in one place.)

Change-Id: Iffb4ce72d8bcf20b4e087d911da6aa84d2f15297
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 8be1996..39b84bb 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -39,12 +39,10 @@
 	android_opengl_GLES11Ext.cpp \
 	android_opengl_GLES20.cpp \
 	android_database_CursorWindow.cpp \
-	android_database_SQLiteCompiledSql.cpp \
+	android_database_SQLiteCommon.cpp \
+	android_database_SQLiteConnection.cpp \
+	android_database_SQLiteGlobal.cpp \
 	android_database_SQLiteDebug.cpp \
-	android_database_SQLiteDatabase.cpp \
-	android_database_SQLiteProgram.cpp \
-	android_database_SQLiteQuery.cpp \
-	android_database_SQLiteStatement.cpp \
 	android_emoji_EmojiFactory.cpp \
 	android_view_Display.cpp \
 	android_view_DisplayEventReceiver.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index c006615..8a3063f 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -121,12 +121,9 @@
 extern int register_android_view_Surface(JNIEnv* env);
 extern int register_android_view_TextureView(JNIEnv* env);
 extern int register_android_database_CursorWindow(JNIEnv* env);
-extern int register_android_database_SQLiteCompiledSql(JNIEnv* env);
-extern int register_android_database_SQLiteDatabase(JNIEnv* env);
+extern int register_android_database_SQLiteConnection(JNIEnv* env);
+extern int register_android_database_SQLiteGlobal(JNIEnv* env);
 extern int register_android_database_SQLiteDebug(JNIEnv* env);
-extern int register_android_database_SQLiteProgram(JNIEnv* env);
-extern int register_android_database_SQLiteQuery(JNIEnv* env);
-extern int register_android_database_SQLiteStatement(JNIEnv* env);
 extern int register_android_debug_JNITest(JNIEnv* env);
 extern int register_android_nio_utils(JNIEnv* env);
 extern int register_android_text_format_Time(JNIEnv* env);
@@ -1141,12 +1138,9 @@
     REG_JNI(register_android_graphics_YuvImage),
 
     REG_JNI(register_android_database_CursorWindow),
-    REG_JNI(register_android_database_SQLiteCompiledSql),
-    REG_JNI(register_android_database_SQLiteDatabase),
+    REG_JNI(register_android_database_SQLiteConnection),
+    REG_JNI(register_android_database_SQLiteGlobal),
     REG_JNI(register_android_database_SQLiteDebug),
-    REG_JNI(register_android_database_SQLiteProgram),
-    REG_JNI(register_android_database_SQLiteQuery),
-    REG_JNI(register_android_database_SQLiteStatement),
     REG_JNI(register_android_os_Debug),
     REG_JNI(register_android_os_FileObserver),
     REG_JNI(register_android_os_FileUtils),
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index 659fa35..d53644d 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -31,8 +31,8 @@
 #include <unistd.h>
 
 #include "binder/CursorWindow.h"
-#include "sqlite3_exception.h"
 #include "android_util_Binder.h"
+#include "android_database_SQLiteCommon.h"
 
 namespace android {
 
diff --git a/core/jni/android_database_SQLiteCommon.cpp b/core/jni/android_database_SQLiteCommon.cpp
new file mode 100644
index 0000000..d5fdb15
--- /dev/null
+++ b/core/jni/android_database_SQLiteCommon.cpp
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+#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) {
+        throw_sqlite3_exception(env, sqlite3_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 */
+void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message) {
+    if (errcode == SQLITE_DONE) {
+        throw_sqlite3_exception(env, errcode, NULL, message);
+    } else {
+        char temp[21];
+        sprintf(temp, "error code %d", errcode);
+        throw_sqlite3_exception(env, errcode, temp, 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) {
+        case SQLITE_IOERR:
+            exceptionClass = "android/database/sqlite/SQLiteDiskIOException";
+            break;
+        case SQLITE_CORRUPT:
+        case SQLITE_NOTADB: // treat "unsupported file format" error as corruption also
+            exceptionClass = "android/database/sqlite/SQLiteDatabaseCorruptException";
+            break;
+        case SQLITE_CONSTRAINT:
+           exceptionClass = "android/database/sqlite/SQLiteConstraintException";
+           break;
+        case SQLITE_ABORT:
+           exceptionClass = "android/database/sqlite/SQLiteAbortException";
+           break;
+        case SQLITE_DONE:
+           exceptionClass = "android/database/sqlite/SQLiteDoneException";
+           break;
+        case SQLITE_FULL:
+           exceptionClass = "android/database/sqlite/SQLiteFullException";
+           break;
+        case SQLITE_MISUSE:
+           exceptionClass = "android/database/sqlite/SQLiteMisuseException";
+           break;
+        case SQLITE_PERM:
+           exceptionClass = "android/database/sqlite/SQLiteAccessPermException";
+           break;
+        case SQLITE_BUSY:
+           exceptionClass = "android/database/sqlite/SQLiteDatabaseLockedException";
+           break;
+        case SQLITE_LOCKED:
+           exceptionClass = "android/database/sqlite/SQLiteTableLockedException";
+           break;
+        case SQLITE_READONLY:
+           exceptionClass = "android/database/sqlite/SQLiteReadOnlyDatabaseException";
+           break;
+        case SQLITE_CANTOPEN:
+           exceptionClass = "android/database/sqlite/SQLiteCantOpenDatabaseException";
+           break;
+        case SQLITE_TOOBIG:
+           exceptionClass = "android/database/sqlite/SQLiteBlobTooBigException";
+           break;
+        case SQLITE_RANGE:
+           exceptionClass = "android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException";
+           break;
+        case SQLITE_NOMEM:
+           exceptionClass = "android/database/sqlite/SQLiteOutOfMemoryException";
+           break;
+        case SQLITE_MISMATCH:
+           exceptionClass = "android/database/sqlite/SQLiteDatatypeMismatchException";
+           break;
+        case SQLITE_UNCLOSED:
+           exceptionClass = "android/database/sqlite/SQLiteUnfinalizedObjectsException";
+           break;
+        default:
+           exceptionClass = "android/database/sqlite/SQLiteException";
+           break;
+    }
+
+    if (sqlite3Message != NULL && message != NULL) {
+        char* fullMessage = (char *)malloc(strlen(sqlite3Message) + strlen(message) + 3);
+        if (fullMessage != NULL) {
+            strcpy(fullMessage, sqlite3Message);
+            strcat(fullMessage, ": ");
+            strcat(fullMessage, message);
+            jniThrowException(env, exceptionClass, fullMessage);
+            free(fullMessage);
+        } else {
+            jniThrowException(env, exceptionClass, sqlite3Message);
+        }
+    } else if (sqlite3Message != NULL) {
+        jniThrowException(env, exceptionClass, sqlite3Message);
+    } else {
+        jniThrowException(env, exceptionClass, message);
+    }
+}
+
+
+} // namespace android
diff --git a/core/jni/android_database_SQLiteCommon.h b/core/jni/android_database_SQLiteCommon.h
new file mode 100644
index 0000000..0cac176
--- /dev/null
+++ b/core/jni/android_database_SQLiteCommon.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#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
diff --git a/core/jni/android_database_SQLiteCompiledSql.cpp b/core/jni/android_database_SQLiteCompiledSql.cpp
deleted file mode 100644
index 857267a..0000000
--- a/core/jni/android_database_SQLiteCompiledSql.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2006-2008 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "Cursor"
-
-#include <jni.h>
-#include <JNIHelp.h>
-#include <android_runtime/AndroidRuntime.h>
-
-#include <sqlite3.h>
-
-#include <utils/Log.h>
-
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "sqlite3_exception.h"
-
-
-namespace android {
-
-static jfieldID gHandleField;
-static jfieldID gStatementField;
-
-
-#define GET_STATEMENT(env, object) \
-        (sqlite3_stmt *)env->GetIntField(object, gStatementField)
-#define GET_HANDLE(env, object) \
-        (sqlite3 *)env->GetIntField(object, gHandleField)
-
-
-sqlite3_stmt * compile(JNIEnv* env, jobject object,
-                       sqlite3 * handle, jstring sqlString)
-{
-    int err;
-    jchar const * sql;
-    jsize sqlLen;
-    sqlite3_stmt * statement = GET_STATEMENT(env, object);
-
-    // Make sure not to leak the statement if it already exists
-    if (statement != NULL) {
-        sqlite3_finalize(statement);
-        env->SetIntField(object, gStatementField, 0);
-    }
-
-    // Compile the SQL
-    sql = env->GetStringChars(sqlString, NULL);
-    sqlLen = env->GetStringLength(sqlString);
-    err = sqlite3_prepare16_v2(handle, sql, sqlLen * 2, &statement, NULL);
-    env->ReleaseStringChars(sqlString, sql);
-
-    if (err == SQLITE_OK) {
-        // Store the statement in the Java object for future calls
-        ALOGV("Prepared statement %p on %p", statement, handle);
-        env->SetIntField(object, gStatementField, (int)statement);
-        return statement;
-    } else {
-        // 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, handle, message);
-        free(message);
-        return NULL;
-    }
-}
-
-static void native_compile(JNIEnv* env, jobject object, jstring sqlString)
-{
-    compile(env, object, GET_HANDLE(env, object), sqlString);
-}
-
-
-static JNINativeMethod sMethods[] =
-{
-     /* name, signature, funcPtr */
-    {"native_compile", "(Ljava/lang/String;)V", (void *)native_compile},
-};
-
-int register_android_database_SQLiteCompiledSql(JNIEnv * env)
-{
-    jclass clazz;
-
-    clazz = env->FindClass("android/database/sqlite/SQLiteCompiledSql");
-    if (clazz == NULL) {
-        ALOGE("Can't find android/database/sqlite/SQLiteCompiledSql");
-        return -1;
-    }
-
-    gHandleField = env->GetFieldID(clazz, "nHandle", "I");
-    gStatementField = env->GetFieldID(clazz, "nStatement", "I");
-
-    if (gHandleField == NULL || gStatementField == NULL) {
-        ALOGE("Error locating fields");
-        return -1;
-    }
-
-    return AndroidRuntime::registerNativeMethods(env,
-        "android/database/sqlite/SQLiteCompiledSql", sMethods, NELEM(sMethods));
-}
-
-} // namespace android
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
new file mode 100644
index 0000000..d0d53f6
--- /dev/null
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -0,0 +1,959 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SQLiteConnection"
+
+#include <jni.h>
+#include <JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include <cutils/ashmem.h>
+#include <sys/mman.h>
+
+#include <string.h>
+#include <unistd.h>
+
+#include "binder/CursorWindow.h"
+
+#include <sqlite3.h>
+#include <sqlite3_android.h>
+
+#include "android_database_SQLiteCommon.h"
+
+#define UTF16_STORAGE 0
+#define ANDROID_TABLE "android_metadata"
+
+namespace android {
+
+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;
+    const String8 path;
+    const String8 label;
+
+    SQLiteConnection(sqlite3* db, int openFlags, const String8& path, const String8& label) :
+        db(db), openFlags(openFlags), path(path), label(label) { }
+};
+
+// 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.string(), 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.string(), sql, tm * 0.000001f);
+}
+
+
+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);
+    String8 path(pathChars);
+    env->ReleaseStringUTFChars(pathStr, pathChars);
+
+    const char* labelChars = env->GetStringUTFChars(labelStr, NULL);
+    String8 label(labelChars);
+    env->ReleaseStringUTFChars(labelStr, labelChars);
+
+    sqlite3* db;
+    int err = sqlite3_open_v2(path.string(), &db, sqliteFlags, NULL);
+    if (err != SQLITE_OK) {
+        throw_sqlite3_exception_errcode(env, err, "Could not open database");
+        return 0;
+    }
+
+    // Set the default busy handler to retry for 1000ms and then return SQLITE_BUSY
+    err = sqlite3_busy_timeout(db, 1000 /* ms */);
+    if (err != SQLITE_OK) {
+        throw_sqlite3_exception(env, db, "Could not set busy timeout");
+        sqlite3_close(db);
+        return 0;
+    }
+
+    // Enable WAL auto-checkpointing after a commit whenever at least one frame is in the log.
+    // This ensures that a checkpoint will occur after each transaction if needed.
+    err = sqlite3_wal_autocheckpoint(db, 1);
+    if (err) {
+        throw_sqlite3_exception(env, db, "Could not enable auto-checkpointing.");
+        sqlite3_close(db);
+        return 0;
+    }
+
+    // Register custom Android functions.
+    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;
+    }
+
+    // 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.string());
+    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 = AndroidRuntime::getJNIEnv();
+
+    // 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 = AndroidRuntime::getJNIEnv();
+    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;
+    }
+}
+
+// Set locale in the android_metadata table, install localized collators, and rebuild indexes
+static void nativeSetLocale(JNIEnv* env, jclass clazz, jint connectionPtr, jstring localeStr) {
+    SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
+
+    if (connection->openFlags & SQLiteConnection::NO_LOCALIZED_COLLATORS) {
+        // We should probably throw IllegalStateException but the contract for
+        // setLocale says that we just do nothing.  Oh well.
+        return;
+    }
+
+    int err;
+    char const* locale = env->GetStringUTFChars(localeStr, NULL);
+    sqlite3_stmt* stmt = NULL;
+    char** meta = NULL;
+    int rowCount, colCount;
+    char* dbLocale = NULL;
+
+    // create the table, if necessary and possible
+    if (!(connection->openFlags & SQLiteConnection::OPEN_READONLY)) {
+        err = sqlite3_exec(connection->db,
+                "CREATE TABLE IF NOT EXISTS " ANDROID_TABLE " (locale TEXT)",
+                NULL, NULL, NULL);
+        if (err != SQLITE_OK) {
+            ALOGE("CREATE TABLE " ANDROID_TABLE " failed");
+            throw_sqlite3_exception(env, connection->db);
+            goto done;
+        }
+    }
+
+    // try to read from the table
+    err = sqlite3_get_table(connection->db,
+            "SELECT locale FROM " ANDROID_TABLE " LIMIT 1",
+            &meta, &rowCount, &colCount, NULL);
+    if (err != SQLITE_OK) {
+        ALOGE("SELECT locale FROM " ANDROID_TABLE " failed");
+        throw_sqlite3_exception(env, connection->db);
+        goto done;
+    }
+
+    dbLocale = (rowCount >= 1) ? meta[colCount] : NULL;
+
+    if (dbLocale != NULL && !strcmp(dbLocale, locale)) {
+        // database locale is the same as the desired locale; set up the collators and go
+        err = register_localized_collators(connection->db, locale, UTF16_STORAGE);
+        if (err != SQLITE_OK) {
+            throw_sqlite3_exception(env, connection->db);
+        }
+        goto done;   // no database changes needed
+    }
+
+    if (connection->openFlags & SQLiteConnection::OPEN_READONLY) {
+        // read-only database, so we're going to have to put up with whatever we got
+        // For registering new index. Not for modifing the read-only database.
+        err = register_localized_collators(connection->db, locale, UTF16_STORAGE);
+        if (err != SQLITE_OK) {
+            throw_sqlite3_exception(env, connection->db);
+        }
+        goto done;
+    }
+
+    // need to update android_metadata and indexes atomically, so use a transaction...
+    err = sqlite3_exec(connection->db, "BEGIN TRANSACTION", NULL, NULL, NULL);
+    if (err != SQLITE_OK) {
+        ALOGE("BEGIN TRANSACTION failed setting locale");
+        throw_sqlite3_exception(env, connection->db);
+        goto done;
+    }
+
+    err = register_localized_collators(connection->db, locale, UTF16_STORAGE);
+    if (err != SQLITE_OK) {
+        ALOGE("register_localized_collators() failed setting locale");
+        throw_sqlite3_exception(env, connection->db);
+        goto rollback;
+    }
+
+    err = sqlite3_exec(connection->db, "DELETE FROM " ANDROID_TABLE, NULL, NULL, NULL);
+    if (err != SQLITE_OK) {
+        ALOGE("DELETE failed setting locale");
+        throw_sqlite3_exception(env, connection->db);
+        goto rollback;
+    }
+
+    static const char *sql = "INSERT INTO " ANDROID_TABLE " (locale) VALUES(?);";
+    err = sqlite3_prepare_v2(connection->db, sql, -1, &stmt, NULL);
+    if (err != SQLITE_OK) {
+        ALOGE("sqlite3_prepare_v2(\"%s\") failed", sql);
+        throw_sqlite3_exception(env, connection->db);
+        goto rollback;
+    }
+
+    err = sqlite3_bind_text(stmt, 1, locale, -1, SQLITE_TRANSIENT);
+    if (err != SQLITE_OK) {
+        ALOGE("sqlite3_bind_text() failed setting locale");
+        throw_sqlite3_exception(env, connection->db);
+        goto rollback;
+    }
+
+    err = sqlite3_step(stmt);
+    if (err != SQLITE_OK && err != SQLITE_DONE) {
+        ALOGE("sqlite3_step(\"%s\") failed setting locale", sql);
+        throw_sqlite3_exception(env, connection->db);
+        goto rollback;
+    }
+
+    err = sqlite3_exec(connection->db, "REINDEX LOCALIZED", NULL, NULL, NULL);
+    if (err != SQLITE_OK) {
+        ALOGE("REINDEX LOCALIZED failed");
+        throw_sqlite3_exception(env, connection->db);
+        goto rollback;
+    }
+
+    // all done, yay!
+    err = sqlite3_exec(connection->db, "COMMIT TRANSACTION", NULL, NULL, NULL);
+    if (err != SQLITE_OK) {
+        ALOGE("COMMIT TRANSACTION failed setting locale");
+        throw_sqlite3_exception(env, connection->db);
+        goto done;
+    }
+
+rollback:
+    if (err != SQLITE_OK) {
+        sqlite3_exec(connection->db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
+    }
+
+done:
+    if (stmt) {
+        sqlite3_finalize(stmt);
+    }
+    if (meta) {
+        sqlite3_free_table(meta);
+    }
+    if (locale) {
+        env->ReleaseStringUTFChars(localeStr, locale);
+    }
+}
+
+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);
+
+    ALOGV("Finalized statement %p on connection %p", statement, connection->db);
+    int err = sqlite3_finalize(statement);
+    if (err != SQLITE_OK) {
+        throw_sqlite3_exception(env, connection->db, NULL);
+    }
+}
+
+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_errcode(env, err, sqlite3_errmsg(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_errcode(env, err, sqlite3_errmsg(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) {
+    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);
+    }
+
+    jniThrowIOException(env, error);
+    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;
+}
+
+enum CopyRowResult {
+    CPR_OK,
+    CPR_FULL,
+    CPR_ERROR,
+};
+
+static CopyRowResult copyRow(JNIEnv* env, CursorWindow* window,
+        sqlite3_stmt* statement, int numColumns, int startPos, int addedRows) {
+    // Allocate a new field directory for the row.
+    status_t status = window->allocRow();
+    if (status) {
+        LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d, error=%d",
+                startPos, addedRows, status);
+        return CPR_FULL;
+    }
+
+    // Pack the row into the window.
+    CopyRowResult result = CPR_OK;
+    for (int i = 0; i < numColumns; i++) {
+        int type = sqlite3_column_type(statement, i);
+        if (type == SQLITE_TEXT) {
+            // TEXT data
+            const char* text = reinterpret_cast<const char*>(
+                    sqlite3_column_text(statement, i));
+            // SQLite does not include the NULL terminator in size, but does
+            // ensure all strings are NULL terminated, so increase size by
+            // one to make sure we store the terminator.
+            size_t sizeIncludingNull = sqlite3_column_bytes(statement, i) + 1;
+            status = window->putString(addedRows, i, text, sizeIncludingNull);
+            if (status) {
+                LOG_WINDOW("Failed allocating %u bytes for text at %d,%d, error=%d",
+                        sizeIncludingNull, startPos + addedRows, i, status);
+                result = CPR_FULL;
+                break;
+            }
+            LOG_WINDOW("%d,%d is TEXT with %u bytes",
+                    startPos + addedRows, i, sizeIncludingNull);
+        } else if (type == SQLITE_INTEGER) {
+            // INTEGER data
+            int64_t value = sqlite3_column_int64(statement, i);
+            status = window->putLong(addedRows, i, value);
+            if (status) {
+                LOG_WINDOW("Failed allocating space for a long in column %d, error=%d",
+                        i, status);
+                result = CPR_FULL;
+                break;
+            }
+            LOG_WINDOW("%d,%d is INTEGER 0x%016llx", startPos + addedRows, i, value);
+        } else if (type == SQLITE_FLOAT) {
+            // FLOAT data
+            double value = sqlite3_column_double(statement, i);
+            status = window->putDouble(addedRows, i, value);
+            if (status) {
+                LOG_WINDOW("Failed allocating space for a double in column %d, error=%d",
+                        i, status);
+                result = CPR_FULL;
+                break;
+            }
+            LOG_WINDOW("%d,%d is FLOAT %lf", startPos + addedRows, i, value);
+        } else if (type == SQLITE_BLOB) {
+            // BLOB data
+            const void* blob = sqlite3_column_blob(statement, i);
+            size_t size = sqlite3_column_bytes(statement, i);
+            status = window->putBlob(addedRows, i, blob, size);
+            if (status) {
+                LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d, error=%d",
+                        size, startPos + addedRows, i, status);
+                result = CPR_FULL;
+                break;
+            }
+            LOG_WINDOW("%d,%d is Blob with %u bytes",
+                    startPos + addedRows, i, size);
+        } else if (type == SQLITE_NULL) {
+            // NULL field
+            status = window->putNull(addedRows, i);
+            if (status) {
+                LOG_WINDOW("Failed allocating space for a null in column %d, error=%d",
+                        i, status);
+                result = CPR_FULL;
+                break;
+            }
+
+            LOG_WINDOW("%d,%d is NULL", startPos + addedRows, i);
+        } else {
+            // Unknown data
+            ALOGE("Unknown column type when filling database window");
+            throw_sqlite3_exception(env, "Unknown column type when filling window");
+            result = CPR_ERROR;
+            break;
+        }
+    }
+
+    // Free the last row if if was not successfully copied.
+    if (result != CPR_OK) {
+        window->freeLastRow();
+    }
+    return result;
+}
+
+static jlong nativeExecuteForCursorWindow(JNIEnv* env, jclass clazz,
+        jint connectionPtr, jint statementPtr, jint windowPtr,
+        jint startPos, jint requiredPos, jboolean countAllRows) {
+    SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
+    sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
+    CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
+
+    status_t status = window->clear();
+    if (status) {
+        String8 msg;
+        msg.appendFormat("Failed to clear the cursor window, status=%d", status);
+        throw_sqlite3_exception(env, connection->db, msg.string());
+        return 0;
+    }
+
+    int numColumns = sqlite3_column_count(statement);
+    status = window->setNumColumns(numColumns);
+    if (status) {
+        String8 msg;
+        msg.appendFormat("Failed to set the cursor window column count to %d, status=%d",
+                numColumns, status);
+        throw_sqlite3_exception(env, connection->db, msg.string());
+        return 0;
+    }
+
+    int retryCount = 0;
+    int totalRows = 0;
+    int addedRows = 0;
+    bool windowFull = false;
+    bool gotException = false;
+    while (!gotException && (!windowFull || countAllRows)) {
+        int err = sqlite3_step(statement);
+        if (err == SQLITE_ROW) {
+            LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows);
+            retryCount = 0;
+            totalRows += 1;
+
+            // Skip the row if the window is full or we haven't reached the start position yet.
+            if (startPos >= totalRows || windowFull) {
+                continue;
+            }
+
+            CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
+            if (cpr == CPR_FULL && addedRows && startPos + addedRows < requiredPos) {
+                // We filled the window before we got to the one row that we really wanted.
+                // Clear the window and start filling it again from here.
+                // TODO: Would be nicer if we could progressively replace earlier rows.
+                window->clear();
+                window->setNumColumns(numColumns);
+                startPos += addedRows;
+                addedRows = 0;
+                cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
+            }
+
+            if (cpr == CPR_OK) {
+                addedRows += 1;
+            } else if (cpr == CPR_FULL) {
+                windowFull = true;
+            } else {
+                gotException = true;
+            }
+        } else if (err == SQLITE_DONE) {
+            // All rows processed, bail
+            LOG_WINDOW("Processed all rows");
+            break;
+        } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
+            // The table is locked, retry
+            LOG_WINDOW("Database locked, retrying");
+            if (retryCount > 50) {
+                ALOGE("Bailing on database busy retry");
+                throw_sqlite3_exception(env, connection->db, "retrycount exceeded");
+                gotException = true;
+            } else {
+                // Sleep to give the thread holding the lock a chance to finish
+                usleep(1000);
+                retryCount++;
+            }
+        } else {
+            throw_sqlite3_exception(env, connection->db);
+            gotException = true;
+        }
+    }
+
+    LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows"
+            "to the window in %d bytes",
+            statement, totalRows, addedRows, window->size() - window->freeSpace());
+    sqlite3_reset(statement);
+
+    // Report the total number of rows on request.
+    if (startPos > totalRows) {
+        ALOGE("startPos %d > actual rows %d", startPos, totalRows);
+    }
+    jlong result = jlong(startPos) << 32 | jlong(totalRows);
+    return result;
+}
+
+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 JNINativeMethod sMethods[] =
+{
+    /* name, signature, funcPtr */
+    { "nativeOpen", "(Ljava/lang/String;ILjava/lang/String;ZZ)I",
+            (void*)nativeOpen },
+    { "nativeClose", "(I)V",
+            (void*)nativeClose },
+    { "nativeRegisterCustomFunction", "(ILandroid/database/sqlite/SQLiteCustomFunction;)V",
+            (void*)nativeRegisterCustomFunction },
+    { "nativeSetLocale", "(ILjava/lang/String;)V",
+            (void*)nativeSetLocale },
+    { "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", "(IIIIIZ)J",
+            (void*)nativeExecuteForCursorWindow },
+    { "nativeGetDbLookaside", "(I)I",
+            (void*)nativeGetDbLookaside },
+};
+
+#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, "android/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 AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteConnection",
+            sMethods, NELEM(sMethods));
+}
+
+} // namespace android
diff --git a/core/jni/android_database_SQLiteDatabase.cpp b/core/jni/android_database_SQLiteDatabase.cpp
deleted file mode 100644
index 28c421d..0000000
--- a/core/jni/android_database_SQLiteDatabase.cpp
+++ /dev/null
@@ -1,636 +0,0 @@
-/*
- * Copyright (C) 2006-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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "SqliteDatabaseCpp"
-
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
-
-#include <jni.h>
-#include <JNIHelp.h>
-#include <android_runtime/AndroidRuntime.h>
-
-#include <sqlite3.h>
-#include <sqlite3_android.h>
-#include <string.h>
-#include <utils/Log.h>
-#include <utils/threads.h>
-#include <utils/List.h>
-#include <utils/Errors.h>
-#include <ctype.h>
-
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <string.h>
-#include <netdb.h>
-#include <sys/ioctl.h>
-
-#include "sqlite3_exception.h"
-
-#define UTF16_STORAGE 0
-#define INVALID_VERSION -1
-#define ANDROID_TABLE "android_metadata"
-/* uncomment the next line to force-enable logging of all statements */
-// #define DB_LOG_STATEMENTS
-
-#define DEBUG_JNI 0
-
-namespace android {
-
-enum {
-    OPEN_READWRITE          = 0x00000000,
-    OPEN_READONLY           = 0x00000001,
-    OPEN_READ_MASK          = 0x00000001,
-    NO_LOCALIZED_COLLATORS  = 0x00000010,
-    CREATE_IF_NECESSARY     = 0x10000000
-};
-
-static jfieldID offset_db_handle;
-static jmethodID method_custom_function_callback;
-static jclass string_class;
-static jint sSqliteSoftHeapLimit = 0;
-
-static char *createStr(const char *path, short extra) {
-    int len = strlen(path) + extra;
-    char *str = (char *)malloc(len + 1);
-    strncpy(str, path, len);
-    str[len] = NULL;
-    return str;
-}
-
-static void sqlLogger(void *databaseName, int iErrCode, const char *zMsg) {
-    // skip printing this message if it is due to certain types of errors
-    if (iErrCode == 0 || iErrCode == SQLITE_CONSTRAINT) return;
-    // print databasename, errorcode and msg
-    ALOGI("sqlite returned: error code = %d, msg = %s, db=%s\n", iErrCode, zMsg, databaseName);
-}
-
-// register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called.
-static void registerLoggingFunc(const char *path) {
-    static bool loggingFuncSet = false;
-    if (loggingFuncSet) {
-        return;
-    }
-
-    ALOGV("Registering sqlite logging func \n");
-    int err = sqlite3_config(SQLITE_CONFIG_LOG, &sqlLogger, (void *)createStr(path, 0));
-    if (err != SQLITE_OK) {
-        ALOGW("sqlite returned error = %d when trying to register logging func.\n", err);
-        return;
-    }
-    loggingFuncSet = true;
-}
-
-/* public native void dbopen(String path, int flags, String locale); */
-static void dbopen(JNIEnv* env, jobject object, jstring pathString, jint flags)
-{
-    int err;
-    sqlite3 * handle = NULL;
-    sqlite3_stmt * statement = NULL;
-    char const * path8 = env->GetStringUTFChars(pathString, NULL);
-    int sqliteFlags;
-
-    // register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called.
-    registerLoggingFunc(path8);
-
-    // convert our flags into the sqlite flags
-    if (flags & CREATE_IF_NECESSARY) {
-        sqliteFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
-    } else if (flags & OPEN_READONLY) {
-        sqliteFlags = SQLITE_OPEN_READONLY;
-    } else {
-        sqliteFlags = SQLITE_OPEN_READWRITE;
-    }
-
-    err = sqlite3_open_v2(path8, &handle, sqliteFlags, NULL);
-    if (err != SQLITE_OK) {
-        ALOGE("sqlite3_open_v2(\"%s\", &handle, %d, NULL) failed\n", path8, sqliteFlags);
-        throw_sqlite3_exception(env, handle);
-        goto done;
-    }
-
-    // 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(sSqliteSoftHeapLimit);
-
-    // Set the default busy handler to retry for 1000ms and then return SQLITE_BUSY
-    err = sqlite3_busy_timeout(handle, 1000 /* ms */);
-    if (err != SQLITE_OK) {
-        ALOGE("sqlite3_busy_timeout(handle, 1000) failed for \"%s\"\n", path8);
-        throw_sqlite3_exception(env, handle);
-        goto done;
-    }
-
-#ifdef DB_INTEGRITY_CHECK
-    static const char* integritySql = "pragma integrity_check(1);";
-    err = sqlite3_prepare_v2(handle, integritySql, -1, &statement, NULL);
-    if (err != SQLITE_OK) {
-        ALOGE("sqlite_prepare_v2(handle, \"%s\") failed for \"%s\"\n", integritySql, path8);
-        throw_sqlite3_exception(env, handle);
-        goto done;
-    }
-
-    // first is OK or error message
-    err = sqlite3_step(statement);
-    if (err != SQLITE_ROW) {
-        ALOGE("integrity check failed for \"%s\"\n", integritySql, path8);
-        throw_sqlite3_exception(env, handle);
-        goto done;
-    } else {
-        const char *text = (const char*)sqlite3_column_text(statement, 0);
-        if (strcmp(text, "ok") != 0) {
-            ALOGE("integrity check failed for \"%s\": %s\n", integritySql, path8, text);
-            jniThrowException(env, "android/database/sqlite/SQLiteDatabaseCorruptException", text);
-            goto done;
-        }
-    }
-#endif
-
-    err = register_android_functions(handle, UTF16_STORAGE);
-    if (err) {
-        throw_sqlite3_exception(env, handle);
-        goto done;
-    }
-
-    ALOGV("Opened '%s' - %p\n", path8, handle);
-    env->SetIntField(object, offset_db_handle, (int) handle);
-    handle = NULL;  // The caller owns the handle now.
-
-done:
-    // Release allocated resources
-    if (path8 != NULL) env->ReleaseStringUTFChars(pathString, path8);
-    if (statement != NULL) sqlite3_finalize(statement);
-    if (handle != NULL) sqlite3_close(handle);
-}
-
-static char *getDatabaseName(JNIEnv* env, sqlite3 * handle, jstring databaseName, short connNum) {
-    char const *path = env->GetStringUTFChars(databaseName, NULL);
-    if (path == NULL) {
-        ALOGE("Failure in getDatabaseName(). VM ran out of memory?\n");
-        return NULL; // VM would have thrown OutOfMemoryError
-    }
-    char *dbNameStr = createStr(path, 4);
-    if (connNum > 999) { // TODO: if number of pooled connections > 999, fix this line.
-      connNum = -1;
-    }
-    sprintf(dbNameStr + strlen(path), "|%03d", connNum);
-    env->ReleaseStringUTFChars(databaseName, path);
-    return dbNameStr;
-}
-
-static void sqlTrace(void *databaseName, const char *sql) {
-    ALOGI("sql_statement|%s|%s\n", (char *)databaseName, sql);
-}
-
-/* public native void enableSqlTracing(); */
-static void enableSqlTracing(JNIEnv* env, jobject object, jstring databaseName, jshort connType)
-{
-    sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
-    sqlite3_trace(handle, &sqlTrace, (void *)getDatabaseName(env, handle, databaseName, connType));
-}
-
-static void sqlProfile(void *databaseName, const char *sql, sqlite3_uint64 tm) {
-    double d = tm/1000000.0;
-    ALOGI("elapsedTime4Sql|%s|%.3f ms|%s\n", (char *)databaseName, d, sql);
-}
-
-/* public native void enableSqlProfiling(); */
-static void enableSqlProfiling(JNIEnv* env, jobject object, jstring databaseName, jshort connType)
-{
-    sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
-    sqlite3_profile(handle, &sqlProfile, (void *)getDatabaseName(env, handle, databaseName,
-            connType));
-}
-
-/* public native void close(); */
-static void dbclose(JNIEnv* env, jobject object)
-{
-    sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
-
-    if (handle != NULL) {
-        // release the memory associated with the traceFuncArg in enableSqlTracing function
-        void *traceFuncArg = sqlite3_trace(handle, &sqlTrace, NULL);
-        if (traceFuncArg != NULL) {
-            free(traceFuncArg);
-        }
-        // release the memory associated with the traceFuncArg in enableSqlProfiling function
-        traceFuncArg = sqlite3_profile(handle, &sqlProfile, NULL);
-        if (traceFuncArg != NULL) {
-            free(traceFuncArg);
-        }
-        ALOGV("Closing database: handle=%p\n", handle);
-        int result = sqlite3_close(handle);
-        if (result == SQLITE_OK) {
-            ALOGV("Closed %p\n", handle);
-            env->SetIntField(object, offset_db_handle, 0);
-        } else {
-            // This can happen if sub-objects aren't closed first.  Make sure the caller knows.
-            throw_sqlite3_exception(env, handle);
-            ALOGE("sqlite3_close(%p) failed: %d\n", handle, result);
-        }
-    }
-}
-
-/* native int native_getDbLookaside(); */
-static jint native_getDbLookaside(JNIEnv* env, jobject object)
-{
-    sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
-    int pCur = -1;
-    int unused;
-    sqlite3_db_status(handle, SQLITE_DBSTATUS_LOOKASIDE_USED, &pCur, &unused, 0);
-    return pCur;
-}
-
-/* set locale in the android_metadata table, install localized collators, and rebuild indexes */
-static void native_setLocale(JNIEnv* env, jobject object, jstring localeString, jint flags)
-{
-    if ((flags & NO_LOCALIZED_COLLATORS)) return;
-
-    int err;
-    char const* locale8 = env->GetStringUTFChars(localeString, NULL);
-    sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
-    sqlite3_stmt* stmt = NULL;
-    char** meta = NULL;
-    int rowCount, colCount;
-    char* dbLocale = NULL;
-
-    // create the table, if necessary and possible
-    if (!(flags & OPEN_READONLY)) {
-        static const char *createSql ="CREATE TABLE IF NOT EXISTS " ANDROID_TABLE " (locale TEXT)";
-        err = sqlite3_exec(handle, createSql, NULL, NULL, NULL);
-        if (err != SQLITE_OK) {
-            ALOGE("CREATE TABLE " ANDROID_TABLE " failed\n");
-            throw_sqlite3_exception(env, handle);
-            goto done;
-        }
-    }
-
-    // try to read from the table
-    static const char *selectSql = "SELECT locale FROM " ANDROID_TABLE " LIMIT 1";
-    err = sqlite3_get_table(handle, selectSql, &meta, &rowCount, &colCount, NULL);
-    if (err != SQLITE_OK) {
-        ALOGE("SELECT locale FROM " ANDROID_TABLE " failed\n");
-        throw_sqlite3_exception(env, handle);
-        goto done;
-    }
-
-    dbLocale = (rowCount >= 1) ? meta[colCount] : NULL;
-
-    if (dbLocale != NULL && !strcmp(dbLocale, locale8)) {
-        // database locale is the same as the desired locale; set up the collators and go
-        err = register_localized_collators(handle, locale8, UTF16_STORAGE);
-        if (err != SQLITE_OK) throw_sqlite3_exception(env, handle);
-        goto done;   // no database changes needed
-    }
-
-    if ((flags & OPEN_READONLY)) {
-        // read-only database, so we're going to have to put up with whatever we got
-        // For registering new index. Not for modifing the read-only database.
-        err = register_localized_collators(handle, locale8, UTF16_STORAGE);
-        if (err != SQLITE_OK) throw_sqlite3_exception(env, handle);
-        goto done;
-    }
-
-    // need to update android_metadata and indexes atomically, so use a transaction...
-    err = sqlite3_exec(handle, "BEGIN TRANSACTION", NULL, NULL, NULL);
-    if (err != SQLITE_OK) {
-        ALOGE("BEGIN TRANSACTION failed setting locale\n");
-        throw_sqlite3_exception(env, handle);
-        goto done;
-    }
-
-    err = register_localized_collators(handle, locale8, UTF16_STORAGE);
-    if (err != SQLITE_OK) {
-        ALOGE("register_localized_collators() failed setting locale\n");
-        throw_sqlite3_exception(env, handle);
-        goto rollback;
-    }
-
-    err = sqlite3_exec(handle, "DELETE FROM " ANDROID_TABLE, NULL, NULL, NULL);
-    if (err != SQLITE_OK) {
-        ALOGE("DELETE failed setting locale\n");
-        throw_sqlite3_exception(env, handle);
-        goto rollback;
-    }
-
-    static const char *sql = "INSERT INTO " ANDROID_TABLE " (locale) VALUES(?);";
-    err = sqlite3_prepare_v2(handle, sql, -1, &stmt, NULL);
-    if (err != SQLITE_OK) {
-        ALOGE("sqlite3_prepare_v2(\"%s\") failed\n", sql);
-        throw_sqlite3_exception(env, handle);
-        goto rollback;
-    }
-
-    err = sqlite3_bind_text(stmt, 1, locale8, -1, SQLITE_TRANSIENT);
-    if (err != SQLITE_OK) {
-        ALOGE("sqlite3_bind_text() failed setting locale\n");
-        throw_sqlite3_exception(env, handle);
-        goto rollback;
-    }
-
-    err = sqlite3_step(stmt);
-    if (err != SQLITE_OK && err != SQLITE_DONE) {
-        ALOGE("sqlite3_step(\"%s\") failed setting locale\n", sql);
-        throw_sqlite3_exception(env, handle);
-        goto rollback;
-    }
-
-    err = sqlite3_exec(handle, "REINDEX LOCALIZED", NULL, NULL, NULL);
-    if (err != SQLITE_OK) {
-        ALOGE("REINDEX LOCALIZED failed\n");
-        throw_sqlite3_exception(env, handle);
-        goto rollback;
-    }
-
-    // all done, yay!
-    err = sqlite3_exec(handle, "COMMIT TRANSACTION", NULL, NULL, NULL);
-    if (err != SQLITE_OK) {
-        ALOGE("COMMIT TRANSACTION failed setting locale\n");
-        throw_sqlite3_exception(env, handle);
-        goto done;
-    }
-
-rollback:
-    if (err != SQLITE_OK) {
-        sqlite3_exec(handle, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
-    }
-
-done:
-    if (locale8 != NULL) env->ReleaseStringUTFChars(localeString, locale8);
-    if (stmt != NULL) sqlite3_finalize(stmt);
-    if (meta != NULL) sqlite3_free_table(meta);
-}
-
-static void native_setSqliteSoftHeapLimit(JNIEnv* env, jobject clazz, jint limit) {
-    sSqliteSoftHeapLimit = limit;
-}
-
-static jint native_releaseMemory(JNIEnv *env, jobject clazz)
-{
-    // Attempt to release as much memory from the
-    return sqlite3_release_memory(sSqliteSoftHeapLimit);
-}
-
-static void native_finalize(JNIEnv* env, jobject object, jint statementId)
-{
-    if (statementId > 0) {
-        sqlite3_finalize((sqlite3_stmt *)statementId);
-    }
-}
-
-static void custom_function_callback(sqlite3_context * context, int argc, sqlite3_value ** argv) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
-    if (!env) {
-        ALOGE("custom_function_callback cannot call into Java on this thread");
-        return;
-    }
-    // get global ref to CustomFunction object from our user data
-    jobject function = (jobject)sqlite3_user_data(context);
-
-    // pack up the arguments into a string array
-    jobjectArray strArray = env->NewObjectArray(argc, string_class, NULL);
-    if (!strArray)
-        goto done;
-    for (int i = 0; i < argc; i++) {
-        char* arg = (char *)sqlite3_value_text(argv[i]);
-        if (!arg) {
-            ALOGE("NULL argument in custom_function_callback.  This should not happen.");
-            return;
-        }
-        jobject obj = env->NewStringUTF(arg);
-        if (!obj)
-            goto done;
-        env->SetObjectArrayElement(strArray, i, obj);
-        env->DeleteLocalRef(obj);
-    }
-
-    env->CallVoidMethod(function, method_custom_function_callback, strArray);
-    env->DeleteLocalRef(strArray);
-
-done:
-    if (env->ExceptionCheck()) {
-        ALOGE("An exception was thrown by custom sqlite3 function.");
-        LOGE_EX(env);
-        env->ExceptionClear();
-    }
-}
-
-static jint native_addCustomFunction(JNIEnv* env, jobject object,
-        jstring name, jint numArgs, jobject function)
-{
-    sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
-    char const *nameStr = env->GetStringUTFChars(name, NULL);
-    jobject ref = env->NewGlobalRef(function);
-    ALOGD_IF(DEBUG_JNI, "native_addCustomFunction %s ref: %p", nameStr, ref);
-    int err = sqlite3_create_function(handle, nameStr, numArgs, SQLITE_UTF8,
-            (void *)ref, custom_function_callback, NULL, NULL);
-    env->ReleaseStringUTFChars(name, nameStr);
-
-    if (err == SQLITE_OK)
-        return (int)ref;
-    else {
-        ALOGE("sqlite3_create_function returned %d", err);
-        env->DeleteGlobalRef(ref);
-        throw_sqlite3_exception(env, handle);
-        return 0;
-     }
-}
-
-static void native_releaseCustomFunction(JNIEnv* env, jobject object, jint ref)
-{
-    ALOGD_IF(DEBUG_JNI, "native_releaseCustomFunction %d", ref);
-    env->DeleteGlobalRef((jobject)ref);
-}
-
-static JNINativeMethod sMethods[] =
-{
-    /* name, signature, funcPtr */
-    {"dbopen", "(Ljava/lang/String;I)V", (void *)dbopen},
-    {"dbclose", "()V", (void *)dbclose},
-    {"enableSqlTracing", "(Ljava/lang/String;S)V", (void *)enableSqlTracing},
-    {"enableSqlProfiling", "(Ljava/lang/String;S)V", (void *)enableSqlProfiling},
-    {"native_setLocale", "(Ljava/lang/String;I)V", (void *)native_setLocale},
-    {"native_getDbLookaside", "()I", (void *)native_getDbLookaside},
-    {"native_setSqliteSoftHeapLimit", "(I)V", (void *)native_setSqliteSoftHeapLimit},
-    {"releaseMemory", "()I", (void *)native_releaseMemory},
-    {"native_finalize", "(I)V", (void *)native_finalize},
-    {"native_addCustomFunction",
-                    "(Ljava/lang/String;ILandroid/database/sqlite/SQLiteDatabase$CustomFunction;)I",
-                    (void *)native_addCustomFunction},
-    {"native_releaseCustomFunction", "(I)V", (void *)native_releaseCustomFunction},
-};
-
-int register_android_database_SQLiteDatabase(JNIEnv *env)
-{
-    jclass clazz;
-
-    clazz = env->FindClass("android/database/sqlite/SQLiteDatabase");
-    if (clazz == NULL) {
-        ALOGE("Can't find android/database/sqlite/SQLiteDatabase\n");
-        return -1;
-    }
-
-    string_class = (jclass)env->NewGlobalRef(env->FindClass("java/lang/String"));
-    if (string_class == NULL) {
-        ALOGE("Can't find java/lang/String\n");
-        return -1;
-    }
-
-    offset_db_handle = env->GetFieldID(clazz, "mNativeHandle", "I");
-    if (offset_db_handle == NULL) {
-        ALOGE("Can't find SQLiteDatabase.mNativeHandle\n");
-        return -1;
-    }
-
-    clazz = env->FindClass("android/database/sqlite/SQLiteDatabase$CustomFunction");
-    if (clazz == NULL) {
-        ALOGE("Can't find android/database/sqlite/SQLiteDatabase$CustomFunction\n");
-        return -1;
-    }
-    method_custom_function_callback = env->GetMethodID(clazz, "callback", "([Ljava/lang/String;)V");
-    if (method_custom_function_callback == NULL) {
-        ALOGE("Can't find method SQLiteDatabase.CustomFunction.callback\n");
-        return -1;
-    }
-
-    return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteDatabase",
-            sMethods, NELEM(sMethods));
-}
-
-/* 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) {
-        throw_sqlite3_exception(env, sqlite3_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 */
-void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message) {
-    if (errcode == SQLITE_DONE) {
-        throw_sqlite3_exception(env, errcode, NULL, message);
-    } else {
-        char temp[21];
-        sprintf(temp, "error code %d", errcode);
-        throw_sqlite3_exception(env, errcode, temp, 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) {
-        case SQLITE_IOERR:
-            exceptionClass = "android/database/sqlite/SQLiteDiskIOException";
-            break;
-        case SQLITE_CORRUPT:
-        case SQLITE_NOTADB: // treat "unsupported file format" error as corruption also
-            exceptionClass = "android/database/sqlite/SQLiteDatabaseCorruptException";
-            break;
-        case SQLITE_CONSTRAINT:
-           exceptionClass = "android/database/sqlite/SQLiteConstraintException";
-           break;
-        case SQLITE_ABORT:
-           exceptionClass = "android/database/sqlite/SQLiteAbortException";
-           break;
-        case SQLITE_DONE:
-           exceptionClass = "android/database/sqlite/SQLiteDoneException";
-           break;
-        case SQLITE_FULL:
-           exceptionClass = "android/database/sqlite/SQLiteFullException";
-           break;
-        case SQLITE_MISUSE:
-           exceptionClass = "android/database/sqlite/SQLiteMisuseException";
-           break;
-        case SQLITE_PERM:
-           exceptionClass = "android/database/sqlite/SQLiteAccessPermException";
-           break;
-        case SQLITE_BUSY:
-           exceptionClass = "android/database/sqlite/SQLiteDatabaseLockedException";
-           break;
-        case SQLITE_LOCKED:
-           exceptionClass = "android/database/sqlite/SQLiteTableLockedException";
-           break;
-        case SQLITE_READONLY:
-           exceptionClass = "android/database/sqlite/SQLiteReadOnlyDatabaseException";
-           break;
-        case SQLITE_CANTOPEN:
-           exceptionClass = "android/database/sqlite/SQLiteCantOpenDatabaseException";
-           break;
-        case SQLITE_TOOBIG:
-           exceptionClass = "android/database/sqlite/SQLiteBlobTooBigException";
-           break;
-        case SQLITE_RANGE:
-           exceptionClass = "android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException";
-           break;
-        case SQLITE_NOMEM:
-           exceptionClass = "android/database/sqlite/SQLiteOutOfMemoryException";
-           break;
-        case SQLITE_MISMATCH:
-           exceptionClass = "android/database/sqlite/SQLiteDatatypeMismatchException";
-           break;
-        case SQLITE_UNCLOSED:
-           exceptionClass = "android/database/sqlite/SQLiteUnfinalizedObjectsException";
-           break;
-        default:
-           exceptionClass = "android/database/sqlite/SQLiteException";
-           break;
-    }
-
-    if (sqlite3Message != NULL && message != NULL) {
-        char* fullMessage = (char *)malloc(strlen(sqlite3Message) + strlen(message) + 3);
-        if (fullMessage != NULL) {
-            strcpy(fullMessage, sqlite3Message);
-            strcat(fullMessage, ": ");
-            strcat(fullMessage, message);
-            jniThrowException(env, exceptionClass, fullMessage);
-            free(fullMessage);
-        } else {
-            jniThrowException(env, exceptionClass, sqlite3Message);
-        }
-    } else if (sqlite3Message != NULL) {
-        jniThrowException(env, exceptionClass, sqlite3Message);
-    } else {
-        jniThrowException(env, exceptionClass, message);
-    }
-}
-
-
-} // namespace android
diff --git a/core/jni/android_database_SQLiteGlobal.cpp b/core/jni/android_database_SQLiteGlobal.cpp
new file mode 100644
index 0000000..82cae5a
--- /dev/null
+++ b/core/jni/android_database_SQLiteGlobal.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SQLiteGlobal"
+
+#include <jni.h>
+#include <JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+#include <sqlite3.h>
+#include <sqlite3_android.h>
+
+#include "android_database_SQLiteCommon.h"
+
+namespace android {
+
+// 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) {
+        if (verboseLog) {
+            ALOGV(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 nativeConfig(JNIEnv* env, jclass clazz, jboolean verboseLog, jint softHeapLimit) {
+    // 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.
+    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(softHeapLimit);
+}
+
+static jint nativeReleaseMemory(JNIEnv* env, jclass clazz, jint bytesToFree) {
+    return sqlite3_release_memory(bytesToFree);
+}
+
+static JNINativeMethod sMethods[] =
+{
+    /* name, signature, funcPtr */
+    { "nativeConfig", "(ZI)V",
+            (void*)nativeConfig },
+    { "nativeReleaseMemory", "(I)I",
+            (void*)nativeReleaseMemory },
+};
+
+int register_android_database_SQLiteGlobal(JNIEnv *env)
+{
+    return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteGlobal",
+            sMethods, NELEM(sMethods));
+}
+
+} // namespace android
diff --git a/core/jni/android_database_SQLiteProgram.cpp b/core/jni/android_database_SQLiteProgram.cpp
deleted file mode 100644
index 2e34c00..0000000
--- a/core/jni/android_database_SQLiteProgram.cpp
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2006-2008 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "Cursor"
-
-#include <jni.h>
-#include <JNIHelp.h>
-#include <android_runtime/AndroidRuntime.h>
-
-#include <sqlite3.h>
-
-#include <utils/Log.h>
-
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "sqlite3_exception.h"
-
-
-namespace android {
-
-static jfieldID gHandleField;
-static jfieldID gStatementField;
-
-
-#define GET_STATEMENT(env, object) \
-        (sqlite3_stmt *)env->GetIntField(object, gStatementField)
-#define GET_HANDLE(env, object) \
-        (sqlite3 *)env->GetIntField(object, gHandleField)
-
-static void native_compile(JNIEnv* env, jobject object, jstring sqlString)
-{
-    char buf[65];
-    strcpy(buf, "android_database_SQLiteProgram->native_compile() not implemented");
-    throw_sqlite3_exception(env, GET_HANDLE(env, object), buf);
-    return;
-}
-
-static void native_bind_null(JNIEnv* env, jobject object,
-                             jint index)
-{
-    int err;
-    sqlite3_stmt * statement = GET_STATEMENT(env, object);
-
-    err = sqlite3_bind_null(statement, index);
-    if (err != SQLITE_OK) {
-        char buf[32];
-        sprintf(buf, "handle %p", statement);
-        throw_sqlite3_exception(env, GET_HANDLE(env, object), buf);
-        return;
-    }
-}
-
-static void native_bind_long(JNIEnv* env, jobject object,
-                             jint index, jlong value)
-{
-    int err;
-    sqlite3_stmt * statement = GET_STATEMENT(env, object);
-
-    err = sqlite3_bind_int64(statement, index, value);
-    if (err != SQLITE_OK) {
-        char buf[32];
-        sprintf(buf, "handle %p", statement);
-        throw_sqlite3_exception(env, GET_HANDLE(env, object), buf);
-        return;
-    }
-}
-
-static void native_bind_double(JNIEnv* env, jobject object,
-                             jint index, jdouble value)
-{
-    int err;
-    sqlite3_stmt * statement = GET_STATEMENT(env, object);
-
-    err = sqlite3_bind_double(statement, index, value);
-    if (err != SQLITE_OK) {
-        char buf[32];
-        sprintf(buf, "handle %p", statement);
-        throw_sqlite3_exception(env, GET_HANDLE(env, object), buf);
-        return;
-    }
-}
-
-static void native_bind_string(JNIEnv* env, jobject object,
-                               jint index, jstring sqlString)
-{
-    int err;
-    jchar const * sql;
-    jsize sqlLen;
-    sqlite3_stmt * statement= GET_STATEMENT(env, object);
-
-    sql = env->GetStringChars(sqlString, NULL);
-    sqlLen = env->GetStringLength(sqlString);
-    err = sqlite3_bind_text16(statement, index, sql, sqlLen * 2, SQLITE_TRANSIENT);
-    env->ReleaseStringChars(sqlString, sql);
-    if (err != SQLITE_OK) {
-        char buf[32];
-        sprintf(buf, "handle %p", statement);
-        throw_sqlite3_exception(env, GET_HANDLE(env, object), buf);
-        return;
-    }
-}
-
-static void native_bind_blob(JNIEnv* env, jobject object,
-                               jint index, jbyteArray value)
-{
-    int err;
-    jchar const * sql;
-    jsize sqlLen;
-    sqlite3_stmt * statement= GET_STATEMENT(env, object);
-
-    jint len = env->GetArrayLength(value);
-    jbyte * bytes = env->GetByteArrayElements(value, NULL);
-
-    err = sqlite3_bind_blob(statement, index, bytes, len, SQLITE_TRANSIENT);
-    env->ReleaseByteArrayElements(value, bytes, JNI_ABORT);
-
-    if (err != SQLITE_OK) {
-        char buf[32];
-        sprintf(buf, "statement %p", statement);
-        throw_sqlite3_exception(env, GET_HANDLE(env, object), buf);
-        return;
-    }
-}
-
-static void native_clear_bindings(JNIEnv* env, jobject object)
-{
-    int err;
-    sqlite3_stmt * statement = GET_STATEMENT(env, object);
-
-    err = sqlite3_clear_bindings(statement);
-    if (err != SQLITE_OK) {
-        throw_sqlite3_exception(env, GET_HANDLE(env, object));
-        return;
-    }
-}
-
-static void native_finalize(JNIEnv* env, jobject object)
-{
-    char buf[66];
-    strcpy(buf, "android_database_SQLiteProgram->native_finalize() not implemented");
-    throw_sqlite3_exception(env, GET_HANDLE(env, object), buf);
-    return;
-}
-
-
-static JNINativeMethod sMethods[] =
-{
-     /* name, signature, funcPtr */
-    {"native_bind_null", "(I)V", (void *)native_bind_null},
-    {"native_bind_long", "(IJ)V", (void *)native_bind_long},
-    {"native_bind_double", "(ID)V", (void *)native_bind_double},
-    {"native_bind_string", "(ILjava/lang/String;)V", (void *)native_bind_string},
-    {"native_bind_blob", "(I[B)V", (void *)native_bind_blob},
-    {"native_clear_bindings", "()V", (void *)native_clear_bindings},
-};
-
-int register_android_database_SQLiteProgram(JNIEnv * env)
-{
-    jclass clazz;
-
-    clazz = env->FindClass("android/database/sqlite/SQLiteProgram");
-    if (clazz == NULL) {
-        ALOGE("Can't find android/database/sqlite/SQLiteProgram");
-        return -1;
-    }
-
-    gHandleField = env->GetFieldID(clazz, "nHandle", "I");
-    gStatementField = env->GetFieldID(clazz, "nStatement", "I");
-
-    if (gHandleField == NULL || gStatementField == NULL) {
-        ALOGE("Error locating fields");
-        return -1;
-    }
-
-    return AndroidRuntime::registerNativeMethods(env,
-        "android/database/sqlite/SQLiteProgram", sMethods, NELEM(sMethods));
-}
-
-} // namespace android
diff --git a/core/jni/android_database_SQLiteQuery.cpp b/core/jni/android_database_SQLiteQuery.cpp
deleted file mode 100644
index da7ccf0..0000000
--- a/core/jni/android_database_SQLiteQuery.cpp
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "SqliteCursor.cpp"
-
-#include <jni.h>
-#include <JNIHelp.h>
-#include <android_runtime/AndroidRuntime.h>
-
-#include <sqlite3.h>
-
-#include <utils/Log.h>
-
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "binder/CursorWindow.h"
-#include "sqlite3_exception.h"
-
-
-namespace android {
-
-enum CopyRowResult {
-    CPR_OK,
-    CPR_FULL,
-    CPR_ERROR,
-};
-
-static CopyRowResult copyRow(JNIEnv* env, CursorWindow* window,
-        sqlite3_stmt* statement, int numColumns, int startPos, int addedRows) {
-    // Allocate a new field directory for the row. This pointer is not reused
-    // since it may be possible for it to be relocated on a call to alloc() when
-    // the field data is being allocated.
-    status_t status = window->allocRow();
-    if (status) {
-        LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d, error=%d",
-                startPos, addedRows, status);
-        return CPR_FULL;
-    }
-
-    // Pack the row into the window.
-    CopyRowResult result = CPR_OK;
-    for (int i = 0; i < numColumns; i++) {
-        int type = sqlite3_column_type(statement, i);
-        if (type == SQLITE_TEXT) {
-            // TEXT data
-            const char* text = reinterpret_cast<const char*>(
-                    sqlite3_column_text(statement, i));
-            // SQLite does not include the NULL terminator in size, but does
-            // ensure all strings are NULL terminated, so increase size by
-            // one to make sure we store the terminator.
-            size_t sizeIncludingNull = sqlite3_column_bytes(statement, i) + 1;
-            status = window->putString(addedRows, i, text, sizeIncludingNull);
-            if (status) {
-                LOG_WINDOW("Failed allocating %u bytes for text at %d,%d, error=%d",
-                        sizeIncludingNull, startPos + addedRows, i, status);
-                result = CPR_FULL;
-                break;
-            }
-            LOG_WINDOW("%d,%d is TEXT with %u bytes",
-                    startPos + addedRows, i, sizeIncludingNull);
-        } else if (type == SQLITE_INTEGER) {
-            // INTEGER data
-            int64_t value = sqlite3_column_int64(statement, i);
-            status = window->putLong(addedRows, i, value);
-            if (status) {
-                LOG_WINDOW("Failed allocating space for a long in column %d, error=%d",
-                        i, status);
-                result = CPR_FULL;
-                break;
-            }
-            LOG_WINDOW("%d,%d is INTEGER 0x%016llx", startPos + addedRows, i, value);
-        } else if (type == SQLITE_FLOAT) {
-            // FLOAT data
-            double value = sqlite3_column_double(statement, i);
-            status = window->putDouble(addedRows, i, value);
-            if (status) {
-                LOG_WINDOW("Failed allocating space for a double in column %d, error=%d",
-                        i, status);
-                result = CPR_FULL;
-                break;
-            }
-            LOG_WINDOW("%d,%d is FLOAT %lf", startPos + addedRows, i, value);
-        } else if (type == SQLITE_BLOB) {
-            // BLOB data
-            const void* blob = sqlite3_column_blob(statement, i);
-            size_t size = sqlite3_column_bytes(statement, i);
-            status = window->putBlob(addedRows, i, blob, size);
-            if (status) {
-                LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d, error=%d",
-                        size, startPos + addedRows, i, status);
-                result = CPR_FULL;
-                break;
-            }
-            LOG_WINDOW("%d,%d is Blob with %u bytes",
-                    startPos + addedRows, i, size);
-        } else if (type == SQLITE_NULL) {
-            // NULL field
-            status = window->putNull(addedRows, i);
-            if (status) {
-                LOG_WINDOW("Failed allocating space for a null in column %d, error=%d",
-                        i, status);
-                result = CPR_FULL;
-                break;
-            }
-
-            LOG_WINDOW("%d,%d is NULL", startPos + addedRows, i);
-        } else {
-            // Unknown data
-            ALOGE("Unknown column type when filling database window");
-            throw_sqlite3_exception(env, "Unknown column type when filling window");
-            result = CPR_ERROR;
-            break;
-        }
-    }
-
-    // Free the last row if if was not successfully copied.
-    if (result != CPR_OK) {
-        window->freeLastRow();
-    }
-    return result;
-}
-
-static jlong nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr,
-        jint statementPtr, jint windowPtr, jint offsetParam,
-        jint startPos, jint requiredPos, jboolean countAllRows) {
-    sqlite3* database = reinterpret_cast<sqlite3*>(databasePtr);
-    sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
-    CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
-
-    // Only do the binding if there is a valid offsetParam. If no binding needs to be done
-    // offsetParam will be set to 0, an invalid value.
-    if (offsetParam > 0) {
-        // Bind the offset parameter, telling the program which row to start with
-        // If an offset parameter is used, we cannot simply clear the window if it
-        // turns out that the requiredPos won't fit because the result set may
-        // depend on startPos, so we set startPos to requiredPos.
-        startPos = requiredPos;
-        int err = sqlite3_bind_int(statement, offsetParam, startPos);
-        if (err != SQLITE_OK) {
-            ALOGE("Unable to bind offset position, offsetParam = %d", offsetParam);
-            throw_sqlite3_exception(env, database);
-            return 0;
-        }
-        LOG_WINDOW("Bound offset position to startPos %d", startPos);
-    }
-
-    // We assume numRows is initially 0.
-    LOG_WINDOW("Window: numRows = %d, size = %d, freeSpace = %d",
-            window->getNumRows(), window->size(), window->freeSpace());
-
-    int numColumns = sqlite3_column_count(statement);
-    status_t status = window->setNumColumns(numColumns);
-    if (status) {
-        ALOGE("Failed to change column count from %d to %d", window->getNumColumns(), numColumns);
-        jniThrowException(env, "java/lang/IllegalStateException", "numColumns mismatch");
-        return 0;
-    }
-
-    int retryCount = 0;
-    int totalRows = 0;
-    int addedRows = 0;
-    bool windowFull = false;
-    bool gotException = false;
-    while (!gotException && (!windowFull || countAllRows)) {
-        int err = sqlite3_step(statement);
-        if (err == SQLITE_ROW) {
-            LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows);
-            retryCount = 0;
-            totalRows += 1;
-
-            // Skip the row if the window is full or we haven't reached the start position yet.
-            if (startPos >= totalRows || windowFull) {
-                continue;
-            }
-
-            CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
-            if (cpr == CPR_FULL && addedRows && startPos + addedRows < requiredPos) {
-                // We filled the window before we got to the one row that we really wanted.
-                // Clear the window and start filling it again from here.
-                // TODO: Would be nicer if we could progressively replace earlier rows.
-                window->clear();
-                window->setNumColumns(numColumns);
-                startPos += addedRows;
-                addedRows = 0;
-                cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
-            }
-
-            if (cpr == CPR_OK) {
-                addedRows += 1;
-            } else if (cpr == CPR_FULL) {
-                windowFull = true;
-            } else {
-                gotException = true;
-            }
-        } else if (err == SQLITE_DONE) {
-            // All rows processed, bail
-            LOG_WINDOW("Processed all rows");
-            break;
-        } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
-            // The table is locked, retry
-            LOG_WINDOW("Database locked, retrying");
-            if (retryCount > 50) {
-                ALOGE("Bailing on database busy retry");
-                throw_sqlite3_exception(env, database, "retrycount exceeded");
-                gotException = true;
-            } else {
-                // Sleep to give the thread holding the lock a chance to finish
-                usleep(1000);
-                retryCount++;
-            }
-        } else {
-            throw_sqlite3_exception(env, database);
-            gotException = true;
-        }
-    }
-
-    LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows"
-            "to the window in %d bytes",
-            statement, totalRows, addedRows, window->size() - window->freeSpace());
-    sqlite3_reset(statement);
-
-    // Report the total number of rows on request.
-    if (startPos > totalRows) {
-        ALOGE("startPos %d > actual rows %d", startPos, totalRows);
-    }
-    jlong result = jlong(startPos) << 32 | jlong(totalRows);
-    return result;
-}
-
-static jint nativeColumnCount(JNIEnv* env, jclass clazz, jint statementPtr) {
-    sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
-    return sqlite3_column_count(statement);
-}
-
-static jstring nativeColumnName(JNIEnv* env, jclass clazz, jint statementPtr,
-        jint columnIndex) {
-    sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
-    const char* name = sqlite3_column_name(statement, columnIndex);
-    return env->NewStringUTF(name);
-}
-
-
-static JNINativeMethod sMethods[] =
-{
-     /* name, signature, funcPtr */
-    { "nativeFillWindow", "(IIIIIIZ)J",
-            (void*)nativeFillWindow },
-    { "nativeColumnCount", "(I)I",
-            (void*)nativeColumnCount},
-    { "nativeColumnName", "(II)Ljava/lang/String;",
-            (void*)nativeColumnName},
-};
-
-int register_android_database_SQLiteQuery(JNIEnv * env)
-{
-    return AndroidRuntime::registerNativeMethods(env,
-        "android/database/sqlite/SQLiteQuery", sMethods, NELEM(sMethods));
-}
-
-} // namespace android
diff --git a/core/jni/android_database_SQLiteStatement.cpp b/core/jni/android_database_SQLiteStatement.cpp
deleted file mode 100644
index e376258..0000000
--- a/core/jni/android_database_SQLiteStatement.cpp
+++ /dev/null
@@ -1,286 +0,0 @@
-/* //device/libs/android_runtime/android_database_SQLiteCursor.cpp
-**
-** Copyright 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.
-*/
-
-#undef LOG_TAG
-#define LOG_TAG "SQLiteStatementCpp"
-
-#include "android_util_Binder.h"
-
-#include <jni.h>
-#include <JNIHelp.h>
-#include <android_runtime/AndroidRuntime.h>
-
-#include <sqlite3.h>
-
-#include <cutils/ashmem.h>
-#include <utils/Log.h>
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include "sqlite3_exception.h"
-
-namespace android {
-
-
-sqlite3_stmt * compile(JNIEnv* env, jobject object,
-                       sqlite3 * handle, jstring sqlString);
-
-static jfieldID gHandleField;
-static jfieldID gStatementField;
-
-
-#define GET_STATEMENT(env, object) \
-        (sqlite3_stmt *)env->GetIntField(object, gStatementField)
-#define GET_HANDLE(env, object) \
-        (sqlite3 *)env->GetIntField(object, gHandleField)
-
-
-static jint native_execute(JNIEnv* env, jobject object)
-{
-    int err;
-    sqlite3 * handle = GET_HANDLE(env, object);
-    sqlite3_stmt * statement = GET_STATEMENT(env, object);
-    int numChanges = -1;
-
-    // Execute the statement
-    err = sqlite3_step(statement);
-
-    // Throw an exception if an error occurred
-    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_errcode(env, err, sqlite3_errmsg(handle));
-    } else {
-        numChanges = sqlite3_changes(handle);
-    }
-
-    // Reset the statement so it's ready to use again
-    sqlite3_reset(statement);
-    return numChanges;
-}
-
-static jlong native_executeInsert(JNIEnv* env, jobject object)
-{
-    sqlite3 * handle = GET_HANDLE(env, object);
-    jint numChanges = native_execute(env, object);
-    if (numChanges > 0) {
-        return sqlite3_last_insert_rowid(handle);
-    } else {
-        return -1;
-    }
-}
-
-static jlong native_1x1_long(JNIEnv* env, jobject object)
-{
-    int err;
-    sqlite3 * handle = GET_HANDLE(env, object);
-    sqlite3_stmt * statement = GET_STATEMENT(env, object);
-    jlong value = -1;
-
-    // Execute the statement
-    err = sqlite3_step(statement);
-
-    // Handle the result
-    if (err == SQLITE_ROW) {
-        // No errors, read the data and return it
-        value = sqlite3_column_int64(statement, 0);
-    } else {
-        throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(handle));
-    }
-
-    // Reset the statment so it's ready to use again
-    sqlite3_reset(statement);
-
-    return value;
-}
-
-static jstring native_1x1_string(JNIEnv* env, jobject object)
-{
-    int err;
-    sqlite3 * handle = GET_HANDLE(env, object);
-    sqlite3_stmt * statement = GET_STATEMENT(env, object);
-    jstring value = NULL;
-
-    // Execute the statement
-    err = sqlite3_step(statement);
-
-    // Handle the result
-    if (err == SQLITE_ROW) {
-        // No errors, read the data and return it
-        char const * text = (char const *)sqlite3_column_text(statement, 0);
-        value = env->NewStringUTF(text);
-    } else {
-        throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(handle));
-    }
-
-    // Reset the statment so it's ready to use again
-    sqlite3_reset(statement);
-
-    return value;
-}
-
-static jobject createParcelFileDescriptor(JNIEnv * env, int fd)
-{
-    // Create FileDescriptor object
-    jobject fileDesc = jniCreateFileDescriptor(env, fd);
-    if (fileDesc == NULL) {
-        // FileDescriptor constructor has thrown an exception
-        close(fd);
-        return NULL;
-    }
-
-    // Wrap it in a ParcelFileDescriptor
-    jobject parcelFileDesc = newParcelFileDescriptor(env, fileDesc);
-    if (parcelFileDesc == NULL) {
-        // ParcelFileDescriptor constructor has thrown an exception
-        close(fd);
-        return NULL;
-    }
-
-    return parcelFileDesc;
-}
-
-// Creates an ashmem area, copies some data into it, and returns
-// a ParcelFileDescriptor for the ashmem area.
-static jobject create_ashmem_region_with_data(JNIEnv * env,
-                                              const void * data, int length)
-{
-    // Create ashmem area
-    int fd = ashmem_create_region(NULL, length);
-    if (fd < 0) {
-        ALOGE("ashmem_create_region failed: %s", strerror(errno));
-        jniThrowIOException(env, errno);
-        return NULL;
-    }
-
-    if (length > 0) {
-        // mmap the ashmem area
-        void * ashmem_ptr =
-                mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
-        if (ashmem_ptr == MAP_FAILED) {
-            ALOGE("mmap failed: %s", strerror(errno));
-            jniThrowIOException(env, errno);
-            close(fd);
-            return NULL;
-        }
-
-        // Copy data to ashmem area
-        memcpy(ashmem_ptr, data, length);
-
-        // munmap ashmem area
-        if (munmap(ashmem_ptr, length) < 0) {
-            ALOGE("munmap failed: %s", strerror(errno));
-            jniThrowIOException(env, errno);
-            close(fd);
-            return NULL;
-        }
-    }
-
-    // Make ashmem area read-only
-    if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
-        ALOGE("ashmem_set_prot_region failed: %s", strerror(errno));
-        jniThrowIOException(env, errno);
-        close(fd);
-        return NULL;
-    }
-
-    // Wrap it in a ParcelFileDescriptor
-    return createParcelFileDescriptor(env, fd);
-}
-
-static jobject native_1x1_blob_ashmem(JNIEnv* env, jobject object)
-{
-    int err;
-    sqlite3 * handle = GET_HANDLE(env, object);
-    sqlite3_stmt * statement = GET_STATEMENT(env, object);
-    jobject value = NULL;
-
-    // Execute the statement
-    err = sqlite3_step(statement);
-
-    // Handle the result
-    if (err == SQLITE_ROW) {
-        // No errors, read the data and return it
-        const void * blob = sqlite3_column_blob(statement, 0);
-        if (blob != NULL) {
-            int len = sqlite3_column_bytes(statement, 0);
-            if (len >= 0) {
-                value = create_ashmem_region_with_data(env, blob, len);
-            }
-        }
-    } else {
-        throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(handle));
-    }
-
-    // Reset the statment so it's ready to use again
-    sqlite3_reset(statement);
-
-    return value;
-}
-
-static void native_executeSql(JNIEnv* env, jobject object, jstring sql)
-{
-    char const* sqlString = env->GetStringUTFChars(sql, NULL);
-    sqlite3 * handle = GET_HANDLE(env, object);
-    int err = sqlite3_exec(handle, sqlString, NULL, NULL, NULL);
-    if (err != SQLITE_OK) {
-        throw_sqlite3_exception(env, handle);
-    }
-    env->ReleaseStringUTFChars(sql, sqlString);
-}
-
-static JNINativeMethod sMethods[] =
-{
-     /* name, signature, funcPtr */
-    {"native_execute", "()I", (void *)native_execute},
-    {"native_executeInsert", "()J", (void *)native_executeInsert},
-    {"native_1x1_long", "()J", (void *)native_1x1_long},
-    {"native_1x1_string", "()Ljava/lang/String;", (void *)native_1x1_string},
-    {"native_1x1_blob_ashmem", "()Landroid/os/ParcelFileDescriptor;", (void *)native_1x1_blob_ashmem},
-    {"native_executeSql", "(Ljava/lang/String;)V", (void *)native_executeSql},
-};
-
-int register_android_database_SQLiteStatement(JNIEnv * env)
-{
-    jclass clazz;
-
-    clazz = env->FindClass("android/database/sqlite/SQLiteStatement");
-    if (clazz == NULL) {
-        ALOGE("Can't find android/database/sqlite/SQLiteStatement");
-        return -1;
-    }
-
-    gHandleField = env->GetFieldID(clazz, "nHandle", "I");
-    gStatementField = env->GetFieldID(clazz, "nStatement", "I");
-
-    if (gHandleField == NULL || gStatementField == NULL) {
-        ALOGE("Error locating fields");
-        return -1;
-    }
-
-    return AndroidRuntime::registerNativeMethods(env,
-        "android/database/sqlite/SQLiteStatement", sMethods, NELEM(sMethods));
-}
-
-} // namespace android
diff --git a/core/jni/sqlite3_exception.h b/core/jni/sqlite3_exception.h
deleted file mode 100644
index 13735a1..0000000
--- a/core/jni/sqlite3_exception.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* //device/libs/include/android_runtime/sqlite3_exception.h
-**
-** Copyright 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.
-*/
-
-#ifndef _SQLITE3_EXCEPTION_H
-#define _SQLITE3_EXCEPTION_H 1
-
-#include <jni.h>
-#include <JNIHelp.h>
-//#include <android_runtime/AndroidRuntime.h>
-
-#include <sqlite3.h>
-
-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 // _SQLITE3_EXCEPTION_H