|
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/. */ |
|
4 |
|
5 #include <stdlib.h> |
|
6 #include <stdio.h> |
|
7 #include <jni.h> |
|
8 #include <android/log.h> |
|
9 #include "dlfcn.h" |
|
10 #include "APKOpen.h" |
|
11 #include "ElfLoader.h" |
|
12 #include "SQLiteBridge.h" |
|
13 |
|
14 #ifdef DEBUG |
|
15 #define LOG(x...) __android_log_print(ANDROID_LOG_INFO, "GeckoJNI", x) |
|
16 #else |
|
17 #define LOG(x...) |
|
18 #endif |
|
19 |
|
20 #define SQLITE_WRAPPER_INT(name) name ## _t f_ ## name; |
|
21 |
|
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) |
|
38 |
|
39 void setup_sqlite_functions(void *sqlite_handle) |
|
40 { |
|
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); |
|
58 #undef GETFUNC |
|
59 } |
|
60 |
|
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; |
|
69 |
|
70 static jobject sqliteInternalCall(JNIEnv* jenv, sqlite3 *db, jstring jQuery, |
|
71 jobjectArray jParams, jlongArray jQueryRes); |
|
72 |
|
73 static void throwSqliteException(JNIEnv* jenv, const char* aFormat, ...) |
|
74 { |
|
75 va_list ap; |
|
76 va_start(ap, aFormat); |
|
77 char* msg = nullptr; |
|
78 vasprintf(&msg, aFormat, ap); |
|
79 LOG("Error in SQLiteBridge: %s\n", msg); |
|
80 JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException", msg); |
|
81 free(msg); |
|
82 va_end(ap); |
|
83 } |
|
84 |
|
85 static void |
|
86 JNI_Setup(JNIEnv* jenv) |
|
87 { |
|
88 if (initialized) return; |
|
89 |
|
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"); |
|
94 |
|
95 if (lStringClass == nullptr |
|
96 || lObjectClass == nullptr |
|
97 || lByteBufferClass == nullptr |
|
98 || lCursorClass == nullptr) { |
|
99 throwSqliteException(jenv, "FindClass error"); |
|
100 return; |
|
101 } |
|
102 |
|
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); |
|
109 |
|
110 if (stringClass == nullptr || objectClass == nullptr |
|
111 || byteBufferClass == nullptr |
|
112 || cursorClass == nullptr) { |
|
113 throwSqliteException(jenv, "NewGlobalRef error"); |
|
114 return; |
|
115 } |
|
116 |
|
117 // public static ByteBuffer allocateDirect(int capacity) |
|
118 jByteBufferAllocateDirect = |
|
119 jenv->GetStaticMethodID(byteBufferClass, "allocateDirect", "(I)Ljava/nio/ByteBuffer;"); |
|
120 // new MatrixBlobCursor(String []) |
|
121 jCursorConstructor = |
|
122 jenv->GetMethodID(cursorClass, "<init>", "([Ljava/lang/String;)V"); |
|
123 // public void addRow (Object[] columnValues) |
|
124 jCursorAddRow = |
|
125 jenv->GetMethodID(cursorClass, "addRow", "([Ljava/lang/Object;)V"); |
|
126 |
|
127 if (jByteBufferAllocateDirect == nullptr |
|
128 || jCursorConstructor == nullptr |
|
129 || jCursorAddRow == nullptr) { |
|
130 throwSqliteException(jenv, "GetMethodId error"); |
|
131 return; |
|
132 } |
|
133 |
|
134 initialized = true; |
|
135 } |
|
136 |
|
137 extern "C" NS_EXPORT jobject JNICALL |
|
138 Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCall(JNIEnv* jenv, jclass, |
|
139 jstring jDb, |
|
140 jstring jQuery, |
|
141 jobjectArray jParams, |
|
142 jlongArray jQueryRes) |
|
143 { |
|
144 JNI_Setup(jenv); |
|
145 |
|
146 int rc; |
|
147 jobject jCursor = nullptr; |
|
148 const char* dbPath; |
|
149 sqlite3 *db; |
|
150 |
|
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 |
|
158 return nullptr; |
|
159 } |
|
160 jCursor = sqliteInternalCall(jenv, db, jQuery, jParams, jQueryRes); |
|
161 f_sqlite3_close(db); |
|
162 return jCursor; |
|
163 } |
|
164 |
|
165 extern "C" NS_EXPORT jobject JNICALL |
|
166 Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCallWithDb(JNIEnv* jenv, jclass, |
|
167 jlong jDb, |
|
168 jstring jQuery, |
|
169 jobjectArray jParams, |
|
170 jlongArray jQueryRes) |
|
171 { |
|
172 JNI_Setup(jenv); |
|
173 |
|
174 jobject jCursor = nullptr; |
|
175 sqlite3 *db = (sqlite3*)jDb; |
|
176 jCursor = sqliteInternalCall(jenv, db, jQuery, jParams, jQueryRes); |
|
177 return jCursor; |
|
178 } |
|
179 |
|
180 extern "C" NS_EXPORT jlong JNICALL |
|
181 Java_org_mozilla_gecko_sqlite_SQLiteBridge_openDatabase(JNIEnv* jenv, jclass, |
|
182 jstring jDb) |
|
183 { |
|
184 JNI_Setup(jenv); |
|
185 |
|
186 int rc; |
|
187 const char* dbPath; |
|
188 sqlite3 *db; |
|
189 |
|
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 |
|
197 return 0; |
|
198 } |
|
199 return (jlong)db; |
|
200 } |
|
201 |
|
202 extern "C" NS_EXPORT void JNICALL |
|
203 Java_org_mozilla_gecko_sqlite_SQLiteBridge_closeDatabase(JNIEnv* jenv, jclass, |
|
204 jlong jDb) |
|
205 { |
|
206 JNI_Setup(jenv); |
|
207 |
|
208 sqlite3 *db = (sqlite3*)jDb; |
|
209 f_sqlite3_close(db); |
|
210 } |
|
211 |
|
212 static jobject |
|
213 sqliteInternalCall(JNIEnv* jenv, |
|
214 sqlite3 *db, |
|
215 jstring jQuery, |
|
216 jobjectArray jParams, |
|
217 jlongArray jQueryRes) |
|
218 { |
|
219 JNI_Setup(jenv); |
|
220 |
|
221 jobject jCursor = nullptr; |
|
222 jsize numPars = 0; |
|
223 |
|
224 const char *pzTail; |
|
225 sqlite3_stmt *ppStmt; |
|
226 int rc; |
|
227 |
|
228 const char* queryStr; |
|
229 queryStr = jenv->GetStringUTFChars(jQuery, nullptr); |
|
230 |
|
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)); |
|
235 return nullptr; |
|
236 } |
|
237 jenv->ReleaseStringUTFChars(jQuery, queryStr); |
|
238 |
|
239 // Check if number of parameters matches |
|
240 if (jParams != nullptr) { |
|
241 numPars = jenv->GetArrayLength(jParams); |
|
242 } |
|
243 int sqlNumPars; |
|
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); |
|
250 return nullptr; |
|
251 } |
|
252 |
|
253 if (jParams != nullptr) { |
|
254 // Bind parameters, if any |
|
255 if (numPars > 0) { |
|
256 for (int i = 0; i < numPars; i++) { |
|
257 jobject jObjectParam = jenv->GetObjectArrayElement(jParams, i); |
|
258 // IsInstanceOf or isAssignableFrom? String is final, so IsInstanceOf |
|
259 // should be OK. |
|
260 jboolean isString = jenv->IsInstanceOf(jObjectParam, stringClass); |
|
261 if (isString != JNI_TRUE) { |
|
262 throwSqliteException(jenv, |
|
263 "Parameter is not of String type"); |
|
264 return nullptr; |
|
265 } |
|
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"); |
|
273 return nullptr; |
|
274 } |
|
275 } |
|
276 } |
|
277 } |
|
278 |
|
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)); |
|
284 return nullptr; |
|
285 } |
|
286 |
|
287 // Get the column count and names |
|
288 int cols; |
|
289 cols = f_sqlite3_column_count(ppStmt); |
|
290 |
|
291 { |
|
292 // Allocate a String[cols] |
|
293 jobjectArray jStringArray = jenv->NewObjectArray(cols, |
|
294 stringClass, |
|
295 nullptr); |
|
296 if (jStringArray == nullptr) { |
|
297 throwSqliteException(jenv, "Can't allocate String[]"); |
|
298 return nullptr; |
|
299 } |
|
300 |
|
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); |
|
306 } |
|
307 |
|
308 // Construct the MatrixCursor(String[]) with given column names |
|
309 jCursor = jenv->NewObject(cursorClass, |
|
310 jCursorConstructor, |
|
311 jStringArray); |
|
312 if (jCursor == nullptr) { |
|
313 throwSqliteException(jenv, "Can't allocate MatrixBlobCursor"); |
|
314 return nullptr; |
|
315 } |
|
316 } |
|
317 |
|
318 // Return the id and number of changed rows in jQueryRes |
|
319 { |
|
320 jlong id = f_sqlite3_last_insert_rowid(db); |
|
321 jenv->SetLongArrayRegion(jQueryRes, 0, 1, &id); |
|
322 |
|
323 jlong changed = f_sqlite3_changes(db); |
|
324 jenv->SetLongArrayRegion(jQueryRes, 1, 1, &changed); |
|
325 } |
|
326 |
|
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) { |
|
331 // Process row |
|
332 // Construct Object[] |
|
333 jobjectArray jRow = jenv->NewObjectArray(cols, |
|
334 objectClass, |
|
335 nullptr); |
|
336 if (jRow == nullptr) { |
|
337 throwSqliteException(jenv, "Can't allocate jRow Object[]"); |
|
338 return nullptr; |
|
339 } |
|
340 |
|
341 for (int i = 0; i < cols; i++) { |
|
342 int colType = f_sqlite3_column_type(ppStmt, i); |
|
343 if (colType == SQLITE_BLOB) { |
|
344 // Treat as blob |
|
345 const void* blob = f_sqlite3_column_blob(ppStmt, i); |
|
346 int colLen = f_sqlite3_column_bytes(ppStmt, i); |
|
347 |
|
348 // Construct ByteBuffer of correct size |
|
349 jobject jByteBuffer = |
|
350 jenv->CallStaticObjectMethod(byteBufferClass, |
|
351 jByteBufferAllocateDirect, |
|
352 colLen); |
|
353 if (jByteBuffer == nullptr) { |
|
354 throwSqliteException(jenv, |
|
355 "Failure calling ByteBuffer.allocateDirect"); |
|
356 return nullptr; |
|
357 } |
|
358 |
|
359 // Get its backing array |
|
360 void* bufferArray = jenv->GetDirectBufferAddress(jByteBuffer); |
|
361 if (bufferArray == nullptr) { |
|
362 throwSqliteException(jenv, |
|
363 "Failure calling GetDirectBufferAddress"); |
|
364 return nullptr; |
|
365 } |
|
366 memcpy(bufferArray, blob, colLen); |
|
367 |
|
368 jenv->SetObjectArrayElement(jRow, i, jByteBuffer); |
|
369 jenv->DeleteLocalRef(jByteBuffer); |
|
370 } else if (colType == SQLITE_NULL) { |
|
371 jenv->SetObjectArrayElement(jRow, i, nullptr); |
|
372 } else { |
|
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); |
|
378 } |
|
379 } |
|
380 |
|
381 // Append Object[] to Cursor |
|
382 jenv->CallVoidMethod(jCursor, jCursorAddRow, jRow); |
|
383 |
|
384 // Clean up |
|
385 jenv->DeleteLocalRef(jRow); |
|
386 |
|
387 // Get next row |
|
388 rc = f_sqlite3_step(ppStmt); |
|
389 // Real error? |
|
390 if (rc != SQLITE_ROW && rc != SQLITE_DONE) { |
|
391 throwSqliteException(jenv, |
|
392 "Can't re-step statement:(%d) %s", rc, f_sqlite3_errmsg(db)); |
|
393 return nullptr; |
|
394 } |
|
395 } |
|
396 |
|
397 rc = f_sqlite3_finalize(ppStmt); |
|
398 if (rc != SQLITE_OK) { |
|
399 throwSqliteException(jenv, |
|
400 "Can't finalize statement: %s", f_sqlite3_errmsg(db)); |
|
401 return nullptr; |
|
402 } |
|
403 |
|
404 return jCursor; |
|
405 } |