but when getWritableDatabase() gets called , crashing with below message
Caused by: android.database.sqlite.SQLiteException: file is not a database (code 26): , while compiling: PRAGMA journal_mode
Thank you for your interest in SQLCipher and for posting to the discussion forum.
SQLCipher 3.x has different encryption settings than SQLCipher 4.x, so youâll need to migrate your database(s) to use these new encryption settings (or open them in compatibility mode) as outlined in this post: Upgrading to SQLCipher 4
SQLCipher should properly migrate all your tables and database data when performing the migration, so you shouldnât need to drop your tables and re-create them onUpgrade like youâre doing unless thatâs an application requirement.
From your code it looks like youâre hard coding a key value. We strongly recommend against doing that. Thereâs some information about Key Material and Selection, along with some technical guidance in these posts:
I did a series of tests and looked at all the examples you posted. I tried to do the migration starting from a db with default settings and using âcipher migrationâ and it works correctly. In my opinion the problem arises from the page size.
You donât need to create the new database first, the sqlcipher_export(...) function will do that during the export. You need to open the old database using the hook, but also execute the following:
Thank you. You solved my problem. I had read in a comment not to use âcompatibilityâ when the original db uses custom settings.
In any case there was a piece missing: âthere was no need to create a new databaseâ.
Greetings
Hello
Now I am trying to update
implementation ânet.zetetic:android-database-sqlcipher:3.5.9â to implementation ânet.zetetic:android-database-sqlcipher:4.5.4â
My implementation is mentioned below.
public DatabaseHelper open() throws SQLException {
SQLiteDatabase.loadLibs(this.context);
database = this.getWritableDatabase(Constants.api_secret);
return this;
}
public SQLiteDatabase getDatabase() {
if (database == null) {
SQLiteDatabase.loadLibs(this.context);
database = getWritableDatabase(Constants.api_secret);
}
return database;
}
@Override
public void preKey(SQLiteDatabase sqLiteDatabase) {
}
@Override
public void postKey(SQLiteDatabase sqLiteDatabase) {
try (Cursor cursor = sqLiteDatabase.rawQuery("PRAGMA cipher_migrate", null)) {
// Check if migration was successful
if (cursor != null && cursor.moveToFirst()) {
int result = cursor.getInt(0);
cursor.close();
if (result == 0) {
Log.d("DataBaseHelper", "migration is successful");
} else {
// Handle migration failure
throw new SQLiteException("Cipher migration failed");
}
} else {
// Handle no result or cursor error
throw new SQLiteException("No result from PRAGMA cipher_migrate");
}
} catch (Exception e) {
throw new SQLiteException("Error executing PRAGMA cipher_migrate", e);
}
}
});
this.context = context;
}
working fine but some times getting ANR.
Can you please let me know based on below link
postKey() implementation is correct or not( not sure why this change is causing ANR as log is printed - Migration is successful )?. Only for changing the version( 3.5.9 to 4.5.4) do I need to increase the db version?. Can you please help on this.
Unfortunately, there is not enough information to determine why you are occasionally receiving an ANR. Because the cipher_migrate process will have to rewrite each page within the database, the size of each database may impact the overall time to complete the process. You should make sure you are not performing this operation on the main UI thread as it may block.
Can you please let me know
postKey() implementation is correct or not?
private DatabaseHelper(Context context) {
super(context, DBNAME, null, VERSION, new SQLiteDatabaseHook() { @Override
public void preKey(SQLiteDatabase sqLiteDatabase) {
}
@Override
public void postKey(SQLiteDatabase sqLiteDatabase) {
try (Cursor cursor = sqLiteDatabase.rawQuery("PRAGMA cipher_migrate", null)) {
// Check if migration was successful
if (cursor != null && cursor.moveToFirst()) {
int result = cursor.getInt(0);
cursor.close();
if (result == 0) {
Log.d("DataBaseHelper", "migration is successful");
} else {
// Handle migration failure
throw new SQLiteException("Cipher migration failed");
}
} else {
// Handle no result or cursor error
throw new SQLiteException("No result from PRAGMA cipher_migrate");
}
} catch (Exception e) {
throw new SQLiteException("Error executing PRAGMA cipher_migrate", e);
}
}
});
this.context = context;
unfortunately I am getting below crash. @Override
public void postKey(SQLiteDatabase sqLiteDatabase) {
Executors.newSingleThreadExecutor().execute(new Runnable() { @Override
public void run() {
try (Cursor cursor = sqLiteDatabase.rawQuery(âPRAGMA cipher_migrateâ, null)) {
// Check if migration was successful
if (cursor != null && cursor.moveToFirst()) {
int result = cursor.getInt(0);
cursor.close();
if (result == 0) {
Log.d(âDataBaseHelperâ, âmigration is successfulâ);
} else {
// Handle migration failure
throw new SQLiteException(âCipher migration failedâ);
}
} else {
// Handle no result or cursor error
throw new SQLiteException(âNo result from PRAGMA cipher_migrateâ);
}
} catch (Exception e) {
throw new SQLiteException(âError executing PRAGMA cipher_migrateâ, e);
}
}
});
}
});
=================
FATAL EXCEPTION: AsyncTask #2
Process:
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$4.done(AsyncTask.java:415)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:381)
at java.util.concurrent.FutureTask.setException(FutureTask.java:250)
at java.util.concurrent.FutureTask.run(FutureTask.java:269)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:305)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012)
Caused by: android.database.sqlite.SQLiteException: unable to close due to unfinalized statements or unfinished backups: sqlite3_close() failed
at net.sqlcipher.database.SQLiteDatabase.dbclose(Native Method)
at net.sqlcipher.database.SQLiteDatabase.openDatabaseInternal(SQLiteDatabase.java:2635)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1250)
at net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:1325)
at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:167)
at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:136)
at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:132)
at android.os.AsyncTask$3.call(AsyncTask.java:394)
at java.util.concurrent.FutureTask.run(FutureTask.java:264)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:305)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012)
2024-06-25 20:39:06.126 4166-4585 AndroidRuntime E FATAL EXCEPTION: pool-11-thread-1
Process:
android.database.sqlite.SQLiteException: Error executing PRAGMA cipher_migrate
at
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012)
Caused by: android.database.sqlite.SQLiteException: Cipher migration failed
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012)
It look like youâre using try-with-resources, but also explicitly closing the cursor. If you remove the explicit call to close on the cursor does it go away? Is your AsyncTask doing anything else with the connection?
there are 2 FATAL EXCEPTIONS One during migration and another during clearTable()
2024-06-26 08:06:54.353 13645-13769 AndroidRuntime com.ticke.android.app E FATAL EXCEPTION: AsyncTask #1
Process: com.ticke.android.app, PID: 13645
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$4.done(AsyncTask.java:415)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:381)
at java.util.concurrent.FutureTask.setException(FutureTask.java:250)
at java.util.concurrent.FutureTask.run(FutureTask.java:269)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:305)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012)
Caused by: android.database.sqlite.SQLiteException: unable to close due to unfinalized statements or unfinished backups: sqlite3_close() failed
at net.sqlcipher.database.SQLiteDatabase.dbclose(Native Method)
at net.sqlcipher.database.SQLiteDatabase.openDatabaseInternal(SQLiteDatabase.java:2635)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1250)
at net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:1325)
at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:167)
at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:136)
at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:132)
at com.ticke.android.app.common.services.DatabaseHelper.getDatabase(DatabaseHelper.java:124)
at com.ticke.android.app.common.services.DatabaseHelper.clearTables(DatabaseHelper.java:130)
atcom.ticke.android.app.common.services.DatabaseHelper$StoreUserTicketListTask.doInBackground(DatabaseHelper.java:146)
at com.ticke.android.app.common.services.DatabaseHelper$StoreUserTicketListTask.doInBackground(DatabaseHelper.java:140)
at android.os.AsyncTask$3.call(AsyncTask.java:394)
at java.util.concurrent.FutureTask.run(FutureTask.java:264)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:305)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012)
===============================================================
2024-06-26 08:06:54.356 13645-13770 AndroidRuntime com.ticke.android.app E FATAL EXCEPTION: pool-11-thread-1
Process: com.ticke.android.app, PID: 13645
android.database.sqlite.SQLiteException: Error executing PRAGMA cipher_migrate
at com.ticke.android.common.services.DatabaseHelper$1$1.run(DatabaseHelper.java:91)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012)
Caused by: android.database.sqlite.SQLiteException: Cipher migration failed
at com.ticke.android.common.services.DatabaseHelper$1$1.run(DatabaseHelper.java:84)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012)
Inside asyncTask first we are calling clearTables()
public void clearTables() {
getDatabase().execSQL(âdelete from ticketâ);
getDatabase().execSQL(âdelete from performanceâ);
}
public SQLiteDatabase getDatabase() {
if (database == null) {
SQLiteDatabase.loadLibs(this.context);
database = getWritableDatabase(Constants.api_secret); // this is the last call from appâs class DatabaseHelper before crash.
}
return database;
}
postKey() code remains same , but removed cursor.close()
@Override
public void postKey(SQLiteDatabase sqLiteDatabase) {
Executors.newSingleThreadExecutor().execute(new Runnable() { @Override
public void run() {
try (Cursor cursor = sqLiteDatabase.rawQuery(âPRAGMA cipher_migrateâ, null)) {
// Check if migration was successful
if (cursor != null && cursor.moveToFirst()) {
int result = cursor.getInt(0);
if (result == 0) {
Log.d(âDataBaseHelperâ, âmigration is successfulâ);
} else {
// Handle migration failure
throw new SQLiteException(âCipher migration failedâ);
}
} else {
// Handle no result or cursor error
throw new SQLiteException(âNo result from PRAGMA cipher_migrateâ);
}
} catch (Exception e) {
throw new SQLiteException(âError executing PRAGMA cipher_migrateâ, e);
}
}
});
}
});
What happens when you pull an existing SQLCipher 3 database off the device onto your local machine and attempt to run the PRAGMA cipher_migrate command on your host computer when using SQLCipher 4?
I am asking you to extract the SQLCipher 3 database from an Android device onto a host computer, then run the SQLCipher command line shell program (using version 4.*), opening the database and running PRAGMA cipher_migrate. What is your result from doing this?