SQLite Android AAR Version Mismatch: sqlite_version() Reports 3.46.0 Instead of 3.46.1

Discrepancy Between sqlite-android-3460100.aar Implementation and Runtime Version Reporting

1. Core Symptom: SQLite Version Mismatch in Android Build Pipeline

The fundamental conflict arises when an Android application built with the official SQLite Android Archive (AAR) file sqlite-android-3460100.aar reports version 3.46.0 via SELECT sqlite_version() at runtime, despite the AAR’s metadata and included binaries containing 3.46.1 identifiers. This occurs specifically in a Flutter environment where the sqlite3_flutter_libs package introduces a transitive dependency conflict through eu.simonbinder:sqlite3-native-library:3.46.0+1. The Android build system prioritizes this older native library over the explicitly included AAR file, causing version string discrepancies between the developer’s expectations (based on the AAR’s versioning) and actual runtime behavior.

Key technical relationships:

  • AAR File Contents: Contains ARM64/x86 JNI libraries (libsqliteX.so) with embedded version strings 3.46.1 in their metadata sections
  • Gradle Dependency Tree: Resolves conflicting native library versions through default version matching rules
  • Flutter Plugin Architecture: sqlite3_flutter_libs injects precompiled SQLite binaries via Maven coordinates, overriding local AAR inclusions
  • APK Packaging: Merges multiple JNI implementations from different sources without version validation checks

The mismatch manifests through three observable layers:

  1. Build Configuration: Explicit AAR dependency declaration in app/build.gradle
  2. Native Binary Inspection: Verified SHA3-256 hash matching SQLite.org’s signed distribution
  3. Runtime Query Execution: Inconsistent version reporting across platforms (iOS vs Android)

2. Underlying Conflict: Native Library Resolution Hierarchy in Multi-Dependency Environments

2.1. Gradle’s Dependency Resolution Precedence Rules

Android’s build system follows strict priority rules when merging dependencies:

  1. Transitive Dependencies from plugin-injected Maven coordinates
  2. Local .AAR Files in project’s lib/ directory
  3. Precompiled NDK Libraries in jniLibs/ directories
  4. Explicit Version Declarations in build.gradle

In this scenario:

  • sqlite3_flutter_libs declares eu.simonbinder:sqlite3-native-library:3.46.0+1 as a transitive dependency
  • Gradle prioritizes this Maven-hosted artifact over the local sqlite-android-3460100.aar
  • The older 3.46.0 binaries get merged into the final APK’s lib/arm64-v8a folder

2.2. Flutter FFI Initialization Sequence

The sqfliteFfiInit() method in Dart code triggers SQLite’s native binding process:

  1. Probes android.jar for system SQLite implementation (typically outdated)
  2. Falls back to plugin-provided JNI libraries via android.database.sqlite
  3. Loads first compatible .so file found in APK’s native library directories

With conflicting libraries present:

  • The build system merges both 3.46.0 (from Maven) and 3.46.1 (from AAR) into intermediate build artifacts
  • Android’s APK packaging process retains duplicate .so files with identical names but different contents
  • Dynamic linker loads whichever library appears first in the filesystem order (nondeterministic without explicit ordering)

2.3. Version String Embedding in SQLite Binaries

SQLite’s version information exists in multiple locations within compiled binaries:

  • Header Metadata: #define SQLITE_VERSION "3.46.1"
  • Compile-Time Constants: sqlite3_libversion_number() returns 3046001
  • Export Table: sqlite3_version[] char array populated at build time

When multiple implementations coexist:

  • SELECT sqlite_version() reflects the actually loaded library’s header metadata
  • Binary inspection via strings command shows all embedded versions present in APK
  • No runtime checks prevent mixing major/minor versions across dependencies

3. Resolution Protocol: Enforcing AAR Priority in Multi-Source Builds

3.1. Dependency Tree Analysis and Conflict Resolution

Step 1: Generate Gradle Dependency Report

./gradlew :app:dependencies --configuration releaseRuntimeClasspath > deps.txt

Search for sqlite3-native-library in output:

+--- eu.simonbinder:sqlite3-native-library:3.46.0+1
|    +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.20
|    \--- com.almworks.sqlite4java:libsqlite:0.6

Step 2: Exclude Transitive Dependency
Modify app/build.gradle:

dependencies {
    implementation files('lib/sqlite-android-3460100.aar')
    implementation('com.tekartik.sqflite:sqflite') {
        exclude group: 'eu.simonbinder', module: 'sqlite3-native-library'
    }
}

Step 3: Verify Clean Dependency Tree
Rerun dependency report to confirm removal of 3.46.0+1 references.

3.2. APK Binary Validation via Reverse Engineering

Step 1: Extract APK’s Native Libraries

unzip -q app-release.apk -d apk_contents
find apk_contents -name 'libsqlite*.so' -exec strings {} \; | grep '3\.46\.'

Expected Output:

3.46.1
3.46.1

Step 2: Cross-Reference Library Checksums
Compare extracted .so files against AAR contents:

# Extract AAR
unzip sqlite-android-3460100.aar -d aar_contents
# Compare ARM64 library
diff aar_contents/jni/arm64-v8a/libsqliteX.so apk_contents/lib/arm64-v8a/libsqliteX.so

3.3. Build Configuration Hardening

Option 1: Enforce AAR Priority via ResolutionStrategy
Add to app/build.gradle:

configurations.all {
    resolutionStrategy {
        force files('lib/sqlite-android-3460100.aar')
        preferProjectModules()
    }
}

Option 2: Explicit Native Library Source Sets
Override Flutter plugin’s default JNI inclusion:

android {
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs/sqlite-android-3460100/jni']
        }
    }
}

3.4. Runtime Validation Layer

Implement startup check in Dart code:

Future<void> validateSqliteVersion() async {
  final result = await db.rawQuery('SELECT sqlite_version(), sqlite_source_id()');
  final version = result.first['sqlite_version'];
  final sourceId = result.first['sqlite_source_id'];
  assert(version == '3.46.1', 
    'SQLite version mismatch: Expected 3.46.1, found $version. Source ID: $sourceId');
}

Key Validation Points:

  • Compare both sqlite_version() and sqlite_source_id() against official build manifests
  • Halt initialization if version mismatch detected
  • Log full sqlite3_config() settings during debug builds

3.5. Flutter Plugin Configuration Audit

Review pubspec.yaml for implicit SQLite dependencies:

dependencies:
  sqflite: ^2.2.8 # Might bring in sqlite3_flutter_libs

Mitigation Strategies:

  1. Pin Exact Versions:
  sqlite3_flutter_libs: 0.5.24 # Avoid 'any' version specifier
  1. Use Dependency Overrides:
dependency_overrides:
  sqlite3_flutter_libs:
    path: ../patched_sqlite_flutter

3.6. Advanced: Custom SQLite Loading with JNI Shim

For critical applications requiring absolute version control:
Step 1: Disable Default JNI Loading

public class CustomSQLiteLoader {
    static {
        System.loadLibrary("custom_sqliteX");
    }
}

Step 2: Embed AAR Libraries as Custom JNI

  • Rename libsqliteX.so to libcustom_sqliteX.so
  • Place in app/src/main/jniLibs/${ABI}/
  • Modify Flutter plugin initialization to use custom loader

3.7. Continuous Integration Safeguards

Implement CI pipeline checks:

- name: Verify SQLite Version
  run: |
    adb shell "dumpsys package com.example.app | grep versionName"
    adb shell "run-as com.example.app cat /data/data/com.example.app/databases/test.db | sqlite3 'SELECT sqlite_version();'"
    # Fail build if output != 3.46.1

4. Post-Resolution Monitoring

After applying fixes:

  1. APK Diff Analysis: Use apkanalyzer to confirm native library changes
  2. Performance Benchmarking: Compare PRAGMA compile_options; outputs
  3. Crash Reporting: Monitor for SQLITE_MISMATCH errors in production

This comprehensive approach addresses dependency conflicts at the build system level, verifies binary integrity through multiple validation layers, and institutes safeguards against regression through CI/CD integration.

Related Guides

Leave a Reply

Your email address will not be published. Required fields are marked *