Refactor Storage Plugin API
diff --git a/app/src/androidTest/java/com/stevesoltys/seedvault/PluginTest.kt b/app/src/androidTest/java/com/stevesoltys/seedvault/PluginTest.kt
index 9b4bc46..2045add 100644
--- a/app/src/androidTest/java/com/stevesoltys/seedvault/PluginTest.kt
+++ b/app/src/androidTest/java/com/stevesoltys/seedvault/PluginTest.kt
@@ -3,18 +3,14 @@
 import androidx.test.core.content.pm.PackageInfoBuilder
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.platform.app.InstrumentationRegistry
-import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderBackupPlugin
-import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderFullRestorePlugin
-import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderKVRestorePlugin
-import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderRestorePlugin
+import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
+import com.stevesoltys.seedvault.plugins.StoragePlugin
+import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderStoragePlugin
+import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderLegacyPlugin
 import com.stevesoltys.seedvault.plugins.saf.DocumentsStorage
 import com.stevesoltys.seedvault.plugins.saf.FILE_BACKUP_METADATA
 import com.stevesoltys.seedvault.plugins.saf.deleteContents
 import com.stevesoltys.seedvault.settings.SettingsManager
-import com.stevesoltys.seedvault.transport.backup.BackupPlugin
-import com.stevesoltys.seedvault.transport.restore.FullRestorePlugin
-import com.stevesoltys.seedvault.transport.restore.KVRestorePlugin
-import com.stevesoltys.seedvault.transport.restore.RestorePlugin
 import io.mockk.every
 import io.mockk.mockk
 import kotlinx.coroutines.Dispatchers
@@ -39,14 +35,10 @@
     private val mockedSettingsManager: SettingsManager = mockk()
     private val storage = DocumentsStorage(context, mockedSettingsManager)
 
-    private val backupPlugin: BackupPlugin = DocumentsProviderBackupPlugin(context, storage)
-
-    private val kvRestorePlugin: KVRestorePlugin =
-        DocumentsProviderKVRestorePlugin(context, storage)
-    private val fullRestorePlugin: FullRestorePlugin =
-        DocumentsProviderFullRestorePlugin(context, storage)
-    private val restorePlugin: RestorePlugin =
-        DocumentsProviderRestorePlugin(context, storage, kvRestorePlugin, fullRestorePlugin)
+    private val storagePlugin: StoragePlugin = DocumentsProviderStoragePlugin(context, storage)
+    @Suppress("Deprecation")
+    private val legacyStoragePlugin: LegacyStoragePlugin =
+        DocumentsProviderLegacyPlugin(context, storage)
 
     private val token = System.currentTimeMillis() - 365L * 24L * 60L * 60L * 1000L
     private val packageInfo = PackageInfoBuilder.newBuilder().setPackageName("org.example").build()
@@ -67,7 +59,7 @@
 
     @Test
     fun testProviderPackageName() {
-        assertNotNull(backupPlugin.providerPackageName)
+        assertNotNull(storagePlugin.providerPackageName)
     }
 
     /**
@@ -80,38 +72,38 @@
     @Test
     fun testInitializationAndRestoreSets() = runBlocking(Dispatchers.IO) {
         // no backups available initially
-        assertEquals(0, backupPlugin.getAvailableBackups()?.toList()?.size)
+        assertEquals(0, storagePlugin.getAvailableBackups()?.toList()?.size)
         val uri = settingsManager.getStorage()?.getDocumentFile(context)?.uri ?: error("no storage")
-        assertFalse(backupPlugin.hasBackup(uri))
+        assertFalse(storagePlugin.hasBackup(uri))
 
         // prepare returned tokens requested when initializing device
         every { mockedSettingsManager.getToken() } returnsMany listOf(token, token + 1, token + 1)
 
         // start new restore set and initialize device afterwards
-        backupPlugin.startNewRestoreSet(token)
-        backupPlugin.initializeDevice()
+        storagePlugin.startNewRestoreSet(token)
+        storagePlugin.initializeDevice()
 
         // write metadata (needed for backup to be recognized)
-        backupPlugin.getOutputStream(token, FILE_BACKUP_METADATA)
+        storagePlugin.getOutputStream(token, FILE_BACKUP_METADATA)
             .writeAndClose(getRandomByteArray())
 
         // one backup available now
-        assertEquals(1, backupPlugin.getAvailableBackups()?.toList()?.size)
-        assertTrue(backupPlugin.hasBackup(uri))
+        assertEquals(1, storagePlugin.getAvailableBackups()?.toList()?.size)
+        assertTrue(storagePlugin.hasBackup(uri))
 
         // initializing again (with another restore set) does add a restore set
-        backupPlugin.startNewRestoreSet(token + 1)
-        backupPlugin.initializeDevice()
-        backupPlugin.getOutputStream(token + 1, FILE_BACKUP_METADATA)
+        storagePlugin.startNewRestoreSet(token + 1)
+        storagePlugin.initializeDevice()
+        storagePlugin.getOutputStream(token + 1, FILE_BACKUP_METADATA)
             .writeAndClose(getRandomByteArray())
-        assertEquals(2, backupPlugin.getAvailableBackups()?.toList()?.size)
-        assertTrue(backupPlugin.hasBackup(uri))
+        assertEquals(2, storagePlugin.getAvailableBackups()?.toList()?.size)
+        assertTrue(storagePlugin.hasBackup(uri))
 
         // initializing again (without new restore set) doesn't change number of restore sets
-        backupPlugin.initializeDevice()
-        backupPlugin.getOutputStream(token + 1, FILE_BACKUP_METADATA)
+        storagePlugin.initializeDevice()
+        storagePlugin.getOutputStream(token + 1, FILE_BACKUP_METADATA)
             .writeAndClose(getRandomByteArray())
-        assertEquals(2, backupPlugin.getAvailableBackups()?.toList()?.size)
+        assertEquals(2, storagePlugin.getAvailableBackups()?.toList()?.size)
 
         // ensure that the new backup dir exist
         assertTrue(storage.currentSetDir!!.exists())
@@ -121,15 +113,15 @@
     fun testMetadataWriteRead() = runBlocking(Dispatchers.IO) {
         every { mockedSettingsManager.getToken() } returns token
 
-        backupPlugin.startNewRestoreSet(token)
-        backupPlugin.initializeDevice()
+        storagePlugin.startNewRestoreSet(token)
+        storagePlugin.initializeDevice()
 
         // write metadata
         val metadata = getRandomByteArray()
-        backupPlugin.getOutputStream(token, FILE_BACKUP_METADATA).writeAndClose(metadata)
+        storagePlugin.getOutputStream(token, FILE_BACKUP_METADATA).writeAndClose(metadata)
 
         // get available backups, expect only one with our token and no error
-        var availableBackups = backupPlugin.getAvailableBackups()?.toList()
+        var availableBackups = storagePlugin.getAvailableBackups()?.toList()
         check(availableBackups != null)
         assertEquals(1, availableBackups.size)
         assertEquals(token, availableBackups[0].token)
@@ -138,9 +130,9 @@
         assertReadEquals(metadata, availableBackups[0].inputStreamRetriever())
 
         // initializing again (without changing storage) keeps restore set with same token
-        backupPlugin.initializeDevice()
-        backupPlugin.getOutputStream(token, FILE_BACKUP_METADATA).writeAndClose(metadata)
-        availableBackups = backupPlugin.getAvailableBackups()?.toList()
+        storagePlugin.initializeDevice()
+        storagePlugin.getOutputStream(token, FILE_BACKUP_METADATA).writeAndClose(metadata)
+        availableBackups = storagePlugin.getAvailableBackups()?.toList()
         check(availableBackups != null)
         assertEquals(1, availableBackups.size)
         assertEquals(token, availableBackups[0].token)
@@ -157,22 +149,25 @@
 
         // write random bytes as APK
         val apk1 = getRandomByteArray(1337 * 1024)
-        backupPlugin.getOutputStream(token, "${packageInfo.packageName}.apk").writeAndClose(apk1)
+        storagePlugin.getOutputStream(token, "${packageInfo.packageName}.apk").writeAndClose(apk1)
 
         // assert that read APK bytes match what was written
-        assertReadEquals(apk1, restorePlugin.getApkInputStream(token, packageInfo.packageName, ""))
+        assertReadEquals(
+            apk1,
+            legacyStoragePlugin.getApkInputStream(token, packageInfo.packageName, "")
+        )
 
         // write random bytes as another APK
         val suffix2 = getRandomBase64(23)
         val apk2 = getRandomByteArray(23 * 1024 * 1024)
 
-        backupPlugin.getOutputStream(token, "${packageInfo2.packageName}$suffix2.apk")
+        storagePlugin.getOutputStream(token, "${packageInfo2.packageName}$suffix2.apk")
             .writeAndClose(apk2)
 
         // assert that read APK bytes match what was written
         assertReadEquals(
             apk2,
-            restorePlugin.getApkInputStream(token, packageInfo2.packageName, suffix2)
+            legacyStoragePlugin.getApkInputStream(token, packageInfo2.packageName, suffix2)
         )
     }
 
@@ -185,41 +180,41 @@
         val name2 = getRandomBase64()
 
         // no data available initially
-        assertFalse(backupPlugin.hasData(token, name1))
-        assertFalse(backupPlugin.hasData(token, name2))
+        assertFalse(storagePlugin.hasData(token, name1))
+        assertFalse(storagePlugin.hasData(token, name2))
 
         // write full backup data
         val data = getRandomByteArray(5 * 1024 * 1024)
-        backupPlugin.getOutputStream(token, name1).writeAndClose(data)
+        storagePlugin.getOutputStream(token, name1).writeAndClose(data)
 
         // data is available now, but only this token
-        assertTrue(backupPlugin.hasData(token, name1))
-        assertFalse(backupPlugin.hasData(token + 1, name1))
+        assertTrue(storagePlugin.hasData(token, name1))
+        assertFalse(storagePlugin.hasData(token + 1, name1))
 
         // restore data matches backed up data
-        assertReadEquals(data, backupPlugin.getInputStream(token, name1))
+        assertReadEquals(data, storagePlugin.getInputStream(token, name1))
 
         // write and check data for second package
         val data2 = getRandomByteArray(5 * 1024 * 1024)
-        backupPlugin.getOutputStream(token, name2).writeAndClose(data2)
-        assertTrue(backupPlugin.hasData(token, name2))
-        assertReadEquals(data2, backupPlugin.getInputStream(token, name2))
+        storagePlugin.getOutputStream(token, name2).writeAndClose(data2)
+        assertTrue(storagePlugin.hasData(token, name2))
+        assertReadEquals(data2, storagePlugin.getInputStream(token, name2))
 
         // remove data of first package again and ensure that no more data is found
-        backupPlugin.removeData(token, name1)
-        assertFalse(backupPlugin.hasData(token, name1))
+        storagePlugin.removeData(token, name1)
+        assertFalse(storagePlugin.hasData(token, name1))
 
         // second package is still there
-        assertTrue(backupPlugin.hasData(token, name2))
+        assertTrue(storagePlugin.hasData(token, name2))
 
         // ensure that it gets deleted as well
-        backupPlugin.removeData(token, name2)
-        assertFalse(backupPlugin.hasData(token, name2))
+        storagePlugin.removeData(token, name2)
+        assertFalse(storagePlugin.hasData(token, name2))
     }
 
     private fun initStorage(token: Long) = runBlocking {
         every { mockedSettingsManager.getToken() } returns token
-        backupPlugin.initializeDevice()
+        storagePlugin.initializeDevice()
     }
 
 }
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/KVRestorePlugin.kt b/app/src/main/java/com/stevesoltys/seedvault/plugins/LegacyStoragePlugin.kt
similarity index 61%
rename from app/src/main/java/com/stevesoltys/seedvault/transport/restore/KVRestorePlugin.kt
rename to app/src/main/java/com/stevesoltys/seedvault/plugins/LegacyStoragePlugin.kt
index 6cdd18c..1dbf797 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/KVRestorePlugin.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/plugins/LegacyStoragePlugin.kt
@@ -1,10 +1,11 @@
-package com.stevesoltys.seedvault.transport.restore
+package com.stevesoltys.seedvault.plugins
 
 import android.content.pm.PackageInfo
 import java.io.IOException
 import java.io.InputStream
 
-interface KVRestorePlugin {
+@Deprecated("Only for old v0 backup format")
+interface LegacyStoragePlugin {
 
     /**
      * Return true if there is data stored for the given package.
@@ -36,4 +37,19 @@
         key: String
     ): InputStream
 
+    /**
+     * Return true if there is data stored for the given package.
+     */
+    @Throws(IOException::class)
+    suspend fun hasDataForFullPackage(token: Long, packageInfo: PackageInfo): Boolean
+
+    @Throws(IOException::class)
+    suspend fun getInputStreamForPackage(token: Long, packageInfo: PackageInfo): InputStream
+
+    /**
+     * Returns an [InputStream] for the given token, for reading an APK that is to be restored.
+     */
+    @Throws(IOException::class)
+    suspend fun getApkInputStream(token: Long, packageName: String, suffix: String): InputStream
+
 }
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupPlugin.kt b/app/src/main/java/com/stevesoltys/seedvault/plugins/StoragePlugin.kt
similarity index 96%
rename from app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupPlugin.kt
rename to app/src/main/java/com/stevesoltys/seedvault/plugins/StoragePlugin.kt
index 5157bd1..5ba5734 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupPlugin.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/plugins/StoragePlugin.kt
@@ -1,4 +1,4 @@
-package com.stevesoltys.seedvault.transport.backup
+package com.stevesoltys.seedvault.plugins
 
 import android.app.backup.RestoreSet
 import android.net.Uri
@@ -7,7 +7,7 @@
 import java.io.InputStream
 import java.io.OutputStream
 
-interface BackupPlugin {
+interface StoragePlugin {
 
     /**
      * Start a new [RestoreSet] with the given token.
diff --git a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderFullRestorePlugin.kt b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderFullRestorePlugin.kt
deleted file mode 100644
index 639dd03..0000000
--- a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderFullRestorePlugin.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.stevesoltys.seedvault.plugins.saf
-
-import android.content.Context
-import android.content.pm.PackageInfo
-import com.stevesoltys.seedvault.transport.restore.FullRestorePlugin
-import java.io.IOException
-import java.io.InputStream
-
-@Suppress("BlockingMethodInNonBlockingContext", "Deprecation")
-@Deprecated("Use only for v0 restore")
-internal class DocumentsProviderFullRestorePlugin(
-    private val context: Context,
-    private val documentsStorage: DocumentsStorage
-) : FullRestorePlugin {
-
-    @Throws(IOException::class)
-    override suspend fun hasDataForPackage(token: Long, packageInfo: PackageInfo): Boolean {
-        val backupDir = documentsStorage.getFullBackupDir(token) ?: return false
-        return backupDir.findFileBlocking(context, packageInfo.packageName) != null
-    }
-
-    @Throws(IOException::class)
-    override suspend fun getInputStreamForPackage(
-        token: Long,
-        packageInfo: PackageInfo
-    ): InputStream {
-        val backupDir = documentsStorage.getFullBackupDir(token) ?: throw IOException()
-        val packageFile =
-            backupDir.findFileBlocking(context, packageInfo.packageName) ?: throw IOException()
-        return documentsStorage.getInputStream(packageFile)
-    }
-
-}
diff --git a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderKVRestorePlugin.kt b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderLegacyPlugin.kt
similarity index 60%
rename from app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderKVRestorePlugin.kt
rename to app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderLegacyPlugin.kt
index 76e4c25..8e5d095 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderKVRestorePlugin.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderLegacyPlugin.kt
@@ -2,17 +2,19 @@
 
 import android.content.Context
 import android.content.pm.PackageInfo
+import androidx.annotation.WorkerThread
 import androidx.documentfile.provider.DocumentFile
-import com.stevesoltys.seedvault.transport.restore.KVRestorePlugin
+import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
+import java.io.FileNotFoundException
 import java.io.IOException
 import java.io.InputStream
 
-@Suppress("BlockingMethodInNonBlockingContext", "Deprecation")
-@Deprecated("Use only for v0 restore")
-internal class DocumentsProviderKVRestorePlugin(
+@WorkerThread
+@Suppress("BlockingMethodInNonBlockingContext", "Deprecation") // all methods do I/O
+internal class DocumentsProviderLegacyPlugin(
     private val context: Context,
     private val storage: DocumentsStorage
-) : KVRestorePlugin {
+) : LegacyStoragePlugin {
 
     private var packageDir: DocumentFile? = null
     private var packageChildren: List<DocumentFile>? = null
@@ -59,4 +61,33 @@
         return storage.getInputStream(keyFile)
     }
 
+    @Throws(IOException::class)
+    override suspend fun hasDataForFullPackage(token: Long, packageInfo: PackageInfo): Boolean {
+        val backupDir = storage.getFullBackupDir(token) ?: return false
+        return backupDir.findFileBlocking(context, packageInfo.packageName) != null
+    }
+
+    @Throws(IOException::class)
+    override suspend fun getInputStreamForPackage(
+        token: Long,
+        packageInfo: PackageInfo
+    ): InputStream {
+        val backupDir = storage.getFullBackupDir(token) ?: throw IOException()
+        val packageFile =
+            backupDir.findFileBlocking(context, packageInfo.packageName) ?: throw IOException()
+        return storage.getInputStream(packageFile)
+    }
+
+    @Throws(IOException::class)
+    override suspend fun getApkInputStream(
+        token: Long,
+        packageName: String,
+        suffix: String
+    ): InputStream {
+        val setDir = storage.getSetDir(token) ?: throw IOException()
+        val file = setDir.findFileBlocking(context, "$packageName$suffix.apk")
+            ?: throw FileNotFoundException()
+        return storage.getInputStream(file)
+    }
+
 }
diff --git a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderModule.kt b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderModule.kt
index b3ff0d4..638ee99 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderModule.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderModule.kt
@@ -1,18 +1,14 @@
 package com.stevesoltys.seedvault.plugins.saf
 
-import com.stevesoltys.seedvault.transport.backup.BackupPlugin
-import com.stevesoltys.seedvault.transport.restore.FullRestorePlugin
-import com.stevesoltys.seedvault.transport.restore.KVRestorePlugin
-import com.stevesoltys.seedvault.transport.restore.RestorePlugin
+import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import org.koin.android.ext.koin.androidContext
 import org.koin.dsl.module
 
 val documentsProviderModule = module {
     single { DocumentsStorage(androidContext(), get()) }
 
-    single<BackupPlugin> { DocumentsProviderBackupPlugin(androidContext(), get()) }
-
-    single<KVRestorePlugin> { DocumentsProviderKVRestorePlugin(androidContext(), get()) }
-    single<FullRestorePlugin> { DocumentsProviderFullRestorePlugin(androidContext(), get()) }
-    single<RestorePlugin> { DocumentsProviderRestorePlugin(androidContext(), get(), get(), get()) }
+    single<StoragePlugin> { DocumentsProviderStoragePlugin(androidContext(), get()) }
+    @Suppress("Deprecation")
+    single<LegacyStoragePlugin> { DocumentsProviderLegacyPlugin(androidContext(), get()) }
 }
diff --git a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderRestorePlugin.kt b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderRestorePlugin.kt
deleted file mode 100644
index b701aa5..0000000
--- a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderRestorePlugin.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.stevesoltys.seedvault.plugins.saf
-
-import android.content.Context
-import androidx.annotation.WorkerThread
-import com.stevesoltys.seedvault.transport.restore.FullRestorePlugin
-import com.stevesoltys.seedvault.transport.restore.KVRestorePlugin
-import com.stevesoltys.seedvault.transport.restore.RestorePlugin
-import java.io.FileNotFoundException
-import java.io.IOException
-import java.io.InputStream
-
-@WorkerThread
-@Suppress("BlockingMethodInNonBlockingContext") // all methods do I/O
-internal class DocumentsProviderRestorePlugin(
-    private val context: Context,
-    private val storage: DocumentsStorage,
-    override val kvRestorePlugin: KVRestorePlugin,
-    override val fullRestorePlugin: FullRestorePlugin
-) : RestorePlugin {
-
-    @Throws(IOException::class)
-    override suspend fun getApkInputStream(
-        token: Long,
-        packageName: String,
-        suffix: String
-    ): InputStream {
-        val setDir = storage.getSetDir(token) ?: throw IOException()
-        val file = setDir.findFileBlocking(context, "$packageName$suffix.apk")
-            ?: throw FileNotFoundException()
-        return storage.getInputStream(file)
-    }
-
-}
diff --git a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderBackupPlugin.kt b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderStoragePlugin.kt
similarity index 95%
rename from app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderBackupPlugin.kt
rename to app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderStoragePlugin.kt
index 7f64a62..ee15097 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderBackupPlugin.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderStoragePlugin.kt
@@ -5,20 +5,20 @@
 import android.net.Uri
 import android.util.Log
 import androidx.documentfile.provider.DocumentFile
-import com.stevesoltys.seedvault.transport.backup.BackupPlugin
-import com.stevesoltys.seedvault.transport.backup.EncryptedMetadata
+import com.stevesoltys.seedvault.plugins.EncryptedMetadata
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import java.io.FileNotFoundException
 import java.io.IOException
 import java.io.InputStream
 import java.io.OutputStream
 
-private val TAG = DocumentsProviderBackupPlugin::class.java.simpleName
+private val TAG = DocumentsProviderStoragePlugin::class.java.simpleName
 
 @Suppress("BlockingMethodInNonBlockingContext")
-internal class DocumentsProviderBackupPlugin(
+internal class DocumentsProviderStoragePlugin(
     private val context: Context,
     private val storage: DocumentsStorage
-) : BackupPlugin {
+) : StoragePlugin {
 
     private val packageManager: PackageManager = context.packageManager
 
diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/install/ApkRestore.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/install/ApkRestore.kt
index dcc4165..6ff73c5 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/restore/install/ApkRestore.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/restore/install/ApkRestore.kt
@@ -8,16 +8,16 @@
 import com.stevesoltys.seedvault.crypto.Crypto
 import com.stevesoltys.seedvault.metadata.ApkSplit
 import com.stevesoltys.seedvault.metadata.PackageMetadata
+import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import com.stevesoltys.seedvault.restore.RestorableBackup
 import com.stevesoltys.seedvault.restore.install.ApkInstallState.FAILED_SYSTEM_APP
 import com.stevesoltys.seedvault.restore.install.ApkInstallState.IN_PROGRESS
 import com.stevesoltys.seedvault.restore.install.ApkInstallState.QUEUED
 import com.stevesoltys.seedvault.restore.install.ApkInstallState.SUCCEEDED
-import com.stevesoltys.seedvault.transport.backup.BackupPlugin
 import com.stevesoltys.seedvault.transport.backup.copyStreamsAndGetHash
 import com.stevesoltys.seedvault.transport.backup.getSignatures
 import com.stevesoltys.seedvault.transport.backup.isSystemApp
-import com.stevesoltys.seedvault.transport.restore.RestorePlugin
 import kotlinx.coroutines.TimeoutCancellationException
 import kotlinx.coroutines.flow.FlowCollector
 import kotlinx.coroutines.flow.flow
@@ -28,8 +28,9 @@
 
 internal class ApkRestore(
     private val context: Context,
-    private val backupPlugin: BackupPlugin,
-    private val restorePlugin: RestorePlugin,
+    private val storagePlugin: StoragePlugin,
+    @Suppress("Deprecation")
+    private val legacyStoragePlugin: LegacyStoragePlugin,
     private val crypto: Crypto,
     private val splitCompatChecker: ApkSplitCompatibilityChecker,
     private val apkInstaller: ApkInstaller
@@ -157,7 +158,7 @@
     }
 
     /**
-     * Retrieves APK splits from [RestorePlugin] and caches them locally.
+     * Retrieves APK splits from [StoragePlugin] and caches them locally.
      *
      * @throws SecurityException if a split has an unexpected SHA-256 hash.
      * @return a list of all APKs that need to be installed
@@ -195,7 +196,7 @@
     }
 
     /**
-     * Retrieves an APK from the [RestorePlugin] and caches it locally
+     * Retrieves an APK from the [StoragePlugin] and caches it locally
      * while calculating its SHA-256 hash.
      *
      * @return a [Pair] of the cached [File] and SHA-256 hash.
@@ -214,10 +215,10 @@
         // copy APK to cache file and calculate SHA-256 hash while we are at it
         val inputStream = if (version == 0.toByte()) {
             @Suppress("Deprecation")
-            restorePlugin.getApkInputStream(token, packageName, suffix)
+            legacyStoragePlugin.getApkInputStream(token, packageName, suffix)
         } else {
             val name = crypto.getNameForApk(salt, packageName, suffix)
-            backupPlugin.getInputStream(token, name)
+            storagePlugin.getInputStream(token, name)
         }
         val sha256 = copyStreamsAndGetHash(inputStream, cachedApk.outputStream())
         return Pair(cachedApk, sha256)
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinator.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinator.kt
index 7e19d9d..17a6654 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinator.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinator.kt
@@ -29,6 +29,7 @@
 import com.stevesoltys.seedvault.metadata.PackageState.QUOTA_EXCEEDED
 import com.stevesoltys.seedvault.metadata.PackageState.UNKNOWN_ERROR
 import com.stevesoltys.seedvault.metadata.PackageState.WAS_STOPPED
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import com.stevesoltys.seedvault.plugins.saf.FILE_BACKUP_METADATA
 import com.stevesoltys.seedvault.settings.SettingsManager
 import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
@@ -64,7 +65,7 @@
 @Suppress("BlockingMethodInNonBlockingContext")
 internal class BackupCoordinator(
     private val context: Context,
-    private val plugin: BackupPlugin,
+    private val plugin: StoragePlugin,
     private val kv: KVBackup,
     private val full: FullBackup,
     private val apkBackup: ApkBackup,
@@ -508,7 +509,7 @@
         }
     }
 
-    private suspend fun BackupPlugin.getMetadataOutputStream(token: Long? = null): OutputStream {
+    private suspend fun StoragePlugin.getMetadataOutputStream(token: Long? = null): OutputStream {
         val t = token ?: settingsManager.getToken() ?: throw IOException("no current token")
         return getOutputStream(t, FILE_BACKUP_METADATA)
     }
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/FullBackup.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/FullBackup.kt
index e14899b..06ebbba 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/FullBackup.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/FullBackup.kt
@@ -11,6 +11,7 @@
 import com.stevesoltys.seedvault.crypto.Crypto
 import com.stevesoltys.seedvault.header.VERSION
 import com.stevesoltys.seedvault.header.getADForFull
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import com.stevesoltys.seedvault.settings.SettingsManager
 import libcore.io.IoUtils.closeQuietly
 import java.io.EOFException
@@ -38,7 +39,7 @@
 
 @Suppress("BlockingMethodInNonBlockingContext")
 internal class FullBackup(
-    private val plugin: BackupPlugin,
+    private val plugin: StoragePlugin,
     private val settingsManager: SettingsManager,
     private val inputFactory: InputFactory,
     private val crypto: Crypto
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/KVBackup.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/KVBackup.kt
index 9d32ca0..50e5ed7 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/KVBackup.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/KVBackup.kt
@@ -12,6 +12,7 @@
 import com.stevesoltys.seedvault.crypto.Crypto
 import com.stevesoltys.seedvault.header.VERSION
 import com.stevesoltys.seedvault.header.getADForKV
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import com.stevesoltys.seedvault.settings.SettingsManager
 import java.io.IOException
 import java.util.zip.GZIPOutputStream
@@ -31,7 +32,7 @@
 
 @Suppress("BlockingMethodInNonBlockingContext")
 internal class KVBackup(
-    private val plugin: BackupPlugin,
+    private val plugin: StoragePlugin,
     private val settingsManager: SettingsManager,
     private val inputFactory: InputFactory,
     private val crypto: Crypto,
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/FullRestore.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/FullRestore.kt
index 67fc1da..2828971 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/FullRestore.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/FullRestore.kt
@@ -12,7 +12,8 @@
 import com.stevesoltys.seedvault.header.MAX_SEGMENT_LENGTH
 import com.stevesoltys.seedvault.header.UnsupportedVersionException
 import com.stevesoltys.seedvault.header.getADForFull
-import com.stevesoltys.seedvault.transport.backup.BackupPlugin
+import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import libcore.io.IoUtils.closeQuietly
 import java.io.EOFException
 import java.io.IOException
@@ -33,8 +34,9 @@
 
 @Suppress("BlockingMethodInNonBlockingContext")
 internal class FullRestore(
-    private val plugin: BackupPlugin,
-    private val legacyPlugin: FullRestorePlugin,
+    private val plugin: StoragePlugin,
+    @Suppress("Deprecation")
+    private val legacyPlugin: LegacyStoragePlugin,
     private val outputFactory: OutputFactory,
     private val headerReader: HeaderReader,
     private val crypto: Crypto
@@ -52,7 +54,7 @@
     @Throws(IOException::class)
     @Deprecated("Use BackupPlugin#hasData() instead")
     suspend fun hasDataForPackage(token: Long, packageInfo: PackageInfo): Boolean {
-        return legacyPlugin.hasDataForPackage(token, packageInfo)
+        return legacyPlugin.hasDataForFullPackage(token, packageInfo)
     }
 
     /**
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/FullRestorePlugin.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/FullRestorePlugin.kt
deleted file mode 100644
index dacd0e0..0000000
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/FullRestorePlugin.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.stevesoltys.seedvault.transport.restore
-
-import android.content.pm.PackageInfo
-import java.io.IOException
-import java.io.InputStream
-
-interface FullRestorePlugin {
-
-    /**
-     * Return true if there is data stored for the given package.
-     */
-    @Throws(IOException::class)
-    suspend fun hasDataForPackage(token: Long, packageInfo: PackageInfo): Boolean
-
-    @Throws(IOException::class)
-    suspend fun getInputStreamForPackage(token: Long, packageInfo: PackageInfo): InputStream
-
-}
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/KVRestore.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/KVRestore.kt
index 7af153f..520b564 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/KVRestore.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/KVRestore.kt
@@ -15,7 +15,8 @@
 import com.stevesoltys.seedvault.header.UnsupportedVersionException
 import com.stevesoltys.seedvault.header.VERSION
 import com.stevesoltys.seedvault.header.getADForKV
-import com.stevesoltys.seedvault.transport.backup.BackupPlugin
+import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import com.stevesoltys.seedvault.transport.backup.KVDb
 import com.stevesoltys.seedvault.transport.backup.KvDbManager
 import libcore.io.IoUtils.closeQuietly
@@ -41,8 +42,9 @@
 
 @Suppress("BlockingMethodInNonBlockingContext")
 internal class KVRestore(
-    private val plugin: BackupPlugin,
-    private val legacyPlugin: KVRestorePlugin,
+    private val plugin: StoragePlugin,
+    @Suppress("Deprecation")
+    private val legacyPlugin: LegacyStoragePlugin,
     private val outputFactory: OutputFactory,
     private val headerReader: HeaderReader,
     private val crypto: Crypto,
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinator.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinator.kt
index 586a846..d73e738 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinator.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinator.kt
@@ -21,7 +21,7 @@
 import com.stevesoltys.seedvault.metadata.MetadataManager
 import com.stevesoltys.seedvault.metadata.MetadataReader
 import com.stevesoltys.seedvault.settings.SettingsManager
-import com.stevesoltys.seedvault.transport.backup.BackupPlugin
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
 import java.io.IOException
 
@@ -46,7 +46,7 @@
     private val settingsManager: SettingsManager,
     private val metadataManager: MetadataManager,
     private val notificationManager: BackupNotificationManager,
-    private val plugin: BackupPlugin,
+    private val plugin: StoragePlugin,
     private val kv: KVRestore,
     private val full: FullRestore,
     private val metadataReader: MetadataReader
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreModule.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreModule.kt
index 7ef3c4c..7ea9248 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreModule.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreModule.kt
@@ -5,8 +5,8 @@
 
 val restoreModule = module {
     single { OutputFactory() }
-    single { KVRestore(get(), get<RestorePlugin>().kvRestorePlugin, get(), get(), get(), get()) }
-    single { FullRestore(get(), get<RestorePlugin>().fullRestorePlugin, get(), get(), get()) }
+    single { KVRestore(get(), get(), get(), get(), get(), get()) }
+    single { FullRestore(get(), get(), get(), get(), get()) }
     single {
         RestoreCoordinator(androidContext(), get(), get(), get(), get(), get(), get(), get(), get())
     }
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestorePlugin.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestorePlugin.kt
deleted file mode 100644
index 6fa75cd..0000000
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestorePlugin.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.stevesoltys.seedvault.transport.restore
-
-import java.io.IOException
-import java.io.InputStream
-
-interface RestorePlugin {
-
-    val kvRestorePlugin: KVRestorePlugin
-
-    val fullRestorePlugin: FullRestorePlugin
-
-    /**
-     * Returns an [InputStream] for the given token, for reading an APK that is to be restored.
-     */
-    @Throws(IOException::class)
-    @Deprecated("Use only for v0 restores")
-    suspend fun getApkInputStream(token: Long, packageName: String, suffix: String): InputStream
-
-}
diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/storage/RestoreStorageViewModel.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/storage/RestoreStorageViewModel.kt
index 330567a..05b17db 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/ui/storage/RestoreStorageViewModel.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/ui/storage/RestoreStorageViewModel.kt
@@ -7,7 +7,7 @@
 import com.stevesoltys.seedvault.R
 import com.stevesoltys.seedvault.plugins.saf.DIRECTORY_ROOT
 import com.stevesoltys.seedvault.settings.SettingsManager
-import com.stevesoltys.seedvault.transport.backup.BackupPlugin
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import java.io.IOException
@@ -16,7 +16,7 @@
 
 internal class RestoreStorageViewModel(
     private val app: Application,
-    private val backupPlugin: BackupPlugin,
+    private val storagePlugin: StoragePlugin,
     settingsManager: SettingsManager
 ) : StorageViewModel(app, settingsManager) {
 
@@ -25,7 +25,7 @@
     override fun onLocationSet(uri: Uri) {
         viewModelScope.launch(Dispatchers.IO) {
             val hasBackup = try {
-                backupPlugin.hasBackup(uri)
+                storagePlugin.hasBackup(uri)
             } catch (e: IOException) {
                 Log.e(TAG, "Error reading URI: $uri", e)
                 false
diff --git a/app/src/test/java/com/stevesoltys/seedvault/plugins/saf/BackupPluginTest.kt b/app/src/test/java/com/stevesoltys/seedvault/plugins/saf/StoragePluginTest.kt
similarity index 92%
rename from app/src/test/java/com/stevesoltys/seedvault/plugins/saf/BackupPluginTest.kt
rename to app/src/test/java/com/stevesoltys/seedvault/plugins/saf/StoragePluginTest.kt
index fa8b2e5..c4a1e07 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/plugins/saf/BackupPluginTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/plugins/saf/StoragePluginTest.kt
@@ -12,11 +12,11 @@
 import org.junit.jupiter.api.Test
 
 @Suppress("BlockingMethodInNonBlockingContext")
-internal class BackupPluginTest : BackupTest() {
+internal class StoragePluginTest : BackupTest() {
 
     private val storage = mockk<DocumentsStorage>()
 
-    private val plugin = DocumentsProviderBackupPlugin(context, storage)
+    private val plugin = DocumentsProviderStoragePlugin(context, storage)
 
     private val setDir: DocumentFile = mockk()
     private val backupFile: DocumentFile = mockk()
diff --git a/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkBackupRestoreTest.kt b/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkBackupRestoreTest.kt
index 981ace2..7f3bbf4 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkBackupRestoreTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkBackupRestoreTest.kt
@@ -11,11 +11,11 @@
 import com.stevesoltys.seedvault.metadata.PackageMetadata
 import com.stevesoltys.seedvault.metadata.PackageMetadataMap
 import com.stevesoltys.seedvault.metadata.PackageState
+import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import com.stevesoltys.seedvault.restore.RestorableBackup
 import com.stevesoltys.seedvault.transport.TransportTest
 import com.stevesoltys.seedvault.transport.backup.ApkBackup
-import com.stevesoltys.seedvault.transport.backup.BackupPlugin
-import com.stevesoltys.seedvault.transport.restore.RestorePlugin
 import io.mockk.coEvery
 import io.mockk.every
 import io.mockk.mockk
@@ -46,16 +46,17 @@
     private val strictContext: Context = mockk<Context>().apply {
         every { packageManager } returns pm
     }
-    private val backupPlugin: BackupPlugin = mockk()
-    private val restorePlugin: RestorePlugin = mockk()
+    @Suppress("Deprecation")
+    private val legacyStoragePlugin: LegacyStoragePlugin = mockk()
+    private val storagePlugin: StoragePlugin = mockk()
     private val splitCompatChecker: ApkSplitCompatibilityChecker = mockk()
     private val apkInstaller: ApkInstaller = mockk()
 
     private val apkBackup = ApkBackup(pm, crypto, settingsManager, metadataManager)
     private val apkRestore: ApkRestore = ApkRestore(
         context = strictContext,
-        backupPlugin = backupPlugin,
-        restorePlugin = restorePlugin,
+        storagePlugin = storagePlugin,
+        legacyStoragePlugin = legacyStoragePlugin,
         crypto = crypto,
         splitCompatChecker = splitCompatChecker,
         apkInstaller = apkInstaller
@@ -129,7 +130,7 @@
 
         every { strictContext.cacheDir } returns tmpFile
         every { crypto.getNameForApk(salt, packageName, "") } returns name
-        coEvery { backupPlugin.getInputStream(token, name) } returns inputStream
+        coEvery { storagePlugin.getInputStream(token, name) } returns inputStream
         every { pm.getPackageArchiveInfo(capture(apkPath), any()) } returns packageInfo
         every {
             @Suppress("UNRESOLVED_REFERENCE")
@@ -143,7 +144,7 @@
             splitCompatChecker.isCompatible(metadata.deviceName, listOf(splitName))
         } returns true
         every { crypto.getNameForApk(salt, packageName, splitName) } returns suffixName
-        coEvery { backupPlugin.getInputStream(token, suffixName) } returns splitInputStream
+        coEvery { storagePlugin.getInputStream(token, suffixName) } returns splitInputStream
         coEvery {
             apkInstaller.install(capture(cacheFiles), packageName, installerName, any())
         } returns MutableInstallResult(1).apply {
diff --git a/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkRestoreTest.kt b/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkRestoreTest.kt
index 6f61608..86be958 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkRestoreTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkRestoreTest.kt
@@ -14,6 +14,8 @@
 import com.stevesoltys.seedvault.metadata.ApkSplit
 import com.stevesoltys.seedvault.metadata.PackageMetadata
 import com.stevesoltys.seedvault.metadata.PackageMetadataMap
+import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import com.stevesoltys.seedvault.restore.RestorableBackup
 import com.stevesoltys.seedvault.restore.install.ApkInstallState.FAILED
 import com.stevesoltys.seedvault.restore.install.ApkInstallState.FAILED_SYSTEM_APP
@@ -21,8 +23,6 @@
 import com.stevesoltys.seedvault.restore.install.ApkInstallState.QUEUED
 import com.stevesoltys.seedvault.restore.install.ApkInstallState.SUCCEEDED
 import com.stevesoltys.seedvault.transport.TransportTest
-import com.stevesoltys.seedvault.transport.backup.BackupPlugin
-import com.stevesoltys.seedvault.transport.restore.RestorePlugin
 import io.mockk.coEvery
 import io.mockk.every
 import io.mockk.mockk
@@ -50,15 +50,15 @@
     private val strictContext: Context = mockk<Context>().apply {
         every { packageManager } returns pm
     }
-    private val backupPlugin: BackupPlugin = mockk()
-    private val restorePlugin: RestorePlugin = mockk()
+    private val storagePlugin: StoragePlugin = mockk()
+    private val legacyStoragePlugin: LegacyStoragePlugin = mockk()
     private val splitCompatChecker: ApkSplitCompatibilityChecker = mockk()
     private val apkInstaller: ApkInstaller = mockk()
 
     private val apkRestore: ApkRestore = ApkRestore(
         strictContext,
-        backupPlugin,
-        restorePlugin,
+        storagePlugin,
+        legacyStoragePlugin,
         crypto,
         splitCompatChecker,
         apkInstaller
@@ -96,7 +96,7 @@
 
         every { strictContext.cacheDir } returns File(tmpDir.toString())
         every { crypto.getNameForApk(salt, packageName, "") } returns name
-        coEvery { backupPlugin.getInputStream(token, name) } returns apkInputStream
+        coEvery { storagePlugin.getInputStream(token, name) } returns apkInputStream
 
         apkRestore.restore(backup).collectIndexed { i, value ->
             assertQueuedFailFinished(i, value)
@@ -110,7 +110,7 @@
 
         every { strictContext.cacheDir } returns File(tmpDir.toString())
         every { crypto.getNameForApk(salt, packageName, "") } returns name
-        coEvery { backupPlugin.getInputStream(token, name) } returns apkInputStream
+        coEvery { storagePlugin.getInputStream(token, name) } returns apkInputStream
         every { pm.getPackageArchiveInfo(any(), any()) } returns packageInfo
 
         apkRestore.restore(backup).collectIndexed { i, value ->
@@ -169,7 +169,9 @@
 
         every { strictContext.cacheDir } returns File(tmpDir.toString())
         @Suppress("Deprecation")
-        coEvery { restorePlugin.getApkInputStream(token, packageName, "") } returns apkInputStream
+        coEvery {
+            legacyStoragePlugin.getApkInputStream(token, packageName, "")
+        } returns apkInputStream
         every { pm.getPackageArchiveInfo(any(), any()) } returns packageInfo
         every {
             @Suppress("UNRESOLVED_REFERENCE")
@@ -299,7 +301,7 @@
         every { splitCompatChecker.isCompatible(deviceName, listOf(splitName)) } returns true
         every { crypto.getNameForApk(salt, packageName, splitName) } returns suffixName
         coEvery {
-            backupPlugin.getInputStream(token, suffixName)
+            storagePlugin.getInputStream(token, suffixName)
         } returns ByteArrayInputStream(getRandomByteArray())
 
         apkRestore.restore(backup).collectIndexed { i, value ->
@@ -322,7 +324,7 @@
 
             every { splitCompatChecker.isCompatible(deviceName, listOf(splitName)) } returns true
             every { crypto.getNameForApk(salt, packageName, splitName) } returns suffixName
-            coEvery { backupPlugin.getInputStream(token, suffixName) } throws IOException()
+            coEvery { storagePlugin.getInputStream(token, suffixName) } throws IOException()
 
             apkRestore.restore(backup).collectIndexed { i, value ->
                 assertQueuedProgressFailFinished(i, value)
@@ -358,9 +360,9 @@
         val suffixName1 = getRandomString()
         val suffixName2 = getRandomString()
         every { crypto.getNameForApk(salt, packageName, split1Name) } returns suffixName1
-        coEvery { backupPlugin.getInputStream(token, suffixName1) } returns split1InputStream
+        coEvery { storagePlugin.getInputStream(token, suffixName1) } returns split1InputStream
         every { crypto.getNameForApk(salt, packageName, split2Name) } returns suffixName2
-        coEvery { backupPlugin.getInputStream(token, suffixName2) } returns split2InputStream
+        coEvery { storagePlugin.getInputStream(token, suffixName2) } returns split2InputStream
 
         coEvery {
             apkInstaller.install(match { it.size == 3 }, packageName, installerName, any())
@@ -387,7 +389,7 @@
     private fun cacheBaseApkAndGetInfo(tmpDir: Path) {
         every { strictContext.cacheDir } returns File(tmpDir.toString())
         every { crypto.getNameForApk(salt, packageName, "") } returns name
-        coEvery { backupPlugin.getInputStream(token, name) } returns apkInputStream
+        coEvery { storagePlugin.getInputStream(token, name) } returns apkInputStream
         every { pm.getPackageArchiveInfo(any(), any()) } returns packageInfo
         every {
             @Suppress("UNRESOLVED_REFERENCE")
diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/CoordinatorIntegrationTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/CoordinatorIntegrationTest.kt
index 282a6ba..b7ee1c0 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/transport/CoordinatorIntegrationTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/transport/CoordinatorIntegrationTest.kt
@@ -16,19 +16,18 @@
 import com.stevesoltys.seedvault.metadata.MetadataReaderImpl
 import com.stevesoltys.seedvault.metadata.PackageMetadata
 import com.stevesoltys.seedvault.metadata.PackageState.UNKNOWN_ERROR
+import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import com.stevesoltys.seedvault.plugins.saf.FILE_BACKUP_METADATA
 import com.stevesoltys.seedvault.transport.backup.ApkBackup
 import com.stevesoltys.seedvault.transport.backup.BackupCoordinator
-import com.stevesoltys.seedvault.transport.backup.BackupPlugin
 import com.stevesoltys.seedvault.transport.backup.FullBackup
 import com.stevesoltys.seedvault.transport.backup.InputFactory
 import com.stevesoltys.seedvault.transport.backup.KVBackup
 import com.stevesoltys.seedvault.transport.backup.PackageService
 import com.stevesoltys.seedvault.transport.backup.TestKvDbManager
 import com.stevesoltys.seedvault.transport.restore.FullRestore
-import com.stevesoltys.seedvault.transport.restore.FullRestorePlugin
 import com.stevesoltys.seedvault.transport.restore.KVRestore
-import com.stevesoltys.seedvault.transport.restore.KVRestorePlugin
 import com.stevesoltys.seedvault.transport.restore.OutputFactory
 import com.stevesoltys.seedvault.transport.restore.RestoreCoordinator
 import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
@@ -61,7 +60,9 @@
     private val notificationManager = mockk<BackupNotificationManager>()
     private val dbManager = TestKvDbManager()
 
-    private val backupPlugin = mockk<BackupPlugin>()
+    @Suppress("Deprecation")
+    private val legacyPlugin = mockk<LegacyStoragePlugin>()
+    private val backupPlugin = mockk<StoragePlugin>()
     private val kvBackup =
         KVBackup(backupPlugin, settingsManager, inputFactory, cryptoImpl, dbManager)
     private val fullBackup = FullBackup(backupPlugin, settingsManager, inputFactory, cryptoImpl)
@@ -80,18 +81,16 @@
         notificationManager
     )
 
-    private val kvRestorePlugin = mockk<KVRestorePlugin>()
     private val kvRestore = KVRestore(
         backupPlugin,
-        kvRestorePlugin,
+        legacyPlugin,
         outputFactory,
         headerReader,
         cryptoImpl,
         dbManager
     )
-    private val fullRestorePlugin = mockk<FullRestorePlugin>()
     private val fullRestore =
-        FullRestore(backupPlugin, fullRestorePlugin, outputFactory, headerReader, cryptoImpl)
+        FullRestore(backupPlugin, legacyPlugin, outputFactory, headerReader, cryptoImpl)
     private val restore = RestoreCoordinator(
         context,
         crypto,
diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinatorTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinatorTest.kt
index a169fe1..915f4de 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinatorTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinatorTest.kt
@@ -22,6 +22,7 @@
 import com.stevesoltys.seedvault.metadata.PackageState.QUOTA_EXCEEDED
 import com.stevesoltys.seedvault.metadata.PackageState.UNKNOWN_ERROR
 import com.stevesoltys.seedvault.metadata.PackageState.WAS_STOPPED
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import com.stevesoltys.seedvault.plugins.saf.FILE_BACKUP_METADATA
 import com.stevesoltys.seedvault.settings.Storage
 import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
@@ -44,7 +45,7 @@
 @Suppress("BlockingMethodInNonBlockingContext")
 internal class BackupCoordinatorTest : BackupTest() {
 
-    private val plugin = mockk<BackupPlugin>()
+    private val plugin = mockk<StoragePlugin>()
     private val kv = mockk<KVBackup>()
     private val full = mockk<FullBackup>()
     private val apkBackup = mockk<ApkBackup>()
diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/backup/FullBackupTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/backup/FullBackupTest.kt
index cf31145..ed9dd25 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/transport/backup/FullBackupTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/transport/backup/FullBackupTest.kt
@@ -6,6 +6,7 @@
 import android.app.backup.BackupTransport.TRANSPORT_QUOTA_EXCEEDED
 import com.stevesoltys.seedvault.header.VERSION
 import com.stevesoltys.seedvault.header.getADForFull
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import io.mockk.Runs
 import io.mockk.coEvery
 import io.mockk.every
@@ -23,7 +24,7 @@
 @Suppress("BlockingMethodInNonBlockingContext")
 internal class FullBackupTest : BackupTest() {
 
-    private val plugin = mockk<BackupPlugin>()
+    private val plugin = mockk<StoragePlugin>()
     private val backup = FullBackup(plugin, settingsManager, inputFactory, crypto)
 
     private val bytes = ByteArray(23).apply { Random.nextBytes(this) }
diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/backup/KVBackupTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/backup/KVBackupTest.kt
index a4476b6..83ce4ec 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/transport/backup/KVBackupTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/transport/backup/KVBackupTest.kt
@@ -12,6 +12,7 @@
 import com.stevesoltys.seedvault.header.MAX_KEY_LENGTH_SIZE
 import com.stevesoltys.seedvault.header.VERSION
 import com.stevesoltys.seedvault.header.getADForKV
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import io.mockk.CapturingSlot
 import io.mockk.Runs
 import io.mockk.coEvery
@@ -31,7 +32,7 @@
 @Suppress("BlockingMethodInNonBlockingContext")
 internal class KVBackupTest : BackupTest() {
 
-    private val plugin = mockk<BackupPlugin>()
+    private val plugin = mockk<StoragePlugin>()
     private val dataInput = mockk<BackupDataInput>()
     private val dbManager = mockk<KvDbManager>()
 
diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/restore/FullRestoreTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/restore/FullRestoreTest.kt
index e689805..73044e0 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/transport/restore/FullRestoreTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/transport/restore/FullRestoreTest.kt
@@ -11,7 +11,8 @@
 import com.stevesoltys.seedvault.header.VERSION
 import com.stevesoltys.seedvault.header.VersionHeader
 import com.stevesoltys.seedvault.header.getADForFull
-import com.stevesoltys.seedvault.transport.backup.BackupPlugin
+import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import io.mockk.CapturingSlot
 import io.mockk.Runs
 import io.mockk.coEvery
@@ -33,8 +34,8 @@
 @Suppress("BlockingMethodInNonBlockingContext")
 internal class FullRestoreTest : RestoreTest() {
 
-    private val plugin = mockk<BackupPlugin>()
-    private val legacyPlugin = mockk<FullRestorePlugin>()
+    private val plugin = mockk<StoragePlugin>()
+    private val legacyPlugin = mockk<LegacyStoragePlugin>()
     private val restore = FullRestore(plugin, legacyPlugin, outputFactory, headerReader, crypto)
 
     private val encrypted = getRandomByteArray()
@@ -50,7 +51,7 @@
     @Suppress("deprecation")
     fun `v0 hasDataForPackage() delegates to plugin`() = runBlocking {
         val result = Random.nextBoolean()
-        coEvery { legacyPlugin.hasDataForPackage(token, packageInfo) } returns result
+        coEvery { legacyPlugin.hasDataForFullPackage(token, packageInfo) } returns result
         assertEquals(result, restore.hasDataForPackage(token, packageInfo))
     }
 
diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/restore/KVRestoreTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/restore/KVRestoreTest.kt
index a2ae238..7ab12f9 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/transport/restore/KVRestoreTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/transport/restore/KVRestoreTest.kt
@@ -10,9 +10,10 @@
 import com.stevesoltys.seedvault.header.VERSION
 import com.stevesoltys.seedvault.header.VersionHeader
 import com.stevesoltys.seedvault.header.getADForKV
-import com.stevesoltys.seedvault.transport.backup.BackupPlugin
+import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
 import com.stevesoltys.seedvault.transport.backup.KVDb
 import com.stevesoltys.seedvault.transport.backup.KvDbManager
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import io.mockk.Runs
 import io.mockk.coEvery
 import io.mockk.every
@@ -35,8 +36,8 @@
 @Suppress("BlockingMethodInNonBlockingContext")
 internal class KVRestoreTest : RestoreTest() {
 
-    private val plugin = mockk<BackupPlugin>()
-    private val legacyPlugin = mockk<KVRestorePlugin>()
+    private val plugin = mockk<StoragePlugin>()
+    private val legacyPlugin = mockk<LegacyStoragePlugin>()
     private val dbManager = mockk<KvDbManager>()
     private val output = mockk<BackupDataOutput>()
     private val restore =
diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinatorTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinatorTest.kt
index 357098f..9f4dffa 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinatorTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinatorTest.kt
@@ -16,8 +16,8 @@
 import com.stevesoltys.seedvault.metadata.PackageMetadata
 import com.stevesoltys.seedvault.settings.Storage
 import com.stevesoltys.seedvault.transport.TransportTest
-import com.stevesoltys.seedvault.transport.backup.BackupPlugin
-import com.stevesoltys.seedvault.transport.backup.EncryptedMetadata
+import com.stevesoltys.seedvault.plugins.StoragePlugin
+import com.stevesoltys.seedvault.plugins.EncryptedMetadata
 import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
 import io.mockk.Runs
 import io.mockk.coEvery
@@ -39,7 +39,7 @@
 internal class RestoreCoordinatorTest : TransportTest() {
 
     private val notificationManager: BackupNotificationManager = mockk()
-    private val plugin = mockk<BackupPlugin>()
+    private val plugin = mockk<StoragePlugin>()
     private val kv = mockk<KVRestore>()
     private val full = mockk<FullRestore>()
     private val metadataReader = mockk<MetadataReader>()
diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreV0IntegrationTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreV0IntegrationTest.kt
index 07d4b5a..85e8d61 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreV0IntegrationTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreV0IntegrationTest.kt
@@ -13,10 +13,11 @@
 import com.stevesoltys.seedvault.encodeBase64
 import com.stevesoltys.seedvault.header.HeaderReaderImpl
 import com.stevesoltys.seedvault.metadata.MetadataReaderImpl
+import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
 import com.stevesoltys.seedvault.toByteArrayFromHex
 import com.stevesoltys.seedvault.transport.TransportTest
-import com.stevesoltys.seedvault.transport.backup.BackupPlugin
 import com.stevesoltys.seedvault.transport.backup.KvDbManager
+import com.stevesoltys.seedvault.plugins.StoragePlugin
 import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
 import io.mockk.coEvery
 import io.mockk.every
@@ -49,19 +50,19 @@
     private val metadataReader = MetadataReaderImpl(cryptoImpl)
     private val notificationManager = mockk<BackupNotificationManager>()
 
-    private val backupPlugin = mockk<BackupPlugin>()
-    private val kvRestorePlugin = mockk<KVRestorePlugin>()
+    @Suppress("Deprecation")
+    private val legacyPlugin = mockk<LegacyStoragePlugin>()
+    private val backupPlugin = mockk<StoragePlugin>()
     private val kvRestore = KVRestore(
         backupPlugin,
-        kvRestorePlugin,
+        legacyPlugin,
         outputFactory,
         headerReader,
         cryptoImpl,
         dbManager
     )
-    private val fullRestorePlugin = mockk<FullRestorePlugin>()
     private val fullRestore =
-        FullRestore(backupPlugin, fullRestorePlugin, outputFactory, headerReader, cryptoImpl)
+        FullRestore(backupPlugin, legacyPlugin, outputFactory, headerReader, cryptoImpl)
     private val restore = RestoreCoordinator(
         context,
         crypto,
@@ -161,7 +162,7 @@
         assertEquals(TRANSPORT_OK, restore.startRestore(token, arrayOf(packageInfo)))
 
         // find data for K/V backup
-        coEvery { kvRestorePlugin.hasDataForPackage(token, packageInfo) } returns true
+        coEvery { legacyPlugin.hasDataForPackage(token, packageInfo) } returns true
 
         val restoreDescription = restore.nextRestorePackage() ?: fail()
         assertEquals(packageInfo.packageName, restoreDescription.packageName)
@@ -171,10 +172,10 @@
         val backupDataOutput = mockk<BackupDataOutput>()
         val rInputStream = ByteArrayInputStream(encryptedAppData)
         val rInputStream2 = ByteArrayInputStream(encryptedAppData2)
-        coEvery { kvRestorePlugin.listRecords(token, packageInfo) } returns listOf(key64, key264)
+        coEvery { legacyPlugin.listRecords(token, packageInfo) } returns listOf(key64, key264)
         every { outputFactory.getBackupDataOutput(fileDescriptor) } returns backupDataOutput
         coEvery {
-            kvRestorePlugin.getInputStreamForRecord(
+            legacyPlugin.getInputStreamForRecord(
                 token,
                 packageInfo,
                 key64
@@ -183,7 +184,7 @@
         every { backupDataOutput.writeEntityHeader(key, appData.size) } returns 1137
         every { backupDataOutput.writeEntityData(appData, appData.size) } returns appData.size
         coEvery {
-            kvRestorePlugin.getInputStreamForRecord(
+            legacyPlugin.getInputStreamForRecord(
                 token,
                 packageInfo,
                 key264
@@ -212,8 +213,8 @@
         assertEquals(TRANSPORT_OK, restore.startRestore(token, arrayOf(packageInfo)))
 
         // find data only for full backup
-        coEvery { kvRestorePlugin.hasDataForPackage(token, packageInfo) } returns false
-        coEvery { fullRestorePlugin.hasDataForPackage(token, packageInfo) } returns true
+        coEvery { legacyPlugin.hasDataForPackage(token, packageInfo) } returns false
+        coEvery { legacyPlugin.hasDataForFullPackage(token, packageInfo) } returns true
 
         val restoreDescription = restore.nextRestorePackage() ?: fail()
         assertEquals(packageInfo.packageName, restoreDescription.packageName)
@@ -223,7 +224,7 @@
         val inputStream = ByteArrayInputStream(encryptedData)
         val outputStream = ByteArrayOutputStream()
         coEvery {
-            fullRestorePlugin.getInputStreamForPackage(
+            legacyPlugin.getInputStreamForPackage(
                 token,
                 packageInfo
             )