Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalid input: Failed to load dynamic library in Android using sqlcipher_flutter_libs. #176

Closed
madz opened this issue Aug 15, 2023 · 8 comments

Comments

@madz
Copy link

madz commented Aug 15, 2023

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Issue: Unhandled Exception - Invalid input: Failed to load dynamic library '/data/data/com.example.rmapp/lib/libsqlite3.so'. The attempt to load the library failed because it couldn't find the file at that location.

Even though I added 'sqlcipher_flutter_libs', which I use for encrypting SQLite along with Drift, I'm still encountering this problem.

I've also used the 'open.overrideFor(OperatingSystem.android, openCipherOnAndroid);' function before creating the database file. However, the error persists. Interestingly, my app works fine on Windows, MacOS, and iOS, but I'm facing this problem only on Android.

Here are the key dependencies in my project:

dependencies:
drift: ^2.10.0
sqlcipher_flutter_libs: ^0.5.7
sqlite3: ^2.1.0

dev_dependencies:
drift_dev: ^2.10.0
build_runner: ^2.4.6

@madz madz changed the title Error to load Database in Android using Sqlite Cipher. Invalid input: Failed to load dynamic library in Android using Sqlite Cipher. Aug 15, 2023
@madz madz changed the title Invalid input: Failed to load dynamic library in Android using Sqlite Cipher. Invalid input: Failed to load dynamic library in Android using sqlcipher_flutter_libs. Aug 15, 2023
@simolus3
Copy link
Owner

Thanks for the report. Does this happen with a drift database or also when opening a database manually with the sqlite3 package?

When using NativeDatabase.createInBackground from drift, you need to make sure the open.overrideFor() call happens on the background isolate, e.g.

LazyDatabase _openConnection() {
  return LazyDatabase(() async {
    final dbFolder = await getApplicationDocumentsDirectory();
    final file = File(p.join(dbFolder.path, 'db.sqlite'));
    final rootToken = RootIsolateToken.instance!;
    return NativeDatabase.createInBackground(
      file,
      isolateSetup: () {
        BackgroundIsolateBinaryMessenger.ensureInitialized(rootToken);
        open.overrideFor(OperatingSystem.android, openCipherOnAndroid);
      }
    );
  });
}

@madz
Copy link
Author

madz commented Aug 15, 2023

Hi Simon,

Thanks for getting back to me regarding my issue. I've tried making changes to my code as you suggested, but unfortunately, I'm still facing the same error.

Here's the updated version of my code:

return NativeDatabase.createBackgroundConnection(
  file,
  setup: (rawDb) {
    if (Platform.isAndroid) {
      BackgroundIsolateBinaryMessenger.ensureInitialized(rootToken);
      open.overrideFor(OperatingSystem.android, openCipherOnAndroid);
    }

    // I'm checking if we're using SQLCipher by looking at the cipher_version.
    final result = rawDb.select('pragma cipher_version');
    if (result.isEmpty) {
      throw UnsupportedError(
        'This database needs SQLCipher, but it's not available right now!',
      );
    }

    // I'm adding an encryption key for database security. Unfortunately, the
    // method I'm using doesn't work well with prepared statements, so I'm
    // directly putting the key here.
    rawDb
      ..execute("PRAGMA key = '${DatabaseConstants.dbPassphrase}';")

      // I'm testing if the key works by selecting from a table.
      ..execute('select count(*) from sqlite_master');
  },
);

I followed the instructions in this tutorial: https://drift.simonbinder.eu/docs/other-engines/encryption/ to create an encrypted file, specifically the last part.

Thank you for your help.

@madz
Copy link
Author

madz commented Aug 15, 2023

If it's beneficial, here's my complete code snippet for establishing a database connection:

LazyDatabase _establishDatabaseConnection({
  required bool isTesting,
  required bool isEncrypted,
}) {
  return LazyDatabase(() async {
    var databasePath = '';
    final rootToken = RootIsolateToken.instance!;

    if (isEncrypted && Platform.isWindows) {
      open.overrideFor(OperatingSystem.windows, openCipherOnWindows);
    }

    if (isTesting) {
      final currentDirectory = Directory.current;

      databasePath = isEncrypted
          ? p.join(currentDirectory.path, DatabaseConstants.testDbNameEncrypted)
          : p.join(currentDirectory.path, DatabaseConstants.testDbName);
    } else {
      final appDocumentsDir = await getApplicationDocumentsDirectory();

      databasePath = isEncrypted
          ? p.join(appDocumentsDir.path, DatabaseConstants.appDbNameEncrypted)
          : p.join(appDocumentsDir.path, DatabaseConstants.appDbName);
    }

    final databaseFile = File(databasePath);
    debugPrint(
      'Database Location = $databaseFile isTesting = $isTesting isEncrypted = $isEncrypted',
    );

    if (isEncrypted) {
      return NativeDatabase.createBackgroundConnection(
        databaseFile,
        setup: (rawDb) {
          if (Platform.isAndroid) {
            BackgroundIsolateBinaryMessenger.ensureInitialized(rootToken);
            open.overrideFor(OperatingSystem.android, openCipherOnAndroid);
          }

          final cipherVersionResult = rawDb.select('pragma cipher_version');
          if (cipherVersionResult.isEmpty) {
            throw UnsupportedError(
              'This database requires SQLCipher, which is not currently available!',
            );
          }

          rawDb
            ..execute("PRAGMA key = '${DatabaseConstants.dbPassphrase}';")
            ..execute('select count(*) from sqlite_master');
        },
      );
    }

    return NativeDatabase.createInBackground(
      databaseFile,
    );
  });
}

@madz
Copy link
Author

madz commented Aug 15, 2023

I think there might be a problem with Drift. It's not able to find the libsqlite3.so file, even though the file has already been made.

E/flutter (32523): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Invalid argument(s): Failed to load dynamic library '/data/data/com.example.rmapp/lib/libsqlite3.so': dlopen failed: library "/data/data/com.example.rmapp/lib/libsqlite3.so" not found
E/flutter (32523): #0 DriftCommunication.request (package:drift/src/remote/communication.dart:113:66)
E/flutter (32523): #1 _RemoteQueryExecutor.ensureOpen (package:drift/src/remote/client_impl.dart:158:10)
E/flutter (32523): #2 LazyDatabase.ensureOpen. (package:drift/src/utils/lazy_database.dart:61:49)
E/flutter (32523):
E/flutter (32523): #3 InsertStatement.insert (package:drift/src/runtime/query_builder/statements/insert.dart:73:12)
E/flutter (32523):
E/flutter (32523): #4 _HomeViewState._testDb (package:rmapp/src/features/home/presentation/views/home_views.dart:65:5)
E/flutter (32523):
E/flutter (32523):

@madz
Copy link
Author

madz commented Aug 15, 2023

I tried following the steps in this problem: simolus3/drift#2314. However, the process of using "flutter cache clean" and "flutter pub get" didn't solve my issue.

I'm wondering if the "sqlcipher_flutter_libs" should create the "libsqlite3.so" file, but it appears that this is not functioning correctly.

@madz
Copy link
Author

madz commented Aug 15, 2023

I just now get Simon's code. Here is my code now that fix the error.

return NativeDatabase.createInBackground(
       databaseFile,
       logStatements: true,
       isolateSetup: () {
         if (Platform.isAndroid) {
           BackgroundIsolateBinaryMessenger.ensureInitialized(rootToken);
           open.overrideFor(OperatingSystem.android, openCipherOnAndroid);
         }
       },
       setup: (database) async {
         final cipherVersionResult = database.select('pragma cipher_version');
         if (cipherVersionResult.isEmpty) {
           throw UnsupportedError(
             'This database requires SQLCipher, which is not currently available!',
           );
         }

         database
           ..execute("PRAGMA key = '${DatabaseConstants.dbPassphrase}';")
           ..execute('select count(*) from sqlite_master');
       },
     );

@madz madz closed this as completed Aug 15, 2023
@Luwx
Copy link

Luwx commented Feb 7, 2024

@simolus3 can you update the encryption example? It has this problem

@simolus3
Copy link
Owner

simolus3 commented Feb 7, 2024

Will do, thanks for pointing this out!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants