From 50d507a163d9f2ced8c87088d72897ce99d10af8 Mon Sep 17 00:00:00 2001 From: Nick Parker Date: Fri, 10 Apr 2026 08:54:52 -0500 Subject: [PATCH 1/4] Add beginTransactionReadOnly in support of SupportSQLiteDatabase --- .../java/net/zetetic/database/sqlcipher/SQLiteDatabase.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SQLiteDatabase.java b/sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SQLiteDatabase.java index 1a26f71..2fba349 100644 --- a/sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SQLiteDatabase.java +++ b/sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SQLiteDatabase.java @@ -401,6 +401,10 @@ private static boolean isMainThread() { return looper != null && looper == Looper.getMainLooper(); } + public void beginTransactionReadOnly() { + beginTransaction(); + } + /** * Begins a transaction in EXCLUSIVE mode. *

From 2862a81d10e609d5f88bf6e32c4a7ed62052fc9c Mon Sep 17 00:00:00 2001 From: Nick Parker Date: Thu, 23 Apr 2026 13:49:20 -0500 Subject: [PATCH 2/4] Replace Critical-based JNI API to prevent blocking garbage collector Replace the following APIs: - GetStringCritical - ReleaseStringCritical - GetPrimitiveArrayCritical - ReleasePrimitiveArrayCritical with the following: - GetStringChars - ReleaseStringChars - GetByteArrayElements - ReleaseByteArrayElements The critical version of these API's would prevent the garbage collector from running until the corresponding Release API was called. This is in reference to this report: https://discuss.zetetic.net/t/sqlcipher-for-android-4-14-1-community-edition-release/7190/2 --- .../jni/sqlcipher/android_database_CursorWindow.cpp | 4 ++-- .../sqlcipher/android_database_SQLiteConnection.cpp | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sqlcipher/src/main/jni/sqlcipher/android_database_CursorWindow.cpp b/sqlcipher/src/main/jni/sqlcipher/android_database_CursorWindow.cpp index a835f6d..8ddce4e 100644 --- a/sqlcipher/src/main/jni/sqlcipher/android_database_CursorWindow.cpp +++ b/sqlcipher/src/main/jni/sqlcipher/android_database_CursorWindow.cpp @@ -275,9 +275,9 @@ namespace android { jbyteArray valueObj, jint row, jint column) { auto* window = reinterpret_cast(windowPtr); jsize len = env->GetArrayLength(valueObj); - void* value = env->GetPrimitiveArrayCritical(valueObj, NULL); + jbyte* value = static_cast(env->GetByteArrayElements(valueObj, NULL)); status_t status = window->putBlob(row, column, value, len); - env->ReleasePrimitiveArrayCritical(valueObj, value, JNI_ABORT); + env->ReleaseByteArrayElements(valueObj, value, JNI_ABORT); if (status) { ALOGD("Failed to put blob. error=%d", status); return false; diff --git a/sqlcipher/src/main/jni/sqlcipher/android_database_SQLiteConnection.cpp b/sqlcipher/src/main/jni/sqlcipher/android_database_SQLiteConnection.cpp index f0eab3c..dcb6a7a 100644 --- a/sqlcipher/src/main/jni/sqlcipher/android_database_SQLiteConnection.cpp +++ b/sqlcipher/src/main/jni/sqlcipher/android_database_SQLiteConnection.cpp @@ -354,11 +354,11 @@ static jlong nativePrepareStatement(JNIEnv* env, jclass clazz, jlong connectionP auto* connection = reinterpret_cast(connectionPtr); jsize sqlLength = env->GetStringLength(sqlString); - const jchar* sql = env->GetStringCritical(sqlString, NULL); + const jchar* sql = env->GetStringChars(sqlString, NULL); sqlite3_stmt* statement; int err = sqlite3_prepare16_v2(connection->db, sql, sqlLength * sizeof(jchar), &statement, NULL); - env->ReleaseStringCritical(sqlString, sql); + env->ReleaseStringChars(sqlString, sql); if (err != SQLITE_OK) { // Error messages like 'near ")": syntax error' are not @@ -459,10 +459,10 @@ static void nativeBindString(JNIEnv* env, jclass clazz, jlong connectionPtr, auto* connection = reinterpret_cast(connectionPtr); auto* statement = reinterpret_cast(statementPtr); jsize valueLength = env->GetStringLength(valueString); - const jchar* value = env->GetStringCritical(valueString, NULL); + const jchar* value = env->GetStringChars(valueString, NULL); int err = sqlite3_bind_text16(statement, index, value, valueLength * sizeof(jchar), SQLITE_TRANSIENT); - env->ReleaseStringCritical(valueString, value); + env->ReleaseStringChars(valueString, value); if (err != SQLITE_OK) { throw_sqlite3_exception(env, connection->db, NULL); } @@ -473,9 +473,9 @@ static void nativeBindBlob(JNIEnv* env, jclass clazz, jlong connectionPtr, auto* connection = reinterpret_cast(connectionPtr); auto* statement = reinterpret_cast(statementPtr); jsize valueLength = env->GetArrayLength(valueArray); - auto* value = static_cast(env->GetPrimitiveArrayCritical(valueArray, NULL)); + jbyte* value = static_cast(env->GetByteArrayElements(valueArray, NULL)); int err = sqlite3_bind_blob(statement, index, value, valueLength, SQLITE_TRANSIENT); - env->ReleasePrimitiveArrayCritical(valueArray, value, JNI_ABORT); + env->ReleaseByteArrayElements(valueArray, value, JNI_ABORT); if (err != SQLITE_OK) { throw_sqlite3_exception(env, connection->db, NULL); } From 547380335b4cabf20ee5b022382f645322fd5516 Mon Sep 17 00:00:00 2001 From: Nick Parker Date: Tue, 28 Apr 2026 08:23:17 -0500 Subject: [PATCH 3/4] Update sqlcipher core submodule to v4.15.0 tag --- sqlcipher/src/main/jni/sqlcipher/src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlcipher/src/main/jni/sqlcipher/src b/sqlcipher/src/main/jni/sqlcipher/src index 778ab89..0b4d160 160000 --- a/sqlcipher/src/main/jni/sqlcipher/src +++ b/sqlcipher/src/main/jni/sqlcipher/src @@ -1 +1 @@ -Subproject commit 778ab890cfc30c3631212dcceb0295498abdcd3e +Subproject commit 0b4d16090a71579c4c88220b7fb67bb7396f1434 From a005db3c359ca3c5ede448a5a0cc39a67fa8c32b Mon Sep 17 00:00:00 2001 From: Nick Parker Date: Tue, 28 Apr 2026 08:25:44 -0500 Subject: [PATCH 4/4] Update README for v4.15.0 release --- README.md | 4 ++-- build.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8adadd3..e3870ec 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,14 @@ We welcome contributions, to contribute to SQLCipher for Android, a [contributor Add a local reference to the local library and dependency: ```groovy -implementation files('libs/sqlcipher-android-4.14.0-release.aar') +implementation files('libs/sqlcipher-android-4.15.0-release.aar') implementation 'androidx.sqlite:sqlite:2.6.2' ``` or source a Community edition build from Maven Central: ```groovy -implementation 'net.zetetic:sqlcipher-android:4.14.0@aar' +implementation 'net.zetetic:sqlcipher-android:4.15.0@aar' implementation 'androidx.sqlite:sqlite:2.6.2' ``` diff --git a/build.gradle b/build.gradle index a2dae67..1a94a03 100644 --- a/build.gradle +++ b/build.gradle @@ -33,7 +33,7 @@ project.ext { if(project.hasProperty('sqlcipherAndroidVersion') && "${sqlcipherAndroidVersion}") { libraryVersion = "${sqlcipherAndroidVersion}" } else { - libraryVersion = "4.14.1" + libraryVersion = "4.15.0" } minSdkVersion = 23 androidXSQLiteVersion = "2.6.2"