mozglue/android/SQLiteBridge.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include <stdlib.h>
michael@0 6 #include <stdio.h>
michael@0 7 #include <jni.h>
michael@0 8 #include <android/log.h>
michael@0 9 #include "dlfcn.h"
michael@0 10 #include "APKOpen.h"
michael@0 11 #include "ElfLoader.h"
michael@0 12 #include "SQLiteBridge.h"
michael@0 13
michael@0 14 #ifdef DEBUG
michael@0 15 #define LOG(x...) __android_log_print(ANDROID_LOG_INFO, "GeckoJNI", x)
michael@0 16 #else
michael@0 17 #define LOG(x...)
michael@0 18 #endif
michael@0 19
michael@0 20 #define SQLITE_WRAPPER_INT(name) name ## _t f_ ## name;
michael@0 21
michael@0 22 SQLITE_WRAPPER_INT(sqlite3_open)
michael@0 23 SQLITE_WRAPPER_INT(sqlite3_errmsg)
michael@0 24 SQLITE_WRAPPER_INT(sqlite3_prepare_v2)
michael@0 25 SQLITE_WRAPPER_INT(sqlite3_bind_parameter_count)
michael@0 26 SQLITE_WRAPPER_INT(sqlite3_bind_text)
michael@0 27 SQLITE_WRAPPER_INT(sqlite3_step)
michael@0 28 SQLITE_WRAPPER_INT(sqlite3_column_count)
michael@0 29 SQLITE_WRAPPER_INT(sqlite3_finalize)
michael@0 30 SQLITE_WRAPPER_INT(sqlite3_close)
michael@0 31 SQLITE_WRAPPER_INT(sqlite3_column_name)
michael@0 32 SQLITE_WRAPPER_INT(sqlite3_column_type)
michael@0 33 SQLITE_WRAPPER_INT(sqlite3_column_blob)
michael@0 34 SQLITE_WRAPPER_INT(sqlite3_column_bytes)
michael@0 35 SQLITE_WRAPPER_INT(sqlite3_column_text)
michael@0 36 SQLITE_WRAPPER_INT(sqlite3_changes)
michael@0 37 SQLITE_WRAPPER_INT(sqlite3_last_insert_rowid)
michael@0 38
michael@0 39 void setup_sqlite_functions(void *sqlite_handle)
michael@0 40 {
michael@0 41 #define GETFUNC(name) f_ ## name = (name ## _t) (uintptr_t) __wrap_dlsym(sqlite_handle, #name)
michael@0 42 GETFUNC(sqlite3_open);
michael@0 43 GETFUNC(sqlite3_errmsg);
michael@0 44 GETFUNC(sqlite3_prepare_v2);
michael@0 45 GETFUNC(sqlite3_bind_parameter_count);
michael@0 46 GETFUNC(sqlite3_bind_text);
michael@0 47 GETFUNC(sqlite3_step);
michael@0 48 GETFUNC(sqlite3_column_count);
michael@0 49 GETFUNC(sqlite3_finalize);
michael@0 50 GETFUNC(sqlite3_close);
michael@0 51 GETFUNC(sqlite3_column_name);
michael@0 52 GETFUNC(sqlite3_column_type);
michael@0 53 GETFUNC(sqlite3_column_blob);
michael@0 54 GETFUNC(sqlite3_column_bytes);
michael@0 55 GETFUNC(sqlite3_column_text);
michael@0 56 GETFUNC(sqlite3_changes);
michael@0 57 GETFUNC(sqlite3_last_insert_rowid);
michael@0 58 #undef GETFUNC
michael@0 59 }
michael@0 60
michael@0 61 static bool initialized = false;
michael@0 62 static jclass stringClass;
michael@0 63 static jclass objectClass;
michael@0 64 static jclass byteBufferClass;
michael@0 65 static jclass cursorClass;
michael@0 66 static jmethodID jByteBufferAllocateDirect;
michael@0 67 static jmethodID jCursorConstructor;
michael@0 68 static jmethodID jCursorAddRow;
michael@0 69
michael@0 70 static jobject sqliteInternalCall(JNIEnv* jenv, sqlite3 *db, jstring jQuery,
michael@0 71 jobjectArray jParams, jlongArray jQueryRes);
michael@0 72
michael@0 73 static void throwSqliteException(JNIEnv* jenv, const char* aFormat, ...)
michael@0 74 {
michael@0 75 va_list ap;
michael@0 76 va_start(ap, aFormat);
michael@0 77 char* msg = nullptr;
michael@0 78 vasprintf(&msg, aFormat, ap);
michael@0 79 LOG("Error in SQLiteBridge: %s\n", msg);
michael@0 80 JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException", msg);
michael@0 81 free(msg);
michael@0 82 va_end(ap);
michael@0 83 }
michael@0 84
michael@0 85 static void
michael@0 86 JNI_Setup(JNIEnv* jenv)
michael@0 87 {
michael@0 88 if (initialized) return;
michael@0 89
michael@0 90 jclass lObjectClass = jenv->FindClass("java/lang/Object");
michael@0 91 jclass lStringClass = jenv->FindClass("java/lang/String");
michael@0 92 jclass lByteBufferClass = jenv->FindClass("java/nio/ByteBuffer");
michael@0 93 jclass lCursorClass = jenv->FindClass("org/mozilla/gecko/sqlite/MatrixBlobCursor");
michael@0 94
michael@0 95 if (lStringClass == nullptr
michael@0 96 || lObjectClass == nullptr
michael@0 97 || lByteBufferClass == nullptr
michael@0 98 || lCursorClass == nullptr) {
michael@0 99 throwSqliteException(jenv, "FindClass error");
michael@0 100 return;
michael@0 101 }
michael@0 102
michael@0 103 // Those are only local references. Make them global so they work
michael@0 104 // across calls and threads.
michael@0 105 objectClass = (jclass)jenv->NewGlobalRef(lObjectClass);
michael@0 106 stringClass = (jclass)jenv->NewGlobalRef(lStringClass);
michael@0 107 byteBufferClass = (jclass)jenv->NewGlobalRef(lByteBufferClass);
michael@0 108 cursorClass = (jclass)jenv->NewGlobalRef(lCursorClass);
michael@0 109
michael@0 110 if (stringClass == nullptr || objectClass == nullptr
michael@0 111 || byteBufferClass == nullptr
michael@0 112 || cursorClass == nullptr) {
michael@0 113 throwSqliteException(jenv, "NewGlobalRef error");
michael@0 114 return;
michael@0 115 }
michael@0 116
michael@0 117 // public static ByteBuffer allocateDirect(int capacity)
michael@0 118 jByteBufferAllocateDirect =
michael@0 119 jenv->GetStaticMethodID(byteBufferClass, "allocateDirect", "(I)Ljava/nio/ByteBuffer;");
michael@0 120 // new MatrixBlobCursor(String [])
michael@0 121 jCursorConstructor =
michael@0 122 jenv->GetMethodID(cursorClass, "<init>", "([Ljava/lang/String;)V");
michael@0 123 // public void addRow (Object[] columnValues)
michael@0 124 jCursorAddRow =
michael@0 125 jenv->GetMethodID(cursorClass, "addRow", "([Ljava/lang/Object;)V");
michael@0 126
michael@0 127 if (jByteBufferAllocateDirect == nullptr
michael@0 128 || jCursorConstructor == nullptr
michael@0 129 || jCursorAddRow == nullptr) {
michael@0 130 throwSqliteException(jenv, "GetMethodId error");
michael@0 131 return;
michael@0 132 }
michael@0 133
michael@0 134 initialized = true;
michael@0 135 }
michael@0 136
michael@0 137 extern "C" NS_EXPORT jobject JNICALL
michael@0 138 Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCall(JNIEnv* jenv, jclass,
michael@0 139 jstring jDb,
michael@0 140 jstring jQuery,
michael@0 141 jobjectArray jParams,
michael@0 142 jlongArray jQueryRes)
michael@0 143 {
michael@0 144 JNI_Setup(jenv);
michael@0 145
michael@0 146 int rc;
michael@0 147 jobject jCursor = nullptr;
michael@0 148 const char* dbPath;
michael@0 149 sqlite3 *db;
michael@0 150
michael@0 151 dbPath = jenv->GetStringUTFChars(jDb, nullptr);
michael@0 152 rc = f_sqlite3_open(dbPath, &db);
michael@0 153 jenv->ReleaseStringUTFChars(jDb, dbPath);
michael@0 154 if (rc != SQLITE_OK) {
michael@0 155 throwSqliteException(jenv,
michael@0 156 "Can't open database: %s", f_sqlite3_errmsg(db));
michael@0 157 f_sqlite3_close(db); // close db even if open failed
michael@0 158 return nullptr;
michael@0 159 }
michael@0 160 jCursor = sqliteInternalCall(jenv, db, jQuery, jParams, jQueryRes);
michael@0 161 f_sqlite3_close(db);
michael@0 162 return jCursor;
michael@0 163 }
michael@0 164
michael@0 165 extern "C" NS_EXPORT jobject JNICALL
michael@0 166 Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCallWithDb(JNIEnv* jenv, jclass,
michael@0 167 jlong jDb,
michael@0 168 jstring jQuery,
michael@0 169 jobjectArray jParams,
michael@0 170 jlongArray jQueryRes)
michael@0 171 {
michael@0 172 JNI_Setup(jenv);
michael@0 173
michael@0 174 jobject jCursor = nullptr;
michael@0 175 sqlite3 *db = (sqlite3*)jDb;
michael@0 176 jCursor = sqliteInternalCall(jenv, db, jQuery, jParams, jQueryRes);
michael@0 177 return jCursor;
michael@0 178 }
michael@0 179
michael@0 180 extern "C" NS_EXPORT jlong JNICALL
michael@0 181 Java_org_mozilla_gecko_sqlite_SQLiteBridge_openDatabase(JNIEnv* jenv, jclass,
michael@0 182 jstring jDb)
michael@0 183 {
michael@0 184 JNI_Setup(jenv);
michael@0 185
michael@0 186 int rc;
michael@0 187 const char* dbPath;
michael@0 188 sqlite3 *db;
michael@0 189
michael@0 190 dbPath = jenv->GetStringUTFChars(jDb, nullptr);
michael@0 191 rc = f_sqlite3_open(dbPath, &db);
michael@0 192 jenv->ReleaseStringUTFChars(jDb, dbPath);
michael@0 193 if (rc != SQLITE_OK) {
michael@0 194 throwSqliteException(jenv,
michael@0 195 "Can't open database: %s", f_sqlite3_errmsg(db));
michael@0 196 f_sqlite3_close(db); // close db even if open failed
michael@0 197 return 0;
michael@0 198 }
michael@0 199 return (jlong)db;
michael@0 200 }
michael@0 201
michael@0 202 extern "C" NS_EXPORT void JNICALL
michael@0 203 Java_org_mozilla_gecko_sqlite_SQLiteBridge_closeDatabase(JNIEnv* jenv, jclass,
michael@0 204 jlong jDb)
michael@0 205 {
michael@0 206 JNI_Setup(jenv);
michael@0 207
michael@0 208 sqlite3 *db = (sqlite3*)jDb;
michael@0 209 f_sqlite3_close(db);
michael@0 210 }
michael@0 211
michael@0 212 static jobject
michael@0 213 sqliteInternalCall(JNIEnv* jenv,
michael@0 214 sqlite3 *db,
michael@0 215 jstring jQuery,
michael@0 216 jobjectArray jParams,
michael@0 217 jlongArray jQueryRes)
michael@0 218 {
michael@0 219 JNI_Setup(jenv);
michael@0 220
michael@0 221 jobject jCursor = nullptr;
michael@0 222 jsize numPars = 0;
michael@0 223
michael@0 224 const char *pzTail;
michael@0 225 sqlite3_stmt *ppStmt;
michael@0 226 int rc;
michael@0 227
michael@0 228 const char* queryStr;
michael@0 229 queryStr = jenv->GetStringUTFChars(jQuery, nullptr);
michael@0 230
michael@0 231 rc = f_sqlite3_prepare_v2(db, queryStr, -1, &ppStmt, &pzTail);
michael@0 232 if (rc != SQLITE_OK || ppStmt == nullptr) {
michael@0 233 throwSqliteException(jenv,
michael@0 234 "Can't prepare statement: %s", f_sqlite3_errmsg(db));
michael@0 235 return nullptr;
michael@0 236 }
michael@0 237 jenv->ReleaseStringUTFChars(jQuery, queryStr);
michael@0 238
michael@0 239 // Check if number of parameters matches
michael@0 240 if (jParams != nullptr) {
michael@0 241 numPars = jenv->GetArrayLength(jParams);
michael@0 242 }
michael@0 243 int sqlNumPars;
michael@0 244 sqlNumPars = f_sqlite3_bind_parameter_count(ppStmt);
michael@0 245 if (numPars != sqlNumPars) {
michael@0 246 throwSqliteException(jenv,
michael@0 247 "Passed parameter count (%d) "
michael@0 248 "doesn't match SQL parameter count (%d)",
michael@0 249 numPars, sqlNumPars);
michael@0 250 return nullptr;
michael@0 251 }
michael@0 252
michael@0 253 if (jParams != nullptr) {
michael@0 254 // Bind parameters, if any
michael@0 255 if (numPars > 0) {
michael@0 256 for (int i = 0; i < numPars; i++) {
michael@0 257 jobject jObjectParam = jenv->GetObjectArrayElement(jParams, i);
michael@0 258 // IsInstanceOf or isAssignableFrom? String is final, so IsInstanceOf
michael@0 259 // should be OK.
michael@0 260 jboolean isString = jenv->IsInstanceOf(jObjectParam, stringClass);
michael@0 261 if (isString != JNI_TRUE) {
michael@0 262 throwSqliteException(jenv,
michael@0 263 "Parameter is not of String type");
michael@0 264 return nullptr;
michael@0 265 }
michael@0 266 jstring jStringParam = (jstring)jObjectParam;
michael@0 267 const char* paramStr = jenv->GetStringUTFChars(jStringParam, nullptr);
michael@0 268 // SQLite parameters index from 1.
michael@0 269 rc = f_sqlite3_bind_text(ppStmt, i + 1, paramStr, -1, SQLITE_TRANSIENT);
michael@0 270 jenv->ReleaseStringUTFChars(jStringParam, paramStr);
michael@0 271 if (rc != SQLITE_OK) {
michael@0 272 throwSqliteException(jenv, "Error binding query parameter");
michael@0 273 return nullptr;
michael@0 274 }
michael@0 275 }
michael@0 276 }
michael@0 277 }
michael@0 278
michael@0 279 // Execute the query and step through the results
michael@0 280 rc = f_sqlite3_step(ppStmt);
michael@0 281 if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
michael@0 282 throwSqliteException(jenv,
michael@0 283 "Can't step statement: (%d) %s", rc, f_sqlite3_errmsg(db));
michael@0 284 return nullptr;
michael@0 285 }
michael@0 286
michael@0 287 // Get the column count and names
michael@0 288 int cols;
michael@0 289 cols = f_sqlite3_column_count(ppStmt);
michael@0 290
michael@0 291 {
michael@0 292 // Allocate a String[cols]
michael@0 293 jobjectArray jStringArray = jenv->NewObjectArray(cols,
michael@0 294 stringClass,
michael@0 295 nullptr);
michael@0 296 if (jStringArray == nullptr) {
michael@0 297 throwSqliteException(jenv, "Can't allocate String[]");
michael@0 298 return nullptr;
michael@0 299 }
michael@0 300
michael@0 301 // Assign column names to the String[]
michael@0 302 for (int i = 0; i < cols; i++) {
michael@0 303 const char* colName = f_sqlite3_column_name(ppStmt, i);
michael@0 304 jstring jStr = jenv->NewStringUTF(colName);
michael@0 305 jenv->SetObjectArrayElement(jStringArray, i, jStr);
michael@0 306 }
michael@0 307
michael@0 308 // Construct the MatrixCursor(String[]) with given column names
michael@0 309 jCursor = jenv->NewObject(cursorClass,
michael@0 310 jCursorConstructor,
michael@0 311 jStringArray);
michael@0 312 if (jCursor == nullptr) {
michael@0 313 throwSqliteException(jenv, "Can't allocate MatrixBlobCursor");
michael@0 314 return nullptr;
michael@0 315 }
michael@0 316 }
michael@0 317
michael@0 318 // Return the id and number of changed rows in jQueryRes
michael@0 319 {
michael@0 320 jlong id = f_sqlite3_last_insert_rowid(db);
michael@0 321 jenv->SetLongArrayRegion(jQueryRes, 0, 1, &id);
michael@0 322
michael@0 323 jlong changed = f_sqlite3_changes(db);
michael@0 324 jenv->SetLongArrayRegion(jQueryRes, 1, 1, &changed);
michael@0 325 }
michael@0 326
michael@0 327 // For each row, add an Object[] to the passed ArrayList,
michael@0 328 // with that containing either String or ByteArray objects
michael@0 329 // containing the columns
michael@0 330 while (rc != SQLITE_DONE) {
michael@0 331 // Process row
michael@0 332 // Construct Object[]
michael@0 333 jobjectArray jRow = jenv->NewObjectArray(cols,
michael@0 334 objectClass,
michael@0 335 nullptr);
michael@0 336 if (jRow == nullptr) {
michael@0 337 throwSqliteException(jenv, "Can't allocate jRow Object[]");
michael@0 338 return nullptr;
michael@0 339 }
michael@0 340
michael@0 341 for (int i = 0; i < cols; i++) {
michael@0 342 int colType = f_sqlite3_column_type(ppStmt, i);
michael@0 343 if (colType == SQLITE_BLOB) {
michael@0 344 // Treat as blob
michael@0 345 const void* blob = f_sqlite3_column_blob(ppStmt, i);
michael@0 346 int colLen = f_sqlite3_column_bytes(ppStmt, i);
michael@0 347
michael@0 348 // Construct ByteBuffer of correct size
michael@0 349 jobject jByteBuffer =
michael@0 350 jenv->CallStaticObjectMethod(byteBufferClass,
michael@0 351 jByteBufferAllocateDirect,
michael@0 352 colLen);
michael@0 353 if (jByteBuffer == nullptr) {
michael@0 354 throwSqliteException(jenv,
michael@0 355 "Failure calling ByteBuffer.allocateDirect");
michael@0 356 return nullptr;
michael@0 357 }
michael@0 358
michael@0 359 // Get its backing array
michael@0 360 void* bufferArray = jenv->GetDirectBufferAddress(jByteBuffer);
michael@0 361 if (bufferArray == nullptr) {
michael@0 362 throwSqliteException(jenv,
michael@0 363 "Failure calling GetDirectBufferAddress");
michael@0 364 return nullptr;
michael@0 365 }
michael@0 366 memcpy(bufferArray, blob, colLen);
michael@0 367
michael@0 368 jenv->SetObjectArrayElement(jRow, i, jByteBuffer);
michael@0 369 jenv->DeleteLocalRef(jByteBuffer);
michael@0 370 } else if (colType == SQLITE_NULL) {
michael@0 371 jenv->SetObjectArrayElement(jRow, i, nullptr);
michael@0 372 } else {
michael@0 373 // Treat everything else as text
michael@0 374 const char* txt = (const char*)f_sqlite3_column_text(ppStmt, i);
michael@0 375 jstring jStr = jenv->NewStringUTF(txt);
michael@0 376 jenv->SetObjectArrayElement(jRow, i, jStr);
michael@0 377 jenv->DeleteLocalRef(jStr);
michael@0 378 }
michael@0 379 }
michael@0 380
michael@0 381 // Append Object[] to Cursor
michael@0 382 jenv->CallVoidMethod(jCursor, jCursorAddRow, jRow);
michael@0 383
michael@0 384 // Clean up
michael@0 385 jenv->DeleteLocalRef(jRow);
michael@0 386
michael@0 387 // Get next row
michael@0 388 rc = f_sqlite3_step(ppStmt);
michael@0 389 // Real error?
michael@0 390 if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
michael@0 391 throwSqliteException(jenv,
michael@0 392 "Can't re-step statement:(%d) %s", rc, f_sqlite3_errmsg(db));
michael@0 393 return nullptr;
michael@0 394 }
michael@0 395 }
michael@0 396
michael@0 397 rc = f_sqlite3_finalize(ppStmt);
michael@0 398 if (rc != SQLITE_OK) {
michael@0 399 throwSqliteException(jenv,
michael@0 400 "Can't finalize statement: %s", f_sqlite3_errmsg(db));
michael@0 401 return nullptr;
michael@0 402 }
michael@0 403
michael@0 404 return jCursor;
michael@0 405 }

mercurial