1 /*
2  * Copyright (C) 2011 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 #define LOG_TAG "SQLiteConnection"
18 
19 #include <jni.h>
20 #include <nativehelper/JNIHelp.h>
21 #include <android_runtime/AndroidRuntime.h>
22 #include <android_runtime/Log.h>
23 
24 #include <utils/Log.h>
25 #include <utils/String8.h>
26 #include <utils/String16.h>
27 #include <cutils/ashmem.h>
28 #include <sys/mman.h>
29 
30 #include <string.h>
31 #include <unistd.h>
32 
33 #include <androidfw/CursorWindow.h>
34 
35 #include <sqlite3.h>
36 #include <sqlite3_android.h>
37 
38 #include "android_database_SQLiteCommon.h"
39 
40 #include "core_jni_helpers.h"
41 
42 // Set to 1 to use UTF16 storage for localized indexes.
43 #define UTF16_STORAGE 0
44 
45 namespace android {
46 
47 /* Busy timeout in milliseconds.
48  * If another connection (possibly in another process) has the database locked for
49  * longer than this amount of time then SQLite will generate a SQLITE_BUSY error.
50  * The SQLITE_BUSY error is then raised as a SQLiteDatabaseLockedException.
51  *
52  * In ordinary usage, busy timeouts are quite rare.  Most databases only ever
53  * have a single open connection at a time unless they are using WAL.  When using
54  * WAL, a timeout could occur if one connection is busy performing an auto-checkpoint
55  * operation.  The busy timeout needs to be long enough to tolerate slow I/O write
56  * operations but not so long as to cause the application to hang indefinitely if
57  * there is a problem acquiring a database lock.
58  */
59 static const int BUSY_TIMEOUT_MS = 2500;
60 
61 static struct {
62     jmethodID apply;
63 } gUnaryOperator;
64 
65 static struct {
66     jmethodID apply;
67 } gBinaryOperator;
68 
69 struct SQLiteConnection {
70     // Open flags.
71     // Must be kept in sync with the constants defined in SQLiteDatabase.java.
72     enum {
73         OPEN_READWRITE          = 0x00000000,
74         OPEN_READONLY           = 0x00000001,
75         OPEN_READ_MASK          = 0x00000001,
76         NO_LOCALIZED_COLLATORS  = 0x00000010,
77         CREATE_IF_NECESSARY     = 0x10000000,
78     };
79 
80     sqlite3* const db;
81     const int openFlags;
82     const String8 path;
83     const String8 label;
84 
85     // The prepared statement used to determine which tables are updated by a statement.  This
86     // is is initially null.  It is set non-null on first use.
87     sqlite3_stmt* tableQuery;
88 
89     volatile bool canceled;
90 
SQLiteConnectionandroid::SQLiteConnection91     SQLiteConnection(sqlite3* db, int openFlags, const String8& path, const String8& label) :
92             db(db), openFlags(openFlags), path(path), label(label), tableQuery(nullptr),
93             canceled(false) { }
94 
95 };
96 
97 // Called each time a statement begins execution, when tracing is enabled.
sqliteTraceCallback(void * data,const char * sql)98 static void sqliteTraceCallback(void *data, const char *sql) {
99     SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
100     ALOG(LOG_VERBOSE, SQLITE_TRACE_TAG, "%s: \"%s\"\n", connection->label.c_str(), sql);
101 }
102 
103 // Called each time a statement finishes execution, when profiling is enabled.
sqliteProfileCallback(void * data,const char * sql,sqlite3_uint64 tm)104 static void sqliteProfileCallback(void *data, const char *sql, sqlite3_uint64 tm) {
105     SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
106     ALOG(LOG_VERBOSE, SQLITE_PROFILE_TAG, "%s: \"%s\" took %0.3f ms\n", connection->label.c_str(),
107          sql, tm * 0.000001f);
108 }
109 
110 // Called after each SQLite VM instruction when cancelation is enabled.
sqliteProgressHandlerCallback(void * data)111 static int sqliteProgressHandlerCallback(void* data) {
112     SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
113     return connection->canceled;
114 }
115 
116 
nativeOpen(JNIEnv * env,jclass clazz,jstring pathStr,jint openFlags,jstring labelStr,jboolean enableTrace,jboolean enableProfile,jint lookasideSz,jint lookasideCnt)117 static jlong nativeOpen(JNIEnv* env, jclass clazz, jstring pathStr, jint openFlags,
118         jstring labelStr, jboolean enableTrace, jboolean enableProfile, jint lookasideSz,
119         jint lookasideCnt) {
120     int sqliteFlags;
121     if (openFlags & SQLiteConnection::CREATE_IF_NECESSARY) {
122         sqliteFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
123     } else if (openFlags & SQLiteConnection::OPEN_READONLY) {
124         sqliteFlags = SQLITE_OPEN_READONLY;
125     } else {
126         sqliteFlags = SQLITE_OPEN_READWRITE;
127     }
128 
129     const char* pathChars = env->GetStringUTFChars(pathStr, NULL);
130     String8 path(pathChars);
131     env->ReleaseStringUTFChars(pathStr, pathChars);
132 
133     const char* labelChars = env->GetStringUTFChars(labelStr, NULL);
134     String8 label(labelChars);
135     env->ReleaseStringUTFChars(labelStr, labelChars);
136 
137     sqlite3* db;
138     int err = sqlite3_open_v2(path.c_str(), &db, sqliteFlags, NULL);
139     if (err != SQLITE_OK) {
140         throw_sqlite3_exception_errcode(env, err, "Could not open database");
141         return 0;
142     }
143 
144     if (lookasideSz >= 0 && lookasideCnt >= 0) {
145         int err = sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE, NULL, lookasideSz, lookasideCnt);
146         if (err != SQLITE_OK) {
147             ALOGE("sqlite3_db_config(..., %d, %d) failed: %d", lookasideSz, lookasideCnt, err);
148             throw_sqlite3_exception(env, db, "Cannot set lookaside");
149             sqlite3_close(db);
150             return 0;
151         }
152     }
153 
154     // Check that the database is really read/write when that is what we asked for.
155     if ((sqliteFlags & SQLITE_OPEN_READWRITE) && sqlite3_db_readonly(db, NULL)) {
156         throw_sqlite3_exception(env, db, "Could not open the database in read/write mode.");
157         sqlite3_close(db);
158         return 0;
159     }
160 
161     // Set the default busy handler to retry automatically before returning SQLITE_BUSY.
162     err = sqlite3_busy_timeout(db, BUSY_TIMEOUT_MS);
163     if (err != SQLITE_OK) {
164         throw_sqlite3_exception(env, db, "Could not set busy timeout");
165         sqlite3_close(db);
166         return 0;
167     }
168 
169     // Register custom Android functions.
170     err = register_android_functions(db, UTF16_STORAGE);
171     if (err) {
172         throw_sqlite3_exception(env, db, "Could not register Android SQL functions.");
173         sqlite3_close(db);
174         return 0;
175     }
176 
177     // Create wrapper object.
178     SQLiteConnection* connection = new SQLiteConnection(db, openFlags, path, label);
179 
180     // Enable tracing and profiling if requested.
181     if (enableTrace) {
182         sqlite3_trace(db, &sqliteTraceCallback, connection);
183     }
184     if (enableProfile) {
185         sqlite3_profile(db, &sqliteProfileCallback, connection);
186     }
187 
188     ALOGV("Opened connection %p with label '%s'", db, label.c_str());
189     return reinterpret_cast<jlong>(connection);
190 }
191 
nativeClose(JNIEnv * env,jclass clazz,jlong connectionPtr)192 static void nativeClose(JNIEnv* env, jclass clazz, jlong connectionPtr) {
193     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
194 
195     if (connection) {
196         ALOGV("Closing connection %p", connection->db);
197         if (connection->tableQuery != nullptr) {
198             sqlite3_finalize(connection->tableQuery);
199         }
200         int err = sqlite3_close(connection->db);
201         if (err != SQLITE_OK) {
202             // This can happen if sub-objects aren't closed first.  Make sure the caller knows.
203             ALOGE("sqlite3_close(%p) failed: %d", connection->db, err);
204             throw_sqlite3_exception(env, connection->db, "Count not close db.");
205             return;
206         }
207 
208         delete connection;
209     }
210 }
211 
sqliteCustomScalarFunctionCallback(sqlite3_context * context,int argc,sqlite3_value ** argv)212 static void sqliteCustomScalarFunctionCallback(sqlite3_context *context,
213         int argc, sqlite3_value **argv) {
214     JNIEnv* env = AndroidRuntime::getJNIEnv();
215     jobject functionObjGlobal = reinterpret_cast<jobject>(sqlite3_user_data(context));
216     ScopedLocalRef<jobject> functionObj(env, env->NewLocalRef(functionObjGlobal));
217     ScopedLocalRef<jstring> argString(env,
218             env->NewStringUTF(reinterpret_cast<const char*>(sqlite3_value_text(argv[0]))));
219     ScopedLocalRef<jstring> resString(env,
220             (jstring) env->CallObjectMethod(functionObj.get(), gUnaryOperator.apply, argString.get()));
221 
222     if (env->ExceptionCheck()) {
223         ALOGE("Exception thrown by custom scalar function");
224         sqlite3_result_error(context, "Exception thrown by custom scalar function", -1);
225         env->ExceptionDescribe();
226         env->ExceptionClear();
227         return;
228     }
229 
230     if (resString.get() == nullptr) {
231         sqlite3_result_null(context);
232     } else {
233         ScopedUtfChars res(env, resString.get());
234         sqlite3_result_text(context, res.c_str(), -1, SQLITE_TRANSIENT);
235     }
236 }
237 
sqliteCustomScalarFunctionDestructor(void * data)238 static void sqliteCustomScalarFunctionDestructor(void* data) {
239     jobject functionObjGlobal = reinterpret_cast<jobject>(data);
240 
241     JNIEnv* env = AndroidRuntime::getJNIEnv();
242     env->DeleteGlobalRef(functionObjGlobal);
243 }
244 
nativeRegisterCustomScalarFunction(JNIEnv * env,jclass clazz,jlong connectionPtr,jstring functionName,jobject functionObj)245 static void nativeRegisterCustomScalarFunction(JNIEnv* env, jclass clazz, jlong connectionPtr,
246         jstring functionName, jobject functionObj) {
247     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
248 
249     jobject functionObjGlobal = env->NewGlobalRef(functionObj);
250     ScopedUtfChars functionNameChars(env, functionName);
251     int err = sqlite3_create_function_v2(connection->db,
252             functionNameChars.c_str(), 1, SQLITE_UTF8,
253             reinterpret_cast<void*>(functionObjGlobal),
254             &sqliteCustomScalarFunctionCallback,
255             nullptr,
256             nullptr,
257             &sqliteCustomScalarFunctionDestructor);
258 
259     if (err != SQLITE_OK) {
260         ALOGE("sqlite3_create_function returned %d", err);
261         env->DeleteGlobalRef(functionObjGlobal);
262         throw_sqlite3_exception(env, connection->db);
263         return;
264     }
265 }
266 
sqliteCustomAggregateFunctionStep(sqlite3_context * context,int argc,sqlite3_value ** argv)267 static void sqliteCustomAggregateFunctionStep(sqlite3_context *context,
268         int argc, sqlite3_value **argv) {
269     char** agg = reinterpret_cast<char**>(
270             sqlite3_aggregate_context(context, sizeof(const char**)));
271     if (agg == nullptr) {
272         return;
273     } else if (*agg == nullptr) {
274         // During our first call the best we can do is allocate our result
275         // holder and populate it with our first value; we'll reduce it
276         // against any additional values in future calls
277         const char* res = reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
278         if (res == nullptr) {
279             *agg = nullptr;
280         } else {
281             *agg = strdup(res);
282         }
283         return;
284     }
285 
286     JNIEnv* env = AndroidRuntime::getJNIEnv();
287     jobject functionObjGlobal = reinterpret_cast<jobject>(sqlite3_user_data(context));
288     ScopedLocalRef<jobject> functionObj(env, env->NewLocalRef(functionObjGlobal));
289     ScopedLocalRef<jstring> arg0String(env,
290             env->NewStringUTF(reinterpret_cast<const char*>(*agg)));
291     ScopedLocalRef<jstring> arg1String(env,
292             env->NewStringUTF(reinterpret_cast<const char*>(sqlite3_value_text(argv[0]))));
293     ScopedLocalRef<jstring> resString(env,
294             (jstring) env->CallObjectMethod(functionObj.get(), gBinaryOperator.apply,
295                     arg0String.get(), arg1String.get()));
296 
297     if (env->ExceptionCheck()) {
298         ALOGE("Exception thrown by custom aggregate function");
299         sqlite3_result_error(context, "Exception thrown by custom aggregate function", -1);
300         env->ExceptionDescribe();
301         env->ExceptionClear();
302         return;
303     }
304 
305     // One way or another, we have a new value to collect, and we need to
306     // free our previous value
307     if (*agg != nullptr) {
308         free(*agg);
309     }
310     if (resString.get() == nullptr) {
311         *agg = nullptr;
312     } else {
313         ScopedUtfChars res(env, resString.get());
314         *agg = strdup(res.c_str());
315     }
316 }
317 
sqliteCustomAggregateFunctionFinal(sqlite3_context * context)318 static void sqliteCustomAggregateFunctionFinal(sqlite3_context *context) {
319     // We pass zero size here to avoid allocating for empty sets
320     char** agg = reinterpret_cast<char**>(
321             sqlite3_aggregate_context(context, 0));
322     if (agg == nullptr) {
323         return;
324     } else if (*agg == nullptr) {
325         sqlite3_result_null(context);
326     } else {
327         sqlite3_result_text(context, *agg, -1, SQLITE_TRANSIENT);
328         free(*agg);
329     }
330 }
331 
sqliteCustomAggregateFunctionDestructor(void * data)332 static void sqliteCustomAggregateFunctionDestructor(void* data) {
333     jobject functionObjGlobal = reinterpret_cast<jobject>(data);
334 
335     JNIEnv* env = AndroidRuntime::getJNIEnv();
336     env->DeleteGlobalRef(functionObjGlobal);
337 }
338 
nativeRegisterCustomAggregateFunction(JNIEnv * env,jclass clazz,jlong connectionPtr,jstring functionName,jobject functionObj)339 static void nativeRegisterCustomAggregateFunction(JNIEnv* env, jclass clazz, jlong connectionPtr,
340         jstring functionName, jobject functionObj) {
341     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
342 
343     jobject functionObjGlobal = env->NewGlobalRef(functionObj);
344     ScopedUtfChars functionNameChars(env, functionName);
345     int err = sqlite3_create_function_v2(connection->db,
346             functionNameChars.c_str(), 1, SQLITE_UTF8,
347             reinterpret_cast<void*>(functionObjGlobal),
348             nullptr,
349             &sqliteCustomAggregateFunctionStep,
350             &sqliteCustomAggregateFunctionFinal,
351             &sqliteCustomAggregateFunctionDestructor);
352 
353     if (err != SQLITE_OK) {
354         ALOGE("sqlite3_create_function returned %d", err);
355         env->DeleteGlobalRef(functionObjGlobal);
356         throw_sqlite3_exception(env, connection->db);
357         return;
358     }
359 }
360 
nativeRegisterLocalizedCollators(JNIEnv * env,jclass clazz,jlong connectionPtr,jstring localeStr)361 static void nativeRegisterLocalizedCollators(JNIEnv* env, jclass clazz, jlong connectionPtr,
362         jstring localeStr) {
363     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
364 
365     const char* locale = env->GetStringUTFChars(localeStr, NULL);
366     int err = register_localized_collators(connection->db, locale, UTF16_STORAGE);
367     env->ReleaseStringUTFChars(localeStr, locale);
368 
369     if (err != SQLITE_OK) {
370         throw_sqlite3_exception(env, connection->db);
371     }
372 }
373 
nativePrepareStatement(JNIEnv * env,jclass clazz,jlong connectionPtr,jstring sqlString)374 static jlong nativePrepareStatement(JNIEnv* env, jclass clazz, jlong connectionPtr,
375         jstring sqlString) {
376     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
377 
378     jsize sqlLength = env->GetStringLength(sqlString);
379     const jchar* sql = env->GetStringCritical(sqlString, NULL);
380     sqlite3_stmt* statement;
381     int err = sqlite3_prepare16_v2(connection->db,
382             sql, sqlLength * sizeof(jchar), &statement, NULL);
383     env->ReleaseStringCritical(sqlString, sql);
384 
385     if (err != SQLITE_OK) {
386         // Error messages like 'near ")": syntax error' are not
387         // always helpful enough, so construct an error string that
388         // includes the query itself.
389         const char *query = env->GetStringUTFChars(sqlString, NULL);
390         char *message = (char*) malloc(strlen(query) + 50);
391         if (message) {
392             strcpy(message, ", while compiling: "); // less than 50 chars
393             strcat(message, query);
394         }
395         env->ReleaseStringUTFChars(sqlString, query);
396         throw_sqlite3_exception(env, connection->db, message);
397         free(message);
398         return 0;
399     }
400 
401     ALOGV("Prepared statement %p on connection %p", statement, connection->db);
402     return reinterpret_cast<jlong>(statement);
403 }
404 
nativeFinalizeStatement(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)405 static void nativeFinalizeStatement(JNIEnv* env, jclass clazz, jlong connectionPtr,
406         jlong statementPtr) {
407     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
408     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
409 
410     // We ignore the result of sqlite3_finalize because it is really telling us about
411     // whether any errors occurred while executing the statement.  The statement itself
412     // is always finalized regardless.
413     ALOGV("Finalized statement %p on connection %p", statement, connection->db);
414     sqlite3_finalize(statement);
415 }
416 
nativeGetParameterCount(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)417 static jint nativeGetParameterCount(JNIEnv* env, jclass clazz, jlong connectionPtr,
418         jlong statementPtr) {
419     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
420 
421     return sqlite3_bind_parameter_count(statement);
422 }
423 
nativeIsReadOnly(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)424 static jboolean nativeIsReadOnly(JNIEnv* env, jclass clazz, jlong connectionPtr,
425         jlong statementPtr) {
426     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
427 
428     return sqlite3_stmt_readonly(statement) != 0;
429 }
430 
nativeUpdatesTempOnly(JNIEnv * env,jclass,jlong connectionPtr,jlong statementPtr)431 static jboolean nativeUpdatesTempOnly(JNIEnv* env, jclass,
432         jlong connectionPtr, jlong statementPtr) {
433     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
434     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
435 
436     int result = SQLITE_OK;
437     if (connection->tableQuery == nullptr) {
438         static char const* sql =
439                 "SELECT NULL FROM tables_used(?) WHERE schema != 'temp' AND wr != 0";
440         result = sqlite3_prepare_v2(connection->db, sql, -1, &connection->tableQuery, nullptr);
441         if (result != SQLITE_OK) {
442             ALOGE("failed to compile query table: %s",
443                   sqlite3_errstr(sqlite3_extended_errcode(connection->db)));
444             return false;
445         }
446     }
447 
448     // A temporary, to simplify the code.
449     sqlite3_stmt* query = connection->tableQuery;
450     result = sqlite3_bind_text(query, 1, sqlite3_sql(statement), -1, SQLITE_STATIC);
451     if (result != SQLITE_OK) {
452         ALOGE("tables bind pointer returns %s", sqlite3_errstr(result));
453     }
454     result = sqlite3_step(query);
455     // Make sure the query is no longer bound to the statement SQL string and
456     // that is no longer holding any table locks.
457     sqlite3_reset(query);
458     sqlite3_clear_bindings(query);
459 
460     if (result != SQLITE_ROW && result != SQLITE_DONE) {
461         ALOGE("tables query error: %d/%s", result, sqlite3_errstr(result));
462     }
463     return result == SQLITE_DONE;
464 }
465 
nativeGetColumnCount(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)466 static jint nativeGetColumnCount(JNIEnv* env, jclass clazz, jlong connectionPtr,
467         jlong statementPtr) {
468     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
469 
470     return sqlite3_column_count(statement);
471 }
472 
nativeGetColumnName(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index)473 static jstring nativeGetColumnName(JNIEnv* env, jclass clazz, jlong connectionPtr,
474         jlong statementPtr, jint index) {
475     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
476 
477     const jchar* name = static_cast<const jchar*>(sqlite3_column_name16(statement, index));
478     if (name) {
479         size_t length = 0;
480         while (name[length]) {
481             length += 1;
482         }
483         return env->NewString(name, length);
484     }
485     return NULL;
486 }
487 
nativeBindNull(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index)488 static void nativeBindNull(JNIEnv* env, jclass clazz, jlong connectionPtr,
489         jlong statementPtr, jint index) {
490     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
491     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
492 
493     int err = sqlite3_bind_null(statement, index);
494     if (err != SQLITE_OK) {
495         throw_sqlite3_exception(env, connection->db, NULL);
496     }
497 }
498 
nativeBindLong(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index,jlong value)499 static void nativeBindLong(JNIEnv* env, jclass clazz, jlong connectionPtr,
500         jlong statementPtr, jint index, jlong value) {
501     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
502     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
503 
504     int err = sqlite3_bind_int64(statement, index, value);
505     if (err != SQLITE_OK) {
506         throw_sqlite3_exception(env, connection->db, NULL);
507     }
508 }
509 
nativeBindDouble(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index,jdouble value)510 static void nativeBindDouble(JNIEnv* env, jclass clazz, jlong connectionPtr,
511         jlong statementPtr, jint index, jdouble value) {
512     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
513     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
514 
515     int err = sqlite3_bind_double(statement, index, value);
516     if (err != SQLITE_OK) {
517         throw_sqlite3_exception(env, connection->db, NULL);
518     }
519 }
520 
nativeBindString(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index,jstring valueString)521 static void nativeBindString(JNIEnv* env, jclass clazz, jlong connectionPtr,
522         jlong statementPtr, jint index, jstring valueString) {
523     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
524     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
525 
526     jsize valueLength = env->GetStringLength(valueString);
527     const jchar* value = env->GetStringCritical(valueString, NULL);
528     int err = sqlite3_bind_text16(statement, index, value, valueLength * sizeof(jchar),
529             SQLITE_TRANSIENT);
530     env->ReleaseStringCritical(valueString, value);
531     if (err != SQLITE_OK) {
532         throw_sqlite3_exception(env, connection->db, NULL);
533     }
534 }
535 
nativeBindBlob(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index,jbyteArray valueArray)536 static void nativeBindBlob(JNIEnv* env, jclass clazz, jlong connectionPtr,
537         jlong statementPtr, jint index, jbyteArray valueArray) {
538     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
539     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
540 
541     jsize valueLength = env->GetArrayLength(valueArray);
542     jbyte* value = static_cast<jbyte*>(env->GetPrimitiveArrayCritical(valueArray, NULL));
543     int err = sqlite3_bind_blob(statement, index, value, valueLength, SQLITE_TRANSIENT);
544     env->ReleasePrimitiveArrayCritical(valueArray, value, JNI_ABORT);
545     if (err != SQLITE_OK) {
546         throw_sqlite3_exception(env, connection->db, NULL);
547     }
548 }
549 
nativeResetStatementAndClearBindings(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)550 static void nativeResetStatementAndClearBindings(JNIEnv* env, jclass clazz, jlong connectionPtr,
551         jlong statementPtr) {
552     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
553     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
554 
555     int err = sqlite3_reset(statement);
556     if (err == SQLITE_OK) {
557         err = sqlite3_clear_bindings(statement);
558     }
559     if (err != SQLITE_OK) {
560         throw_sqlite3_exception(env, connection->db, NULL);
561     }
562 }
563 
executeNonQuery(JNIEnv * env,SQLiteConnection * connection,sqlite3_stmt * statement,bool isPragmaStmt)564 static int executeNonQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement,
565         bool isPragmaStmt) {
566     int rc = sqlite3_step(statement);
567     if (isPragmaStmt) {
568         while (rc == SQLITE_ROW) {
569             rc = sqlite3_step(statement);
570         }
571     }
572     if (rc == SQLITE_ROW) {
573         throw_sqlite3_exception(env,
574                 "Queries can be performed using SQLiteDatabase query or rawQuery methods only.");
575     } else if (rc != SQLITE_DONE) {
576         throw_sqlite3_exception(env, connection->db);
577     }
578     return rc;
579 }
580 
nativeExecute(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jboolean isPragmaStmt)581 static void nativeExecute(JNIEnv* env, jclass clazz, jlong connectionPtr, jlong statementPtr,
582         jboolean isPragmaStmt) {
583     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
584     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
585 
586     executeNonQuery(env, connection, statement, isPragmaStmt);
587 }
588 
nativeExecuteForChangedRowCount(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)589 static jint nativeExecuteForChangedRowCount(JNIEnv* env, jclass clazz,
590         jlong connectionPtr, jlong statementPtr) {
591     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
592     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
593 
594     int err = executeNonQuery(env, connection, statement, false);
595     return err == SQLITE_DONE ? sqlite3_changes(connection->db) : -1;
596 }
597 
nativeExecuteForLastInsertedRowId(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)598 static jlong nativeExecuteForLastInsertedRowId(JNIEnv* env, jclass clazz,
599         jlong connectionPtr, jlong statementPtr) {
600     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
601     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
602 
603     int err = executeNonQuery(env, connection, statement, false);
604     return err == SQLITE_DONE && sqlite3_changes(connection->db) > 0
605             ? sqlite3_last_insert_rowid(connection->db) : -1;
606 }
607 
executeOneRowQuery(JNIEnv * env,SQLiteConnection * connection,sqlite3_stmt * statement)608 static int executeOneRowQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement) {
609     int err = sqlite3_step(statement);
610     if (err != SQLITE_ROW) {
611         throw_sqlite3_exception(env, connection->db);
612     }
613     return err;
614 }
615 
nativeExecuteForLong(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)616 static jlong nativeExecuteForLong(JNIEnv* env, jclass clazz,
617         jlong connectionPtr, jlong statementPtr) {
618     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
619     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
620 
621     int err = executeOneRowQuery(env, connection, statement);
622     if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
623         return sqlite3_column_int64(statement, 0);
624     }
625     return -1;
626 }
627 
nativeExecuteForString(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)628 static jstring nativeExecuteForString(JNIEnv* env, jclass clazz,
629         jlong connectionPtr, jlong statementPtr) {
630     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
631     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
632 
633     int err = executeOneRowQuery(env, connection, statement);
634     if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
635         const jchar* text = static_cast<const jchar*>(sqlite3_column_text16(statement, 0));
636         if (text) {
637             size_t length = sqlite3_column_bytes16(statement, 0) / sizeof(jchar);
638             return env->NewString(text, length);
639         }
640     }
641     return NULL;
642 }
643 
createAshmemRegionWithData(JNIEnv * env,const void * data,size_t length)644 static int createAshmemRegionWithData(JNIEnv* env, const void* data, size_t length) {
645     int error = 0;
646     int fd = ashmem_create_region(NULL, length);
647     if (fd < 0) {
648         error = errno;
649         ALOGE("ashmem_create_region failed: %s", strerror(error));
650     } else {
651         if (length > 0) {
652             void* ptr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
653             if (ptr == MAP_FAILED) {
654                 error = errno;
655                 ALOGE("mmap failed: %s", strerror(error));
656             } else {
657                 memcpy(ptr, data, length);
658                 munmap(ptr, length);
659             }
660         }
661 
662         if (!error) {
663             if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
664                 error = errno;
665                 ALOGE("ashmem_set_prot_region failed: %s", strerror(errno));
666             } else {
667                 return fd;
668             }
669         }
670 
671         close(fd);
672     }
673 
674     jniThrowIOException(env, error);
675     return -1;
676 }
677 
nativeExecuteForBlobFileDescriptor(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)678 static jint nativeExecuteForBlobFileDescriptor(JNIEnv* env, jclass clazz,
679         jlong connectionPtr, jlong statementPtr) {
680     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
681     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
682 
683     int err = executeOneRowQuery(env, connection, statement);
684     if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
685         const void* blob = sqlite3_column_blob(statement, 0);
686         if (blob) {
687             int length = sqlite3_column_bytes(statement, 0);
688             if (length >= 0) {
689                 return createAshmemRegionWithData(env, blob, length);
690             }
691         }
692     }
693     return -1;
694 }
695 
696 enum CopyRowResult {
697     CPR_OK,
698     CPR_FULL,
699     CPR_ERROR,
700 };
701 
copyRow(JNIEnv * env,CursorWindow * window,sqlite3_stmt * statement,int numColumns,int startPos,int addedRows)702 static CopyRowResult copyRow(JNIEnv* env, CursorWindow* window,
703         sqlite3_stmt* statement, int numColumns, int startPos, int addedRows) {
704     // Allocate a new field directory for the row.
705     status_t status = window->allocRow();
706     if (status) {
707         LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d, error=%d",
708                 startPos, addedRows, status);
709         return CPR_FULL;
710     }
711 
712     // Pack the row into the window.
713     CopyRowResult result = CPR_OK;
714     for (int i = 0; i < numColumns; i++) {
715         int type = sqlite3_column_type(statement, i);
716         if (type == SQLITE_TEXT) {
717             // TEXT data
718             const char* text = reinterpret_cast<const char*>(
719                     sqlite3_column_text(statement, i));
720             // SQLite does not include the NULL terminator in size, but does
721             // ensure all strings are NULL terminated, so increase size by
722             // one to make sure we store the terminator.
723             size_t sizeIncludingNull = sqlite3_column_bytes(statement, i) + 1;
724             status = window->putString(addedRows, i, text, sizeIncludingNull);
725             if (status) {
726                 LOG_WINDOW("Failed allocating %zu bytes for text at %d,%d, error=%d",
727                         sizeIncludingNull, startPos + addedRows, i, status);
728                 result = CPR_FULL;
729                 break;
730             }
731             LOG_WINDOW("%d,%d is TEXT with %zu bytes",
732                     startPos + addedRows, i, sizeIncludingNull);
733         } else if (type == SQLITE_INTEGER) {
734             // INTEGER data
735             int64_t value = sqlite3_column_int64(statement, i);
736             status = window->putLong(addedRows, i, value);
737             if (status) {
738                 LOG_WINDOW("Failed allocating space for a long in column %d, error=%d",
739                         i, status);
740                 result = CPR_FULL;
741                 break;
742             }
743             LOG_WINDOW("%d,%d is INTEGER %" PRId64, startPos + addedRows, i, value);
744         } else if (type == SQLITE_FLOAT) {
745             // FLOAT data
746             double value = sqlite3_column_double(statement, i);
747             status = window->putDouble(addedRows, i, value);
748             if (status) {
749                 LOG_WINDOW("Failed allocating space for a double in column %d, error=%d",
750                         i, status);
751                 result = CPR_FULL;
752                 break;
753             }
754             LOG_WINDOW("%d,%d is FLOAT %lf", startPos + addedRows, i, value);
755         } else if (type == SQLITE_BLOB) {
756             // BLOB data
757             const void* blob = sqlite3_column_blob(statement, i);
758             size_t size = sqlite3_column_bytes(statement, i);
759             status = window->putBlob(addedRows, i, blob, size);
760             if (status) {
761                 LOG_WINDOW("Failed allocating %zu bytes for blob at %d,%d, error=%d",
762                         size, startPos + addedRows, i, status);
763                 result = CPR_FULL;
764                 break;
765             }
766             LOG_WINDOW("%d,%d is Blob with %zu bytes",
767                     startPos + addedRows, i, size);
768         } else if (type == SQLITE_NULL) {
769             // NULL field
770             status = window->putNull(addedRows, i);
771             if (status) {
772                 LOG_WINDOW("Failed allocating space for a null in column %d, error=%d",
773                         i, status);
774                 result = CPR_FULL;
775                 break;
776             }
777 
778             LOG_WINDOW("%d,%d is NULL", startPos + addedRows, i);
779         } else {
780             // Unknown data
781             ALOGE("Unknown column type when filling database window");
782             throw_sqlite3_exception(env, "Unknown column type when filling window");
783             result = CPR_ERROR;
784             break;
785         }
786     }
787 
788     // Free the last row if if was not successfully copied.
789     if (result != CPR_OK) {
790         window->freeLastRow();
791     }
792     return result;
793 }
794 
nativeExecuteForCursorWindow(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jlong windowPtr,jint startPos,jint requiredPos,jboolean countAllRows)795 static jlong nativeExecuteForCursorWindow(JNIEnv* env, jclass clazz,
796         jlong connectionPtr, jlong statementPtr, jlong windowPtr,
797         jint startPos, jint requiredPos, jboolean countAllRows) {
798     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
799     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
800     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
801 
802     status_t status = window->clear();
803     if (status) {
804         String8 msg;
805         msg.appendFormat("Failed to clear the cursor window, status=%d", status);
806         throw_sqlite3_exception(env, connection->db, msg.c_str());
807         return 0;
808     }
809 
810     int numColumns = sqlite3_column_count(statement);
811     status = window->setNumColumns(numColumns);
812     if (status) {
813         String8 msg;
814         msg.appendFormat("Failed to set the cursor window column count to %d, status=%d",
815                 numColumns, status);
816         throw_sqlite3_exception(env, connection->db, msg.c_str());
817         return 0;
818     }
819 
820     int retryCount = 0;
821     int totalRows = 0;
822     int addedRows = 0;
823     bool windowFull = false;
824     bool gotException = false;
825     while (!gotException && (!windowFull || countAllRows)) {
826         int err = sqlite3_step(statement);
827         if (err == SQLITE_ROW) {
828             LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows);
829             retryCount = 0;
830             totalRows += 1;
831 
832             // Skip the row if the window is full or we haven't reached the start position yet.
833             if (startPos >= totalRows || windowFull) {
834                 continue;
835             }
836 
837             CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
838             if (cpr == CPR_FULL && addedRows && startPos + addedRows <= requiredPos) {
839                 // We filled the window before we got to the one row that we really wanted.
840                 // Clear the window and start filling it again from here.
841                 // TODO: Would be nicer if we could progressively replace earlier rows.
842                 window->clear();
843                 window->setNumColumns(numColumns);
844                 startPos += addedRows;
845                 addedRows = 0;
846                 cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
847             }
848 
849             if (cpr == CPR_OK) {
850                 addedRows += 1;
851             } else if (cpr == CPR_FULL) {
852                 windowFull = true;
853             } else {
854                 gotException = true;
855             }
856         } else if (err == SQLITE_DONE) {
857             // All rows processed, bail
858             LOG_WINDOW("Processed all rows");
859             break;
860         } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
861             // The table is locked, retry
862             LOG_WINDOW("Database locked, retrying");
863             if (retryCount > 50) {
864                 ALOGE("Bailing on database busy retry");
865                 throw_sqlite3_exception(env, connection->db, "retrycount exceeded");
866                 gotException = true;
867             } else {
868                 // Sleep to give the thread holding the lock a chance to finish
869                 usleep(1000);
870                 retryCount++;
871             }
872         } else {
873             throw_sqlite3_exception(env, connection->db);
874             gotException = true;
875         }
876     }
877 
878     LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows "
879             "to the window in %zu bytes",
880             statement, totalRows, addedRows, window->size() - window->freeSpace());
881     sqlite3_reset(statement);
882 
883     // Report the total number of rows on request.
884     if (startPos > totalRows) {
885         ALOGE("startPos %d > actual rows %d", startPos, totalRows);
886     }
887     if (totalRows > 0 && addedRows == 0) {
888         String8 msg;
889         msg.appendFormat("Row too big to fit into CursorWindow requiredPos=%d, totalRows=%d",
890                 requiredPos, totalRows);
891         throw_sqlite3_exception(env, SQLITE_TOOBIG, NULL, msg.c_str());
892         return 0;
893     }
894 
895     jlong result = jlong(startPos) << 32 | jlong(totalRows);
896     return result;
897 }
898 
nativeGetDbLookaside(JNIEnv * env,jobject clazz,jlong connectionPtr)899 static jint nativeGetDbLookaside(JNIEnv* env, jobject clazz, jlong connectionPtr) {
900     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
901 
902     int cur = -1;
903     int unused;
904     sqlite3_db_status(connection->db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &unused, 0);
905     return cur;
906 }
907 
nativeCancel(JNIEnv * env,jobject clazz,jlong connectionPtr)908 static void nativeCancel(JNIEnv* env, jobject clazz, jlong connectionPtr) {
909     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
910     connection->canceled = true;
911 }
912 
nativeResetCancel(JNIEnv * env,jobject clazz,jlong connectionPtr,jboolean cancelable)913 static void nativeResetCancel(JNIEnv* env, jobject clazz, jlong connectionPtr,
914         jboolean cancelable) {
915     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
916     connection->canceled = false;
917 
918     if (cancelable) {
919         sqlite3_progress_handler(connection->db, 4, sqliteProgressHandlerCallback,
920                 connection);
921     } else {
922         sqlite3_progress_handler(connection->db, 0, NULL, NULL);
923     }
924 }
925 
nativeLastInsertRowId(JNIEnv * env,jclass,jlong connectionPtr)926 static jint nativeLastInsertRowId(JNIEnv* env, jclass, jlong connectionPtr) {
927     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
928     return sqlite3_last_insert_rowid(connection->db);
929 }
930 
nativeChanges(JNIEnv * env,jclass,jlong connectionPtr)931 static jlong nativeChanges(JNIEnv* env, jclass, jlong connectionPtr) {
932     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
933     return sqlite3_changes64(connection->db);
934 }
935 
nativeTotalChanges(JNIEnv * env,jclass,jlong connectionPtr)936 static jlong nativeTotalChanges(JNIEnv* env, jclass, jlong connectionPtr) {
937     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
938     return sqlite3_total_changes64(connection->db);
939 }
940 
941 static const JNINativeMethod sMethods[] =
942 {
943     /* name, signature, funcPtr */
944     { "nativeOpen", "(Ljava/lang/String;ILjava/lang/String;ZZII)J",
945             (void*)nativeOpen },
946     { "nativeClose", "(J)V",
947             (void*)nativeClose },
948     { "nativeRegisterCustomScalarFunction", "(JLjava/lang/String;Ljava/util/function/UnaryOperator;)V",
949             (void*)nativeRegisterCustomScalarFunction },
950     { "nativeRegisterCustomAggregateFunction", "(JLjava/lang/String;Ljava/util/function/BinaryOperator;)V",
951             (void*)nativeRegisterCustomAggregateFunction },
952     { "nativeRegisterLocalizedCollators", "(JLjava/lang/String;)V",
953             (void*)nativeRegisterLocalizedCollators },
954     { "nativePrepareStatement", "(JLjava/lang/String;)J",
955             (void*)nativePrepareStatement },
956     { "nativeFinalizeStatement", "(JJ)V",
957             (void*)nativeFinalizeStatement },
958     { "nativeGetParameterCount", "(JJ)I",
959             (void*)nativeGetParameterCount },
960     { "nativeIsReadOnly", "(JJ)Z",
961             (void*)nativeIsReadOnly },
962     { "nativeUpdatesTempOnly", "(JJ)Z",
963             (void*)nativeUpdatesTempOnly },
964     { "nativeGetColumnCount", "(JJ)I",
965             (void*)nativeGetColumnCount },
966     { "nativeGetColumnName", "(JJI)Ljava/lang/String;",
967             (void*)nativeGetColumnName },
968     { "nativeBindNull", "(JJI)V",
969             (void*)nativeBindNull },
970     { "nativeBindLong", "(JJIJ)V",
971             (void*)nativeBindLong },
972     { "nativeBindDouble", "(JJID)V",
973             (void*)nativeBindDouble },
974     { "nativeBindString", "(JJILjava/lang/String;)V",
975             (void*)nativeBindString },
976     { "nativeBindBlob", "(JJI[B)V",
977             (void*)nativeBindBlob },
978     { "nativeResetStatementAndClearBindings", "(JJ)V",
979             (void*)nativeResetStatementAndClearBindings },
980     { "nativeExecute", "(JJZ)V",
981             (void*)nativeExecute },
982     { "nativeExecuteForLong", "(JJ)J",
983             (void*)nativeExecuteForLong },
984     { "nativeExecuteForString", "(JJ)Ljava/lang/String;",
985             (void*)nativeExecuteForString },
986     { "nativeExecuteForBlobFileDescriptor", "(JJ)I",
987             (void*)nativeExecuteForBlobFileDescriptor },
988     { "nativeExecuteForChangedRowCount", "(JJ)I",
989             (void*)nativeExecuteForChangedRowCount },
990     { "nativeExecuteForLastInsertedRowId", "(JJ)J",
991             (void*)nativeExecuteForLastInsertedRowId },
992     { "nativeExecuteForCursorWindow", "(JJJIIZ)J",
993             (void*)nativeExecuteForCursorWindow },
994     { "nativeGetDbLookaside", "(J)I",
995             (void*)nativeGetDbLookaside },
996     { "nativeCancel", "(J)V",
997             (void*)nativeCancel },
998     { "nativeResetCancel", "(JZ)V",
999             (void*)nativeResetCancel },
1000 
1001     { "nativeLastInsertRowId", "(J)I", (void*) nativeLastInsertRowId },
1002     { "nativeChanges", "(J)J", (void*) nativeChanges },
1003     { "nativeTotalChanges", "(J)J", (void*) nativeTotalChanges },
1004 };
1005 
register_android_database_SQLiteConnection(JNIEnv * env)1006 int register_android_database_SQLiteConnection(JNIEnv *env)
1007 {
1008     jclass unaryClazz = FindClassOrDie(env, "java/util/function/UnaryOperator");
1009     gUnaryOperator.apply = GetMethodIDOrDie(env, unaryClazz,
1010             "apply", "(Ljava/lang/Object;)Ljava/lang/Object;");
1011 
1012     jclass binaryClazz = FindClassOrDie(env, "java/util/function/BinaryOperator");
1013     gBinaryOperator.apply = GetMethodIDOrDie(env, binaryClazz,
1014             "apply", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
1015 
1016     return RegisterMethodsOrDie(env, "android/database/sqlite/SQLiteConnection", sMethods,
1017                                 NELEM(sMethods));
1018 }
1019 
1020 } // namespace android
1021