From 9bd49fc5fc82246f8b6b83e2be4e951ecd7412b0 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Mon, 12 Dec 2022 15:50:23 -0300 Subject: [PATCH] [app] Add ContentProviderMigrator to migrate old repos to new DB --- app/build.gradle | 1 + .../java/org/fdroid/fdroid/FDroidApp.java | 12 +++ .../java/org/fdroid/fdroid/UpdateService.java | 6 +- .../fdroid/data/ContentProviderMigrator.kt | 75 +++++++++++++++++++ .../java/org/fdroid/fdroid/data/DBHelper.java | 10 +++ 5 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/org/fdroid/fdroid/data/ContentProviderMigrator.kt diff --git a/app/build.gradle b/app/build.gradle index 240277e88..c5d26ee70 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' apply plugin: 'checkstyle' apply plugin: 'pmd' diff --git a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java index b40a06cb5..7fd17651e 100644 --- a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java +++ b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java @@ -57,6 +57,7 @@ import org.fdroid.database.Repository; import org.fdroid.fdroid.Preferences.ChangeListener; import org.fdroid.fdroid.Preferences.Theme; import org.fdroid.fdroid.data.App; +import org.fdroid.fdroid.data.ContentProviderMigrator; import org.fdroid.fdroid.data.DBHelper; import org.fdroid.fdroid.installer.ApkFileProvider; import org.fdroid.fdroid.installer.InstallHistoryService; @@ -414,6 +415,17 @@ public class FDroidApp extends Application implements androidx.work.Configuratio if (Preferences.get().isScanRemovableStorageEnabled()) { SDCardScannerService.scan(this); } + + // Migrate repos from old content providers to new Room-based DB. + // Added end of 2022 for alphas, can be removed after sufficient time has passed. + Utils.runOffUiThread(() -> { + ContentProviderMigrator migrator = ContentProviderMigrator.INSTANCE; + if (migrator.needsMigration(this)) { + migrator.migrateOldRepos(this, db); + migrator.removeOldDb(this); + UpdateService.forceUpdateRepo(this); + } + }); } /** diff --git a/app/src/main/java/org/fdroid/fdroid/UpdateService.java b/app/src/main/java/org/fdroid/fdroid/UpdateService.java index 774253815..4e04b26b0 100644 --- a/app/src/main/java/org/fdroid/fdroid/UpdateService.java +++ b/app/src/main/java/org/fdroid/fdroid/UpdateService.java @@ -406,11 +406,7 @@ public class UpdateService extends JobIntentService { } } else if ((manualUpdate || forcedUpdate) && fdroidPrefs.isOnDemandDownloadAllowed()) { Utils.debugLog(TAG, "manually requested or forced update"); - if (forcedUpdate) { - DBHelper.resetRepos(this); - // TODO check if we still need something like this: - // InstalledAppProviderService.compareToPackageManager(this); - } + if (forcedUpdate) DBHelper.resetTransient(this); } else if (!fdroidPrefs.isBackgroundDownloadAllowed() && !fdroidPrefs.isOnDemandDownloadAllowed()) { Utils.debugLog(TAG, "don't run update"); return; diff --git a/app/src/main/java/org/fdroid/fdroid/data/ContentProviderMigrator.kt b/app/src/main/java/org/fdroid/fdroid/data/ContentProviderMigrator.kt new file mode 100644 index 000000000..3189276b1 --- /dev/null +++ b/app/src/main/java/org/fdroid/fdroid/data/ContentProviderMigrator.kt @@ -0,0 +1,75 @@ +package org.fdroid.fdroid.data + +import android.content.Context +import android.database.sqlite.SQLiteDatabase +import android.database.sqlite.SQLiteOpenHelper +import org.fdroid.database.FDroidDatabase +import org.fdroid.database.InitialRepository + +object ContentProviderMigrator { + + private const val OLD_DB_NAME = "fdroid" + + fun needsMigration(context: Context): Boolean { + return context.databaseList().contains(OLD_DB_NAME) + } + + fun migrateOldRepos(context: Context, db: FDroidDatabase) { + val repoDao = db.getRepositoryDao() + val repos = repoDao.getRepositories() + var weight = repos.last().weight + ContentProviderDbHelper(context).readableDatabase.use { oldDb -> + val projection = arrayOf("address", "pubkey", "inuse", "userMirrors", "disabledMirrors") + oldDb.query("fdroid_repo", projection, null, null, null, null, null).use { c -> + while (c.moveToNext()) { + val address = c.getString(c.getColumnIndexOrThrow("address")) + val certificate = c.getString(c.getColumnIndexOrThrow("pubkey")) + ?.lowercase() ?: continue + val enabled = c.getInt(c.getColumnIndexOrThrow("inuse")) == 1 + val userMirrors = c.getString(c.getColumnIndexOrThrow("userMirrors")) + ?.split(',') + val disabledMirrors = c.getString(c.getColumnIndexOrThrow("disabledMirrors")) + ?.split(',') + // find existing repos by address, because F-Droid archive re-uses certificate + val repo = repos.find { it.address == address } + if (repo == null) { // new repo to be added to new DB + val newRepo = InitialRepository( + name = "", + address = address, + description = "", + certificate = certificate, + version = 0, + enabled = enabled, + weight = ++weight, + ) + repoDao.insert(newRepo) + } else { // old repo that may need an update for the new DB + if (repo.certificate != certificate) { + continue // don't update with certificate does not match + } + if (repo.enabled != enabled) { + repoDao.setRepositoryEnabled(repo.repoId, enabled) + } + if (!userMirrors.isNullOrEmpty()) { + repoDao.updateUserMirrors(repo.repoId, userMirrors) + } + if (!disabledMirrors.isNullOrEmpty()) { + repoDao.updateDisabledMirrors(repo.repoId, disabledMirrors) + } + } + } + } + } + } + + fun removeOldDb(context: Context) { + context.deleteDatabase(OLD_DB_NAME) + } + + private class ContentProviderDbHelper( + context: Context, + ) : SQLiteOpenHelper(context, OLD_DB_NAME, null, 85) { + override fun onCreate(db: SQLiteDatabase) {} + override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {} + } +} diff --git a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java index 9c582a492..e0241bac8 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java +++ b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java @@ -81,6 +81,16 @@ public class DBHelper { } } + /** + * Removes all index data related to apps from the DB. + * Leaves repositories, their preferences as well as app preferences in place. + */ + @AnyThread + public static void resetTransient(Context context) { + FDroidDatabase db = getDb(context); + Utils.runOffUiThread(() -> db.getAppDao().clearAll()); + } + @AnyThread public static void resetRepos(Context context) { FDroidDatabase db = getDb(context);