Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright (C) 2006-2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #undef LOG_TAG
     18 #define LOG_TAG "Database"
     19 
     20 #include <utils/Log.h>
     21 #include <utils/String8.h>
     22 #include <utils/String16.h>
     23 
     24 #include <jni.h>
     25 #include <JNIHelp.h>
     26 #include <android_runtime/AndroidRuntime.h>
     27 
     28 #include <sqlite3.h>
     29 #include <sqlite3_android.h>
     30 #include <string.h>
     31 #include <utils/Log.h>
     32 #include <utils/threads.h>
     33 #include <utils/List.h>
     34 #include <utils/Errors.h>
     35 #include <ctype.h>
     36 
     37 #include <stdio.h>
     38 #include <sys/types.h>
     39 #include <sys/socket.h>
     40 #include <netinet/in.h>
     41 #include <string.h>
     42 #include <netdb.h>
     43 #include <sys/ioctl.h>
     44 
     45 #include "sqlite3_exception.h"
     46 
     47 #define UTF16_STORAGE 0
     48 #define INVALID_VERSION -1
     49 #define SQLITE_SOFT_HEAP_LIMIT (4 * 1024 * 1024)
     50 #define ANDROID_TABLE "android_metadata"
     51 /* uncomment the next line to force-enable logging of all statements */
     52 // #define DB_LOG_STATEMENTS
     53 
     54 namespace android {
     55 
     56 enum {
     57     OPEN_READWRITE          = 0x00000000,
     58     OPEN_READONLY           = 0x00000001,
     59     OPEN_READ_MASK          = 0x00000001,
     60     NO_LOCALIZED_COLLATORS  = 0x00000010,
     61     CREATE_IF_NECESSARY     = 0x10000000
     62 };
     63 
     64 static jfieldID offset_db_handle;
     65 
     66 static char *createStr(const char *path) {
     67     int len = strlen(path);
     68     char *str = (char *)malloc(len + 1);
     69     strncpy(str, path, len);
     70     str[len] = NULL;
     71     return str;
     72 }
     73 
     74 static void sqlLogger(void *databaseName, int iErrCode, const char *zMsg) {
     75     // skip printing this message if it is due to certain types of errors
     76     if (iErrCode == SQLITE_CONSTRAINT) return;
     77     LOGI("sqlite returned: error code = %d, msg = %s\n", iErrCode, zMsg);
     78 }
     79 
     80 // register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called.
     81 static void registerLoggingFunc(const char *path) {
     82     static bool loggingFuncSet = false;
     83     if (loggingFuncSet) {
     84         return;
     85     }
     86 
     87     LOGV("Registering sqlite logging func \n");
     88     int err = sqlite3_config(SQLITE_CONFIG_LOG, &sqlLogger, (void *)createStr(path));
     89     if (err != SQLITE_OK) {
     90         LOGE("sqlite_config failed error_code = %d. THIS SHOULD NEVER occur.\n", err);
     91         return;
     92     }
     93     loggingFuncSet = true;
     94 }
     95 
     96 /* public native void dbopen(String path, int flags, String locale); */
     97 static void dbopen(JNIEnv* env, jobject object, jstring pathString, jint flags)
     98 {
     99     int err;
    100     sqlite3 * handle = NULL;
    101     sqlite3_stmt * statement = NULL;
    102     char const * path8 = env->GetStringUTFChars(pathString, NULL);
    103     int sqliteFlags;
    104 
    105     // register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called.
    106     registerLoggingFunc(path8);
    107 
    108     // convert our flags into the sqlite flags
    109     if (flags & CREATE_IF_NECESSARY) {
    110         sqliteFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
    111     } else if (flags & OPEN_READONLY) {
    112         sqliteFlags = SQLITE_OPEN_READONLY;
    113     } else {
    114         sqliteFlags = SQLITE_OPEN_READWRITE;
    115     }
    116 
    117     err = sqlite3_open_v2(path8, &handle, sqliteFlags, NULL);
    118     if (err != SQLITE_OK) {
    119         LOGE("sqlite3_open_v2(\"%s\", &handle, %d, NULL) failed\n", path8, sqliteFlags);
    120         throw_sqlite3_exception(env, handle);
    121         goto done;
    122     }
    123 
    124     // The soft heap limit prevents the page cache allocations from growing
    125     // beyond the given limit, no matter what the max page cache sizes are
    126     // set to. The limit does not, as of 3.5.0, affect any other allocations.
    127     sqlite3_soft_heap_limit(SQLITE_SOFT_HEAP_LIMIT);
    128 
    129     // Set the default busy handler to retry for 1000ms and then return SQLITE_BUSY
    130     err = sqlite3_busy_timeout(handle, 1000 /* ms */);
    131     if (err != SQLITE_OK) {
    132         LOGE("sqlite3_busy_timeout(handle, 1000) failed for \"%s\"\n", path8);
    133         throw_sqlite3_exception(env, handle);
    134         goto done;
    135     }
    136 
    137 #ifdef DB_INTEGRITY_CHECK
    138     static const char* integritySql = "pragma integrity_check(1);";
    139     err = sqlite3_prepare_v2(handle, integritySql, -1, &statement, NULL);
    140     if (err != SQLITE_OK) {
    141         LOGE("sqlite_prepare_v2(handle, \"%s\") failed for \"%s\"\n", integritySql, path8);
    142         throw_sqlite3_exception(env, handle);
    143         goto done;
    144     }
    145 
    146     // first is OK or error message
    147     err = sqlite3_step(statement);
    148     if (err != SQLITE_ROW) {
    149         LOGE("integrity check failed for \"%s\"\n", integritySql, path8);
    150         throw_sqlite3_exception(env, handle);
    151         goto done;
    152     } else {
    153         const char *text = (const char*)sqlite3_column_text(statement, 0);
    154         if (strcmp(text, "ok") != 0) {
    155             LOGE("integrity check failed for \"%s\": %s\n", integritySql, path8, text);
    156             jniThrowException(env, "android/database/sqlite/SQLiteDatabaseCorruptException", text);
    157             goto done;
    158         }
    159     }
    160 #endif
    161 
    162     err = register_android_functions(handle, UTF16_STORAGE);
    163     if (err) {
    164         throw_sqlite3_exception(env, handle);
    165         goto done;
    166     }
    167 
    168     LOGV("Opened '%s' - %p\n", path8, handle);
    169     env->SetIntField(object, offset_db_handle, (int) handle);
    170     handle = NULL;  // The caller owns the handle now.
    171 
    172 done:
    173     // Release allocated resources
    174     if (path8 != NULL) env->ReleaseStringUTFChars(pathString, path8);
    175     if (statement != NULL) sqlite3_finalize(statement);
    176     if (handle != NULL) sqlite3_close(handle);
    177 }
    178 
    179 static char *getDatabaseName(JNIEnv* env, sqlite3 * handle, jstring databaseName) {
    180     char const *path = env->GetStringUTFChars(databaseName, NULL);
    181     if (path == NULL) {
    182         LOGE("Failure in getDatabaseName(). VM ran out of memory?\n");
    183         return NULL; // VM would have thrown OutOfMemoryError
    184     }
    185     char *dbNameStr = createStr(path);
    186     env->ReleaseStringUTFChars(databaseName, path);
    187     return dbNameStr;
    188 }
    189 
    190 static void sqlTrace(void *databaseName, const char *sql) {
    191     LOGI("sql_statement|%s|%s\n", (char *)databaseName, sql);
    192 }
    193 
    194 /* public native void enableSqlTracing(); */
    195 static void enableSqlTracing(JNIEnv* env, jobject object, jstring databaseName)
    196 {
    197     sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
    198     sqlite3_trace(handle, &sqlTrace, (void *)getDatabaseName(env, handle, databaseName));
    199 }
    200 
    201 static void sqlProfile(void *databaseName, const char *sql, sqlite3_uint64 tm) {
    202     double d = tm/1000000.0;
    203     LOGI("elapsedTime4Sql|%s|%.3f ms|%s\n", (char *)databaseName, d, sql);
    204 }
    205 
    206 /* public native void enableSqlProfiling(); */
    207 static void enableSqlProfiling(JNIEnv* env, jobject object, jstring databaseName)
    208 {
    209     sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
    210     sqlite3_profile(handle, &sqlProfile, (void *)getDatabaseName(env, handle, databaseName));
    211 }
    212 
    213 
    214 /* public native void close(); */
    215 static void dbclose(JNIEnv* env, jobject object)
    216 {
    217     sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
    218 
    219     if (handle != NULL) {
    220         // release the memory associated with the traceFuncArg in enableSqlTracing function
    221         void *traceFuncArg = sqlite3_trace(handle, &sqlTrace, NULL);
    222         if (traceFuncArg != NULL) {
    223             free(traceFuncArg);
    224         }
    225         // release the memory associated with the traceFuncArg in enableSqlProfiling function
    226         traceFuncArg = sqlite3_profile(handle, &sqlProfile, NULL);
    227         if (traceFuncArg != NULL) {
    228             free(traceFuncArg);
    229         }
    230         LOGV("Closing database: handle=%p\n", handle);
    231         int result = sqlite3_close(handle);
    232         if (result == SQLITE_OK) {
    233             LOGV("Closed %p\n", handle);
    234             env->SetIntField(object, offset_db_handle, 0);
    235         } else {
    236             // This can happen if sub-objects aren't closed first.  Make sure the caller knows.
    237             throw_sqlite3_exception(env, handle);
    238             LOGE("sqlite3_close(%p) failed: %d\n", handle, result);
    239         }
    240     }
    241 }
    242 
    243 /* public native void native_execSQL(String sql); */
    244 static void native_execSQL(JNIEnv* env, jobject object, jstring sqlString)
    245 {
    246     int err;
    247     int stepErr;
    248     sqlite3_stmt * statement = NULL;
    249     sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
    250     jchar const * sql = env->GetStringChars(sqlString, NULL);
    251     jsize sqlLen = env->GetStringLength(sqlString);
    252 
    253     if (sql == NULL || sqlLen == 0) {
    254         jniThrowException(env, "java/lang/IllegalArgumentException", "You must supply an SQL string");
    255         return;
    256     }
    257 
    258     err = sqlite3_prepare16_v2(handle, sql, sqlLen * 2, &statement, NULL);
    259 
    260     env->ReleaseStringChars(sqlString, sql);
    261 
    262     if (err != SQLITE_OK) {
    263         char const * sql8 = env->GetStringUTFChars(sqlString, NULL);
    264         LOGE("Failure %d (%s) on %p when preparing '%s'.\n", err, sqlite3_errmsg(handle), handle, sql8);
    265         throw_sqlite3_exception(env, handle, sql8);
    266         env->ReleaseStringUTFChars(sqlString, sql8);
    267         return;
    268     }
    269 
    270     stepErr = sqlite3_step(statement);
    271     err = sqlite3_finalize(statement);
    272 
    273     if (stepErr != SQLITE_DONE) {
    274         if (stepErr == SQLITE_ROW) {
    275             throw_sqlite3_exception(env, "Queries cannot be performed using execSQL(), use query() instead.");
    276         } else {
    277             char const * sql8 = env->GetStringUTFChars(sqlString, NULL);
    278             LOGE("Failure %d (%s) on %p when executing '%s'\n", err, sqlite3_errmsg(handle), handle, sql8);
    279             throw_sqlite3_exception(env, handle, sql8);
    280             env->ReleaseStringUTFChars(sqlString, sql8);
    281 
    282         }
    283     } else
    284 #ifndef DB_LOG_STATEMENTS
    285     IF_LOGV()
    286 #endif
    287     {
    288         char const * sql8 = env->GetStringUTFChars(sqlString, NULL);
    289         LOGV("Success on %p when executing '%s'\n", handle, sql8);
    290         env->ReleaseStringUTFChars(sqlString, sql8);
    291     }
    292 }
    293 
    294 /* native long lastInsertRow(); */
    295 static jlong lastInsertRow(JNIEnv* env, jobject object)
    296 {
    297     sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
    298 
    299     return sqlite3_last_insert_rowid(handle);
    300 }
    301 
    302 /* native int lastChangeCount(); */
    303 static jint lastChangeCount(JNIEnv* env, jobject object)
    304 {
    305     sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
    306 
    307     return sqlite3_changes(handle);
    308 }
    309 
    310 /* native int native_getDbLookaside(); */
    311 static jint native_getDbLookaside(JNIEnv* env, jobject object)
    312 {
    313     sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
    314     int pCur = -1;
    315     int unused;
    316     sqlite3_db_status(handle, SQLITE_DBSTATUS_LOOKASIDE_USED, &pCur, &unused, 0);
    317     return pCur;
    318 }
    319 
    320 /* set locale in the android_metadata table, install localized collators, and rebuild indexes */
    321 static void native_setLocale(JNIEnv* env, jobject object, jstring localeString, jint flags)
    322 {
    323     if ((flags & NO_LOCALIZED_COLLATORS)) return;
    324 
    325     int err;
    326     char const* locale8 = env->GetStringUTFChars(localeString, NULL);
    327     sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
    328     sqlite3_stmt* stmt = NULL;
    329     char** meta = NULL;
    330     int rowCount, colCount;
    331     char* dbLocale = NULL;
    332 
    333     // create the table, if necessary and possible
    334     if (!(flags & OPEN_READONLY)) {
    335         static const char *createSql ="CREATE TABLE IF NOT EXISTS " ANDROID_TABLE " (locale TEXT)";
    336         err = sqlite3_exec(handle, createSql, NULL, NULL, NULL);
    337         if (err != SQLITE_OK) {
    338             LOGE("CREATE TABLE " ANDROID_TABLE " failed\n");
    339             throw_sqlite3_exception(env, handle);
    340             goto done;
    341         }
    342     }
    343 
    344     // try to read from the table
    345     static const char *selectSql = "SELECT locale FROM " ANDROID_TABLE " LIMIT 1";
    346     err = sqlite3_get_table(handle, selectSql, &meta, &rowCount, &colCount, NULL);
    347     if (err != SQLITE_OK) {
    348         LOGE("SELECT locale FROM " ANDROID_TABLE " failed\n");
    349         throw_sqlite3_exception(env, handle);
    350         goto done;
    351     }
    352 
    353     dbLocale = (rowCount >= 1) ? meta[colCount] : NULL;
    354 
    355     if (dbLocale != NULL && !strcmp(dbLocale, locale8)) {
    356         // database locale is the same as the desired locale; set up the collators and go
    357         err = register_localized_collators(handle, locale8, UTF16_STORAGE);
    358         if (err != SQLITE_OK) throw_sqlite3_exception(env, handle);
    359         goto done;   // no database changes needed
    360     }
    361 
    362     if ((flags & OPEN_READONLY)) {
    363         // read-only database, so we're going to have to put up with whatever we got
    364         // For registering new index. Not for modifing the read-only database.
    365         err = register_localized_collators(handle, locale8, UTF16_STORAGE);
    366         if (err != SQLITE_OK) throw_sqlite3_exception(env, handle);
    367         goto done;
    368     }
    369 
    370     // need to update android_metadata and indexes atomically, so use a transaction...
    371     err = sqlite3_exec(handle, "BEGIN TRANSACTION", NULL, NULL, NULL);
    372     if (err != SQLITE_OK) {
    373         LOGE("BEGIN TRANSACTION failed setting locale\n");
    374         throw_sqlite3_exception(env, handle);
    375         goto done;
    376     }
    377 
    378     err = register_localized_collators(handle, locale8, UTF16_STORAGE);
    379     if (err != SQLITE_OK) {
    380         LOGE("register_localized_collators() failed setting locale\n");
    381         throw_sqlite3_exception(env, handle);
    382         goto rollback;
    383     }
    384 
    385     err = sqlite3_exec(handle, "DELETE FROM " ANDROID_TABLE, NULL, NULL, NULL);
    386     if (err != SQLITE_OK) {
    387         LOGE("DELETE failed setting locale\n");
    388         throw_sqlite3_exception(env, handle);
    389         goto rollback;
    390     }
    391 
    392     static const char *sql = "INSERT INTO " ANDROID_TABLE " (locale) VALUES(?);";
    393     err = sqlite3_prepare_v2(handle, sql, -1, &stmt, NULL);
    394     if (err != SQLITE_OK) {
    395         LOGE("sqlite3_prepare_v2(\"%s\") failed\n", sql);
    396         throw_sqlite3_exception(env, handle);
    397         goto rollback;
    398     }
    399 
    400     err = sqlite3_bind_text(stmt, 1, locale8, -1, SQLITE_TRANSIENT);
    401     if (err != SQLITE_OK) {
    402         LOGE("sqlite3_bind_text() failed setting locale\n");
    403         throw_sqlite3_exception(env, handle);
    404         goto rollback;
    405     }
    406 
    407     err = sqlite3_step(stmt);
    408     if (err != SQLITE_OK && err != SQLITE_DONE) {
    409         LOGE("sqlite3_step(\"%s\") failed setting locale\n", sql);
    410         throw_sqlite3_exception(env, handle);
    411         goto rollback;
    412     }
    413 
    414     err = sqlite3_exec(handle, "REINDEX LOCALIZED", NULL, NULL, NULL);
    415     if (err != SQLITE_OK) {
    416         LOGE("REINDEX LOCALIZED failed\n");
    417         throw_sqlite3_exception(env, handle);
    418         goto rollback;
    419     }
    420 
    421     // all done, yay!
    422     err = sqlite3_exec(handle, "COMMIT TRANSACTION", NULL, NULL, NULL);
    423     if (err != SQLITE_OK) {
    424         LOGE("COMMIT TRANSACTION failed setting locale\n");
    425         throw_sqlite3_exception(env, handle);
    426         goto done;
    427     }
    428 
    429 rollback:
    430     if (err != SQLITE_OK) {
    431         sqlite3_exec(handle, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
    432     }
    433 
    434 done:
    435     if (locale8 != NULL) env->ReleaseStringUTFChars(localeString, locale8);
    436     if (stmt != NULL) sqlite3_finalize(stmt);
    437     if (meta != NULL) sqlite3_free_table(meta);
    438 }
    439 
    440 static jint native_releaseMemory(JNIEnv *env, jobject clazz)
    441 {
    442     // Attempt to release as much memory from the
    443     return sqlite3_release_memory(SQLITE_SOFT_HEAP_LIMIT);
    444 }
    445 
    446 static JNINativeMethod sMethods[] =
    447 {
    448     /* name, signature, funcPtr */
    449     {"dbopen", "(Ljava/lang/String;I)V", (void *)dbopen},
    450     {"dbclose", "()V", (void *)dbclose},
    451     {"enableSqlTracing", "(Ljava/lang/String;)V", (void *)enableSqlTracing},
    452     {"enableSqlProfiling", "(Ljava/lang/String;)V", (void *)enableSqlProfiling},
    453     {"native_execSQL", "(Ljava/lang/String;)V", (void *)native_execSQL},
    454     {"lastInsertRow", "()J", (void *)lastInsertRow},
    455     {"lastChangeCount", "()I", (void *)lastChangeCount},
    456     {"native_setLocale", "(Ljava/lang/String;I)V", (void *)native_setLocale},
    457     {"native_getDbLookaside", "()I", (void *)native_getDbLookaside},
    458     {"releaseMemory", "()I", (void *)native_releaseMemory},
    459 };
    460 
    461 int register_android_database_SQLiteDatabase(JNIEnv *env)
    462 {
    463     jclass clazz;
    464 
    465     clazz = env->FindClass("android/database/sqlite/SQLiteDatabase");
    466     if (clazz == NULL) {
    467         LOGE("Can't find android/database/sqlite/SQLiteDatabase\n");
    468         return -1;
    469     }
    470 
    471     offset_db_handle = env->GetFieldID(clazz, "mNativeHandle", "I");
    472     if (offset_db_handle == NULL) {
    473         LOGE("Can't find SQLiteDatabase.mNativeHandle\n");
    474         return -1;
    475     }
    476 
    477     return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteDatabase", sMethods, NELEM(sMethods));
    478 }
    479 
    480 /* throw a SQLiteException with a message appropriate for the error in handle */
    481 void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) {
    482     throw_sqlite3_exception(env, handle, NULL);
    483 }
    484 
    485 /* throw a SQLiteException with the given message */
    486 void throw_sqlite3_exception(JNIEnv* env, const char* message) {
    487     throw_sqlite3_exception(env, NULL, message);
    488 }
    489 
    490 /* throw a SQLiteException with a message appropriate for the error in handle
    491    concatenated with the given message
    492  */
    493 void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message) {
    494     if (handle) {
    495         throw_sqlite3_exception(env, sqlite3_errcode(handle),
    496                                 sqlite3_errmsg(handle), message);
    497     } else {
    498         // we use SQLITE_OK so that a generic SQLiteException is thrown;
    499         // any code not specified in the switch statement below would do.
    500         throw_sqlite3_exception(env, SQLITE_OK, "unknown error", message);
    501     }
    502 }
    503 
    504 /* throw a SQLiteException for a given error code */
    505 void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message) {
    506     if (errcode == SQLITE_DONE) {
    507         throw_sqlite3_exception(env, errcode, NULL, message);
    508     } else {
    509         char temp[21];
    510         sprintf(temp, "error code %d", errcode);
    511         throw_sqlite3_exception(env, errcode, temp, message);
    512     }
    513 }
    514 
    515 /* throw a SQLiteException for a given error code, sqlite3message, and
    516    user message
    517  */
    518 void throw_sqlite3_exception(JNIEnv* env, int errcode,
    519                              const char* sqlite3Message, const char* message) {
    520     const char* exceptionClass;
    521     switch (errcode) {
    522         case SQLITE_IOERR:
    523             exceptionClass = "android/database/sqlite/SQLiteDiskIOException";
    524             break;
    525         case SQLITE_CORRUPT:
    526             exceptionClass = "android/database/sqlite/SQLiteDatabaseCorruptException";
    527             break;
    528         case SQLITE_CONSTRAINT:
    529            exceptionClass = "android/database/sqlite/SQLiteConstraintException";
    530            break;
    531         case SQLITE_ABORT:
    532            exceptionClass = "android/database/sqlite/SQLiteAbortException";
    533            break;
    534         case SQLITE_DONE:
    535            exceptionClass = "android/database/sqlite/SQLiteDoneException";
    536            break;
    537         case SQLITE_FULL:
    538            exceptionClass = "android/database/sqlite/SQLiteFullException";
    539            break;
    540         case SQLITE_MISUSE:
    541            exceptionClass = "android/database/sqlite/SQLiteMisuseException";
    542            break;
    543         default:
    544            exceptionClass = "android/database/sqlite/SQLiteException";
    545            break;
    546     }
    547 
    548     if (sqlite3Message != NULL && message != NULL) {
    549         char* fullMessage = (char *)malloc(strlen(sqlite3Message) + strlen(message) + 3);
    550         if (fullMessage != NULL) {
    551             strcpy(fullMessage, sqlite3Message);
    552             strcat(fullMessage, ": ");
    553             strcat(fullMessage, message);
    554             jniThrowException(env, exceptionClass, fullMessage);
    555             free(fullMessage);
    556         } else {
    557             jniThrowException(env, exceptionClass, sqlite3Message);
    558         }
    559     } else if (sqlite3Message != NULL) {
    560         jniThrowException(env, exceptionClass, sqlite3Message);
    561     } else {
    562         jniThrowException(env, exceptionClass, message);
    563     }
    564 }
    565 
    566 
    567 } // namespace android
    568