1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include <android/log.h>
11 #include "ElfLoader.h"
12 #include "SQLiteBridge.h"
15 #define LOG(x...) __android_log_print(ANDROID_LOG_INFO, "GeckoJNI", x)
20 #define SQLITE_WRAPPER_INT(name) name ## _t f_ ## name;
22 SQLITE_WRAPPER_INT(sqlite3_open
)
23 SQLITE_WRAPPER_INT(sqlite3_errmsg
)
24 SQLITE_WRAPPER_INT(sqlite3_prepare_v2
)
25 SQLITE_WRAPPER_INT(sqlite3_bind_parameter_count
)
26 SQLITE_WRAPPER_INT(sqlite3_bind_text
)
27 SQLITE_WRAPPER_INT(sqlite3_step
)
28 SQLITE_WRAPPER_INT(sqlite3_column_count
)
29 SQLITE_WRAPPER_INT(sqlite3_finalize
)
30 SQLITE_WRAPPER_INT(sqlite3_close
)
31 SQLITE_WRAPPER_INT(sqlite3_column_name
)
32 SQLITE_WRAPPER_INT(sqlite3_column_type
)
33 SQLITE_WRAPPER_INT(sqlite3_column_blob
)
34 SQLITE_WRAPPER_INT(sqlite3_column_bytes
)
35 SQLITE_WRAPPER_INT(sqlite3_column_text
)
36 SQLITE_WRAPPER_INT(sqlite3_changes
)
37 SQLITE_WRAPPER_INT(sqlite3_last_insert_rowid
)
39 void setup_sqlite_functions(void *sqlite_handle
)
41 #define GETFUNC(name) f_ ## name = (name ## _t) (uintptr_t) __wrap_dlsym(sqlite_handle, #name)
42 GETFUNC(sqlite3_open
);
43 GETFUNC(sqlite3_errmsg
);
44 GETFUNC(sqlite3_prepare_v2
);
45 GETFUNC(sqlite3_bind_parameter_count
);
46 GETFUNC(sqlite3_bind_text
);
47 GETFUNC(sqlite3_step
);
48 GETFUNC(sqlite3_column_count
);
49 GETFUNC(sqlite3_finalize
);
50 GETFUNC(sqlite3_close
);
51 GETFUNC(sqlite3_column_name
);
52 GETFUNC(sqlite3_column_type
);
53 GETFUNC(sqlite3_column_blob
);
54 GETFUNC(sqlite3_column_bytes
);
55 GETFUNC(sqlite3_column_text
);
56 GETFUNC(sqlite3_changes
);
57 GETFUNC(sqlite3_last_insert_rowid
);
61 static bool initialized
= false;
62 static jclass stringClass
;
63 static jclass objectClass
;
64 static jclass byteBufferClass
;
65 static jclass cursorClass
;
66 static jmethodID jByteBufferAllocateDirect
;
67 static jmethodID jCursorConstructor
;
68 static jmethodID jCursorAddRow
;
70 static jobject
sqliteInternalCall(JNIEnv
* jenv
, sqlite3
*db
, jstring jQuery
,
71 jobjectArray jParams
, jlongArray jQueryRes
);
73 static void throwSqliteException(JNIEnv
* jenv
, const char* aFormat
, ...)
76 va_start(ap
, aFormat
);
78 vasprintf(&msg
, aFormat
, ap
);
79 LOG("Error in SQLiteBridge: %s\n", msg
);
80 JNI_Throw(jenv
, "org/mozilla/gecko/sqlite/SQLiteBridgeException", msg
);
86 JNI_Setup(JNIEnv
* jenv
)
88 if (initialized
) return;
90 jclass lObjectClass
= jenv
->FindClass("java/lang/Object");
91 jclass lStringClass
= jenv
->FindClass("java/lang/String");
92 jclass lByteBufferClass
= jenv
->FindClass("java/nio/ByteBuffer");
93 jclass lCursorClass
= jenv
->FindClass("org/mozilla/gecko/sqlite/MatrixBlobCursor");
95 if (lStringClass
== nullptr
96 || lObjectClass
== nullptr
97 || lByteBufferClass
== nullptr
98 || lCursorClass
== nullptr) {
99 throwSqliteException(jenv
, "FindClass error");
103 // Those are only local references. Make them global so they work
104 // across calls and threads.
105 objectClass
= (jclass
)jenv
->NewGlobalRef(lObjectClass
);
106 stringClass
= (jclass
)jenv
->NewGlobalRef(lStringClass
);
107 byteBufferClass
= (jclass
)jenv
->NewGlobalRef(lByteBufferClass
);
108 cursorClass
= (jclass
)jenv
->NewGlobalRef(lCursorClass
);
110 if (stringClass
== nullptr || objectClass
== nullptr
111 || byteBufferClass
== nullptr
112 || cursorClass
== nullptr) {
113 throwSqliteException(jenv
, "NewGlobalRef error");
117 // public static ByteBuffer allocateDirect(int capacity)
118 jByteBufferAllocateDirect
=
119 jenv
->GetStaticMethodID(byteBufferClass
, "allocateDirect", "(I)Ljava/nio/ByteBuffer;");
120 // new MatrixBlobCursor(String [])
122 jenv
->GetMethodID(cursorClass
, "<init>", "([Ljava/lang/String;)V");
123 // public void addRow (Object[] columnValues)
125 jenv
->GetMethodID(cursorClass
, "addRow", "([Ljava/lang/Object;)V");
127 if (jByteBufferAllocateDirect
== nullptr
128 || jCursorConstructor
== nullptr
129 || jCursorAddRow
== nullptr) {
130 throwSqliteException(jenv
, "GetMethodId error");
137 extern "C" NS_EXPORT jobject JNICALL
138 Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCall(JNIEnv
* jenv
, jclass
,
141 jobjectArray jParams
,
142 jlongArray jQueryRes
)
147 jobject jCursor
= nullptr;
151 dbPath
= jenv
->GetStringUTFChars(jDb
, nullptr);
152 rc
= f_sqlite3_open(dbPath
, &db
);
153 jenv
->ReleaseStringUTFChars(jDb
, dbPath
);
154 if (rc
!= SQLITE_OK
) {
155 throwSqliteException(jenv
,
156 "Can't open database: %s", f_sqlite3_errmsg(db
));
157 f_sqlite3_close(db
); // close db even if open failed
160 jCursor
= sqliteInternalCall(jenv
, db
, jQuery
, jParams
, jQueryRes
);
165 extern "C" NS_EXPORT jobject JNICALL
166 Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCallWithDb(JNIEnv
* jenv
, jclass
,
169 jobjectArray jParams
,
170 jlongArray jQueryRes
)
174 jobject jCursor
= nullptr;
175 sqlite3
*db
= (sqlite3
*)jDb
;
176 jCursor
= sqliteInternalCall(jenv
, db
, jQuery
, jParams
, jQueryRes
);
180 extern "C" NS_EXPORT jlong JNICALL
181 Java_org_mozilla_gecko_sqlite_SQLiteBridge_openDatabase(JNIEnv
* jenv
, jclass
,
190 dbPath
= jenv
->GetStringUTFChars(jDb
, nullptr);
191 rc
= f_sqlite3_open(dbPath
, &db
);
192 jenv
->ReleaseStringUTFChars(jDb
, dbPath
);
193 if (rc
!= SQLITE_OK
) {
194 throwSqliteException(jenv
,
195 "Can't open database: %s", f_sqlite3_errmsg(db
));
196 f_sqlite3_close(db
); // close db even if open failed
202 extern "C" NS_EXPORT
void JNICALL
203 Java_org_mozilla_gecko_sqlite_SQLiteBridge_closeDatabase(JNIEnv
* jenv
, jclass
,
208 sqlite3
*db
= (sqlite3
*)jDb
;
213 sqliteInternalCall(JNIEnv
* jenv
,
216 jobjectArray jParams
,
217 jlongArray jQueryRes
)
221 jobject jCursor
= nullptr;
225 sqlite3_stmt
*ppStmt
;
228 const char* queryStr
;
229 queryStr
= jenv
->GetStringUTFChars(jQuery
, nullptr);
231 rc
= f_sqlite3_prepare_v2(db
, queryStr
, -1, &ppStmt
, &pzTail
);
232 if (rc
!= SQLITE_OK
|| ppStmt
== nullptr) {
233 throwSqliteException(jenv
,
234 "Can't prepare statement: %s", f_sqlite3_errmsg(db
));
237 jenv
->ReleaseStringUTFChars(jQuery
, queryStr
);
239 // Check if number of parameters matches
240 if (jParams
!= nullptr) {
241 numPars
= jenv
->GetArrayLength(jParams
);
244 sqlNumPars
= f_sqlite3_bind_parameter_count(ppStmt
);
245 if (numPars
!= sqlNumPars
) {
246 throwSqliteException(jenv
,
247 "Passed parameter count (%d) "
248 "doesn't match SQL parameter count (%d)",
249 numPars
, sqlNumPars
);
253 if (jParams
!= nullptr) {
254 // Bind parameters, if any
256 for (int i
= 0; i
< numPars
; i
++) {
257 jobject jObjectParam
= jenv
->GetObjectArrayElement(jParams
, i
);
258 // IsInstanceOf or isAssignableFrom? String is final, so IsInstanceOf
260 jboolean isString
= jenv
->IsInstanceOf(jObjectParam
, stringClass
);
261 if (isString
!= JNI_TRUE
) {
262 throwSqliteException(jenv
,
263 "Parameter is not of String type");
266 jstring jStringParam
= (jstring
)jObjectParam
;
267 const char* paramStr
= jenv
->GetStringUTFChars(jStringParam
, nullptr);
268 // SQLite parameters index from 1.
269 rc
= f_sqlite3_bind_text(ppStmt
, i
+ 1, paramStr
, -1, SQLITE_TRANSIENT
);
270 jenv
->ReleaseStringUTFChars(jStringParam
, paramStr
);
271 if (rc
!= SQLITE_OK
) {
272 throwSqliteException(jenv
, "Error binding query parameter");
279 // Execute the query and step through the results
280 rc
= f_sqlite3_step(ppStmt
);
281 if (rc
!= SQLITE_ROW
&& rc
!= SQLITE_DONE
) {
282 throwSqliteException(jenv
,
283 "Can't step statement: (%d) %s", rc
, f_sqlite3_errmsg(db
));
287 // Get the column count and names
289 cols
= f_sqlite3_column_count(ppStmt
);
292 // Allocate a String[cols]
293 jobjectArray jStringArray
= jenv
->NewObjectArray(cols
,
296 if (jStringArray
== nullptr) {
297 throwSqliteException(jenv
, "Can't allocate String[]");
301 // Assign column names to the String[]
302 for (int i
= 0; i
< cols
; i
++) {
303 const char* colName
= f_sqlite3_column_name(ppStmt
, i
);
304 jstring jStr
= jenv
->NewStringUTF(colName
);
305 jenv
->SetObjectArrayElement(jStringArray
, i
, jStr
);
308 // Construct the MatrixCursor(String[]) with given column names
309 jCursor
= jenv
->NewObject(cursorClass
,
312 if (jCursor
== nullptr) {
313 throwSqliteException(jenv
, "Can't allocate MatrixBlobCursor");
318 // Return the id and number of changed rows in jQueryRes
320 jlong id
= f_sqlite3_last_insert_rowid(db
);
321 jenv
->SetLongArrayRegion(jQueryRes
, 0, 1, &id
);
323 jlong changed
= f_sqlite3_changes(db
);
324 jenv
->SetLongArrayRegion(jQueryRes
, 1, 1, &changed
);
327 // For each row, add an Object[] to the passed ArrayList,
328 // with that containing either String or ByteArray objects
329 // containing the columns
330 while (rc
!= SQLITE_DONE
) {
332 // Construct Object[]
333 jobjectArray jRow
= jenv
->NewObjectArray(cols
,
336 if (jRow
== nullptr) {
337 throwSqliteException(jenv
, "Can't allocate jRow Object[]");
341 for (int i
= 0; i
< cols
; i
++) {
342 int colType
= f_sqlite3_column_type(ppStmt
, i
);
343 if (colType
== SQLITE_BLOB
) {
345 const void* blob
= f_sqlite3_column_blob(ppStmt
, i
);
346 int colLen
= f_sqlite3_column_bytes(ppStmt
, i
);
348 // Construct ByteBuffer of correct size
349 jobject jByteBuffer
=
350 jenv
->CallStaticObjectMethod(byteBufferClass
,
351 jByteBufferAllocateDirect
,
353 if (jByteBuffer
== nullptr) {
354 throwSqliteException(jenv
,
355 "Failure calling ByteBuffer.allocateDirect");
359 // Get its backing array
360 void* bufferArray
= jenv
->GetDirectBufferAddress(jByteBuffer
);
361 if (bufferArray
== nullptr) {
362 throwSqliteException(jenv
,
363 "Failure calling GetDirectBufferAddress");
366 memcpy(bufferArray
, blob
, colLen
);
368 jenv
->SetObjectArrayElement(jRow
, i
, jByteBuffer
);
369 jenv
->DeleteLocalRef(jByteBuffer
);
370 } else if (colType
== SQLITE_NULL
) {
371 jenv
->SetObjectArrayElement(jRow
, i
, nullptr);
373 // Treat everything else as text
374 const char* txt
= (const char*)f_sqlite3_column_text(ppStmt
, i
);
375 jstring jStr
= jenv
->NewStringUTF(txt
);
376 jenv
->SetObjectArrayElement(jRow
, i
, jStr
);
377 jenv
->DeleteLocalRef(jStr
);
381 // Append Object[] to Cursor
382 jenv
->CallVoidMethod(jCursor
, jCursorAddRow
, jRow
);
385 jenv
->DeleteLocalRef(jRow
);
388 rc
= f_sqlite3_step(ppStmt
);
390 if (rc
!= SQLITE_ROW
&& rc
!= SQLITE_DONE
) {
391 throwSqliteException(jenv
,
392 "Can't re-step statement:(%d) %s", rc
, f_sqlite3_errmsg(db
));
397 rc
= f_sqlite3_finalize(ppStmt
);
398 if (rc
!= SQLITE_OK
) {
399 throwSqliteException(jenv
,
400 "Can't finalize statement: %s", f_sqlite3_errmsg(db
));