diff --git a/Android/build.gradle.kts b/Android/build.gradle.kts index 1332cb6..82d2c6b 100644 --- a/Android/build.gradle.kts +++ b/Android/build.gradle.kts @@ -3,5 +3,5 @@ plugins { id("com.android.application") version ("8.3.2") apply false id("com.android.library") version ("8.3.2") apply false id("org.jetbrains.kotlin.android") version ("1.9.10") apply false - id("org.mozilla.rust-android-gradle.rust-android") version ("0.9.4") apply false +// id("org.mozilla.rust-android-gradle.rust-android") version ("0.9.4") apply false } \ No newline at end of file diff --git a/Android/shared/build.gradle.kts b/Android/shared/build.gradle.kts index 45f4e87..cf03d4c 100644 --- a/Android/shared/build.gradle.kts +++ b/Android/shared/build.gradle.kts @@ -3,7 +3,7 @@ import java.util.Locale plugins { id("com.android.library") id("org.jetbrains.kotlin.android") - id("org.mozilla.rust-android-gradle.rust-android") +// id("org.mozilla.rust-android-gradle.rust-android") } android { @@ -41,7 +41,13 @@ android { // main.java.srcDirs += "${projectDir}/../../shared_types/generated/java" named("main") { java.srcDirs("${projectDir}/../../shared/generated/java") +// TODO just for testing include manually build in sources } +// named("jniLibs") { +// java.srcDirs(layout.buildDirectory.get().file("/rustJniLibs/android/arm64-v8a/libshared.so")) +// } + + } } @@ -56,41 +62,42 @@ dependencies { androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1") } -//apply plugin: "org.mozilla.rust-android-gradle.rust-android" -apply(plugin = "org.mozilla.rust-android-gradle.rust-android") -cargo { - module = "../.." - libname = "shared" - // these are the four recommended targets for Android that will ensure your library works on all mainline android devices - // make sure you have included the rust toolchain for each of these targets: \ - // `rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android` - targets = listOf("arm", "arm64", "x86", "x86_64") - extraCargoBuildArguments = listOf("--package", "shared") - pythonCommand = "python3" -} +//apply(plugin = "org.mozilla.rust-android-gradle.rust-android") +//cargo { +// module = "../.." +// libname = "shared" +// // these are the four recommended targets for Android that will ensure your library works on all mainline android devices +// // make sure you have included the rust toolchain for each of these targets: \ +// // `rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android` +// targets = listOf("arm", "arm64", "x86", "x86_64") +// extraCargoBuildArguments = listOf("--package", "shared") +// pythonCommand = "python3" +// prebuiltToolchains = true +//} afterEvaluate { // The `cargoBuild` task isn"t available until after evaluation. android.libraryVariants.configureEach { - var productFlavor = "" - productFlavors.forEach { flavor -> - productFlavor += flavor.name.replaceFirstChar { - if (it.isLowerCase()) it.titlecase( - Locale.getDefault() - ) else it.toString() - } - } - val buildType = buildType.name.replaceFirstChar { - if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() - } +// var productFlavor = "" +// productFlavors.forEach { flavor -> +// productFlavor += flavor.name.replaceFirstChar { +// if (it.isLowerCase()) it.titlecase( +// Locale.getDefault() +// ) else it.toString() +// } +// } +// val buildType = buildType.name.replaceFirstChar { +// if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() +// } tasks.named("compileDebugKotlin") { dependsOn(tasks.named("bindGen")) } - tasks.named("generate${productFlavor}${buildType}Assets") { - dependsOn(tasks.named("cargoBuild")) - } + +// tasks.named("generate${productFlavor}${buildType}Assets") { +// dependsOn(tasks.named("cargoBuild")) +// } } } diff --git a/RUST SQLITE SQLCIPHER ANDROIDl.md b/RUST SQLITE SQLCIPHER ANDROIDl.md new file mode 100644 index 0000000..e8090de --- /dev/null +++ b/RUST SQLITE SQLCIPHER ANDROIDl.md @@ -0,0 +1,21 @@ +# How to get SQLite with SQLCipher in Rust on Android + +The instructions are specific to this repository at the commit this file was added. +1. Install cargo-ndk +2. Add rusqlite to Cargo.toml with the following features: + ```toml + rusqlite = { version = "0.31.0", features = [ + "bundled-sqlcipher-vendored-openssl", + "bundled", + ] } + ``` + Since it needs a sqlite and openssl version that is specific to android this is the easiest way to get that. +3. Don't try to use the Mozilla Rust Android Gradle Plugin + Won't build due to some error I don't understand +4. Only build with cargo-ndk + ```cargo ndk --target aarch64-linux-android --platform 26 --output-dir ./Android/app/src/main/jniLibs/ -- build``` + Things to note here: + - The target might be different depending on where you want to run it. For me, it was an android emulator on an Apple Silicon MacBook + - The file is output in the app main jniLibs directory. This is only to have it included in the app bundle. + The code actually calling the library is in the "shared" android library project. This was just the easiest way to include it in the app as the gradle stuff I tried wasn't working. +5. Run the app build in Android Studio. There is some UniFFI bindings generation stuff that needs to be run but that is specific to this project. \ No newline at end of file diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 21baeff..29efa9e 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -15,9 +15,11 @@ uniffi = "0.28.0" rusqlite = { version = "0.31.0", features = [ # Does not find openssl/crypto.h # "bundled-sqlcipher", - "bundled-sqlcipher-vendored-openssl", # "bundled-full" # "sqlcipher" + + # This works but only when running cargo-ndk. Not through cargo build gradle plugin + "bundled-sqlcipher-vendored-openssl", "bundled", ] } diff --git a/shared/src/lib.rs b/shared/src/lib.rs index 2ee6c77..5a1c926 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -3,6 +3,7 @@ pub mod database; use std::path::Path; use std::sync::OnceLock; +use rusqlite::params; pub use app::*; @@ -35,11 +36,13 @@ async fn open_database(path: String) -> String { let is_created = database_path.exists(); let connection = - rusqlite::Connection::open(path); + rusqlite::Connection::open(database_path.clone()); - let Ok(connection) = connection else { - return "Error opening database".to_string(); + let connection = match connection { + Ok(connection) => connection, + Err(error) => return format!("Error opening database: {}", error), }; + // Set encryption key for sqlcipher let result = connection .pragma_update(None, "key", "TODO"); @@ -48,6 +51,53 @@ async fn open_database(path: String) -> String { return "Error setting key".to_string(); } + let result = connection.execute_batch("CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY, name TEXT);\ + INSERT INTO test (id, name) VALUES (1, 'test');"); + + if let Err(error) = result { + return format!("Error creating table and inserting test data.: {}", error); + } + + // Read data from database + let result = connection.query_row("SELECT * FROM test", params![],|row| { + + let id = row.get::<_, i32>(0)?; + Ok(id) + }); + + if result.is_err() { + return "Error reading data from database".to_string(); + } + + let result = connection.close(); + + // Try to open encrypted database without key to see if encryption is working + if result.is_err() { + return "Error closing database".to_string(); + } + let connection = + rusqlite::Connection::open(database_path); + + let connection = match connection { + Ok(connection) => connection, + Err(error) => return format!("Error opening database again: {}", error), + }; + + // Don't set encryption key for sqlcipher + // But try to read data from database + let result = connection.query_row("SELECT * FROM test",params![], |row| { + + let id = row.get::<_, i32>(0)?; + Ok(id) + }); + + + if let Err(error) = result { + return format!("Error reading data from database. This is good. It means we can not read the database without encryption key: {}", error); + } + + + // Enable foreign keys // connection