Hi,
I have severe issues with databases becoming unreadable since updating sqlcipher to version 4.1.3.
We have two Android apps that have been using SQL Cipher for years on version 3.3.1-2.
A few weeks back, I moved to 4.1.3 and had to make some changes to the code to support both old existing database 3 versions and new 4 versions. I could not get the database migration from 3 to 4 to work since cipher_migrate always returned an error so I created code to test the version and then automatically set cipher_compatibility before opening.
This worked fine, but in the field, the database seems to corrupt at some point after which it cannot be opened any more. Problem is, I cannot reproduce it myself and my app users are all very far away. I can therefore not debug the issue. They are working in the app after which at some point they just can’t open the database anymore. In the code below it will throw a “No readable database” error meaning that the database could not be opened with either version 3 or 4 of sqlcipher.
The only solution after this is to delete the database and start over until the problem occurs again.
Does anyone see similar behaviour? Is there a know issue that corrupts the database from time to time?
Code:
public class BrooklynDBHelper extends SQLiteOpenHelper {
private static BrooklynDBHelper sInstance;
private static Context c;
private BrooklynDBHelper(Context context, int cipherVersion) {
super(context, BrooklynDB.DATABASE_NAME, null, BrooklynDB.DATABASE_VERSION, new setSQLCipherVersion(cipherVersion));
}
public static synchronized BrooklynDBHelper getInstance(Context context, String schleuteltje) {
if (sInstance == null) {
c = context.getApplicationContext();
SQLiteDatabase.loadLibs(c);
int cipherVersion = testSqlCipherVersion(schleuteltje);
sInstance = new BrooklynDBHelper(c, cipherVersion);
}
return sInstance;
}
private static int testSqlCipherVersion(String schleutol) {
File dbFile = c.getDatabasePath(BrooklynDB.DATABASE_NAME);
if (!dbFile.exists()) { return 0; } // No existing db, return 0
// Try current version
try {
SQLiteDatabase db = SQLiteDatabase.openDatabase(
dbFile.getAbsolutePath(),
schleutol,
null,
SQLiteDatabase.OPEN_READONLY,
new setSQLCipherVersion(0) // Defines current version
);
db.close();
return 0;
} catch (Exception ignore) {
}
// Try version 3
try {
SQLiteDatabase db = SQLiteDatabase.openDatabase(
dbFile.getAbsolutePath(),
schleutol,
null,
SQLiteDatabase.OPEN_READONLY,
new setSQLCipherVersion(3) // Defines version 3
);
db.close();
return 3;
} catch (Exception ignore) {
}
Log.e("BrooklynDB", "Database SQL Cipher version not recognized! We should NOT continue without a database.");
throw new RuntimeException("No readable database");
}
public void close() {
sInstance.close();
}
static class setSQLCipherVersion implements SQLiteDatabaseHook {
private int version; // 0: current version, 3: 1st used version, 4 currently default per May 2019
private setSQLCipherVersion(int version) { this.version = version; }
@Override
public void preKey(SQLiteDatabase database) { }
@Override
public void postKey(SQLiteDatabase database) {
if (version > 0) {
database.rawExecSQL("PRAGMA cipher_compatibility=" + String.valueOf(version) + ";");
}
}
}
}
Thanks a lot!!