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