Skip to content
This repository was archived by the owner on Feb 12, 2026. It is now read-only.

Commit 0f5859e

Browse files
JingMatrixThePedroo
authored andcommitted
Correct database migration and initialization logic (JingMatrix#236)
Reworks the `initDB` function to resolve critical failures. - Prevents a "no such table: configs" crash on fresh installs by deferring the compilation of CREATE TABLE statements until their dependencies are met. - Fixes a "duplicate column" error by correcting the initial schema and letting migration logic add new columns. - Replaces the fragile fall-through switch and nested transactions with a robust, sequential upgrade process inside a single atomic transaction.
1 parent aa363dc commit 0f5859e

File tree

1 file changed

+72
-73
lines changed

1 file changed

+72
-73
lines changed

daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java

Lines changed: 72 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -140,16 +140,15 @@ public int hashCode() {
140140
}
141141
}
142142

143-
private final SQLiteStatement createModulesTable = db.compileStatement("CREATE TABLE IF NOT EXISTS modules (" +
143+
private static final String CREATE_MODULES_TABLE = "CREATE TABLE IF NOT EXISTS modules (" +
144144
"mid integer PRIMARY KEY AUTOINCREMENT," +
145145
"module_pkg_name text NOT NULL UNIQUE," +
146146
"apk_path text NOT NULL, " +
147147
"enabled BOOLEAN DEFAULT 0 " +
148-
"CHECK (enabled IN (0, 1))," +
149-
"auto_include BOOLEAN DEFAULT 0 " +
150-
"CHECK (auto_include IN (0, 1))" +
151-
");");
152-
private final SQLiteStatement createScopeTable = db.compileStatement("CREATE TABLE IF NOT EXISTS scope (" +
148+
"CHECK (enabled IN (0, 1))" +
149+
");";
150+
151+
private static final String CREATE_SCOPE_TABLE = "CREATE TABLE IF NOT EXISTS scope (" +
153152
"mid integer," +
154153
"app_pkg_name text NOT NULL," +
155154
"user_id integer NOT NULL," +
@@ -158,8 +157,9 @@ public int hashCode() {
158157
" FOREIGN KEY (mid)" +
159158
" REFERENCES modules (mid)" +
160159
" ON DELETE CASCADE" +
161-
");");
162-
private final SQLiteStatement createConfigTable = db.compileStatement("CREATE TABLE IF NOT EXISTS configs (" +
160+
");";
161+
162+
private static final String CREATE_CONFIG_TABLE = "CREATE TABLE IF NOT EXISTS configs (" +
163163
"module_pkg_name text NOT NULL," +
164164
"user_id integer NOT NULL," +
165165
"`group` text NOT NULL," +
@@ -170,7 +170,7 @@ public int hashCode() {
170170
" FOREIGN KEY (module_pkg_name)" +
171171
" REFERENCES modules (module_pkg_name)" +
172172
" ON DELETE CASCADE" +
173-
");");
173+
");";
174174

175175
private final Map<ProcessScope, List<Module>> cachedScope = new ConcurrentHashMap<>();
176176

@@ -382,74 +382,73 @@ private void executeInTransaction(Runnable execution) {
382382
}
383383

384384
private void initDB() {
385+
db.setForeignKeyConstraintsEnabled(true);
386+
int oldVersion = db.getVersion();
387+
if (oldVersion >= 4) {
388+
// Database is already up to date.
389+
return;
390+
}
391+
392+
Log.i(TAG, "Initializing/Upgrading database from version " + oldVersion + " to 4");
393+
db.beginTransaction();
385394
try {
386-
db.setForeignKeyConstraintsEnabled(true);
387-
switch (db.getVersion()) {
388-
case 0:
389-
executeInTransaction(() -> {
390-
createModulesTable.execute();
391-
createScopeTable.execute();
392-
createConfigTable.execute();
393-
var values = new ContentValues();
394-
values.put("module_pkg_name", "lspd");
395-
values.put("apk_path", ConfigFileManager.managerApkPath.toString());
396-
// dummy module for config
397-
db.insertWithOnConflict("modules", null, values, SQLiteDatabase.CONFLICT_IGNORE);
398-
db.setVersion(1);
399-
});
400-
case 1:
401-
executeInTransaction(() -> {
402-
db.compileStatement("DROP INDEX IF EXISTS configs_idx;").execute();
403-
db.compileStatement("DROP TABLE IF EXISTS config;").execute();
404-
db.compileStatement("ALTER TABLE scope RENAME TO old_scope;").execute();
405-
db.compileStatement("ALTER TABLE configs RENAME TO old_configs;").execute();
406-
createConfigTable.execute();
407-
createScopeTable.execute();
408-
db.compileStatement("CREATE INDEX IF NOT EXISTS configs_idx ON configs (module_pkg_name, user_id);").execute();
409-
executeInTransaction(() -> {
410-
try {
411-
db.compileStatement("INSERT INTO scope SELECT * FROM old_scope;").execute();
412-
} catch (Throwable e) {
413-
Log.w(TAG, "migrate scope", e);
414-
}
415-
});
416-
executeInTransaction(() -> {
417-
try {
418-
executeInTransaction(() -> db.compileStatement("INSERT INTO configs SELECT * FROM old_configs;").execute());
419-
} catch (Throwable e) {
420-
Log.w(TAG, "migrate config", e);
421-
}
422-
});
423-
db.compileStatement("DROP TABLE old_scope;").execute();
424-
db.compileStatement("DROP TABLE old_configs;").execute();
425-
db.setVersion(2);
426-
});
427-
case 2:
428-
executeInTransaction(() -> {
429-
db.compileStatement("UPDATE scope SET app_pkg_name = 'system' WHERE app_pkg_name = 'android';").execute();
430-
db.setVersion(3);
431-
});
432-
case 3:
433-
try {
434-
executeInTransaction(() -> {
435-
db.compileStatement("ALTER TABLE modules ADD COLUMN auto_include BOOLEAN DEFAULT 0 CHECK (auto_include IN (0, 1));").execute();
436-
db.setVersion(4);
437-
});
438-
} catch (SQLiteException ex) {
439-
// Fix wrong init code for new column auto_include
440-
if (ex.getMessage().startsWith("duplicate column name: auto_include")) {
441-
db.setVersion(4);
442-
} else {
443-
throw ex;
444-
}
445-
}
446-
default:
447-
break;
395+
if (oldVersion == 0) {
396+
db.execSQL(CREATE_MODULES_TABLE);
397+
db.execSQL(CREATE_SCOPE_TABLE);
398+
db.execSQL(CREATE_CONFIG_TABLE);
399+
400+
var values = new ContentValues();
401+
values.put("module_pkg_name", "lspd");
402+
values.put("apk_path", ConfigFileManager.managerApkPath.toString());
403+
db.insertWithOnConflict("modules", null, values, SQLiteDatabase.CONFLICT_IGNORE);
404+
oldVersion = 1;
448405
}
406+
if (oldVersion < 2) {
407+
// Upgrade from 1 to 2: Recreate tables to enforce constraints and clean up.
408+
db.compileStatement("DROP INDEX IF EXISTS configs_idx;").execute();
409+
db.compileStatement("DROP TABLE IF EXISTS config;").execute();
410+
db.compileStatement("ALTER TABLE scope RENAME TO old_scope;").execute();
411+
db.compileStatement("ALTER TABLE configs RENAME TO old_configs;").execute();
412+
413+
db.execSQL(CREATE_SCOPE_TABLE);
414+
db.execSQL(CREATE_CONFIG_TABLE);
415+
416+
try {
417+
db.compileStatement("INSERT INTO scope SELECT * FROM old_scope;").execute();
418+
} catch (Throwable e) {
419+
Log.w(TAG, "Failed to migrate scope data", e);
420+
}
421+
try {
422+
db.compileStatement("INSERT INTO configs SELECT * FROM old_configs;").execute();
423+
} catch (Throwable e) {
424+
Log.w(TAG, "Failed to migrate config data", e);
425+
}
426+
427+
db.compileStatement("DROP TABLE old_scope;").execute();
428+
db.compileStatement("DROP TABLE old_configs;").execute();
429+
db.compileStatement("CREATE INDEX IF NOT EXISTS configs_idx ON configs (module_pkg_name, user_id);").execute();
430+
}
431+
if (oldVersion < 3) {
432+
// Upgrade from 2 to 3: Rename 'android' scope to 'system'.
433+
db.compileStatement("UPDATE scope SET app_pkg_name = 'system' WHERE app_pkg_name = 'android';").execute();
434+
}
435+
if (oldVersion < 4) {
436+
// Upgrade from 3 to 4: Add the 'auto_include' column to the modules table.
437+
try {
438+
db.compileStatement("ALTER TABLE modules ADD COLUMN auto_include BOOLEAN DEFAULT 0 CHECK (auto_include IN (0, 1));").execute();
439+
} catch (SQLiteException ex) {
440+
// This might happen if the column already exists from a previous buggy run.
441+
Log.w(TAG, "Could not add auto_include column, it may already exist.", ex);
442+
}
443+
}
444+
db.setVersion(4);
445+
db.setTransactionSuccessful();
446+
Log.i(TAG, "Database upgrade to version 4 successful.");
449447
} catch (Throwable e) {
450-
Log.e(TAG, "init db", e);
448+
Log.e(TAG, "Failed to initialize or upgrade database, transaction rolled back.", e);
449+
} finally {
450+
db.endTransaction();
451451
}
452-
453452
}
454453

455454
private List<ProcessScope> getAssociatedProcesses(Application app) throws RemoteException {

0 commit comments

Comments
 (0)