Refactor Storage Plugin API
This commit is contained in:
parent
50066f0317
commit
3c5e4120c7
30 changed files with 219 additions and 268 deletions
|
@ -3,18 +3,14 @@ package com.stevesoltys.seedvault
|
|||
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 @@ class PluginTest : KoinComponent {
|
|||
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 @@ class PluginTest : KoinComponent {
|
|||
|
||||
@Test
|
||||
fun testProviderPackageName() {
|
||||
assertNotNull(backupPlugin.providerPackageName)
|
||||
assertNotNull(storagePlugin.providerPackageName)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,38 +72,38 @@ class PluginTest : KoinComponent {
|
|||
@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 @@ class PluginTest : KoinComponent {
|
|||
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 @@ class PluginTest : KoinComponent {
|
|||
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 @@ class PluginTest : KoinComponent {
|
|||
|
||||
// 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 @@ class PluginTest : KoinComponent {
|
|||
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()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 @@ interface KVRestorePlugin {
|
|||
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
|
||||
|
||||
}
|
|
@ -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.IOException
|
|||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
interface BackupPlugin {
|
||||
interface StoragePlugin {
|
||||
|
||||
/**
|
||||
* Start a new [RestoreSet] with the given token.
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -2,17 +2,19 @@ package com.stevesoltys.seedvault.plugins.saf
|
|||
|
||||
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 @@ internal class DocumentsProviderKVRestorePlugin(
|
|||
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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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()) }
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -5,20 +5,20 @@ import android.content.pm.PackageManager
|
|||
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
|
||||
|
|
@ -8,16 +8,16 @@ import android.util.Log
|
|||
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 @@ private val TAG = ApkRestore::class.java.simpleName
|
|||
|
||||
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 @@ internal class ApkRestore(
|
|||
}
|
||||
|
||||
/**
|
||||
* 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 @@ internal class ApkRestore(
|
|||
}
|
||||
|
||||
/**
|
||||
* 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 @@ internal class ApkRestore(
|
|||
// 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)
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.stevesoltys.seedvault.metadata.PackageState.NO_DATA
|
|||
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 @@ private class CoordinatorState(
|
|||
@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 @@ internal class BackupCoordinator(
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import android.util.Log
|
|||
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 @@ private val TAG = FullBackup::class.java.simpleName
|
|||
|
||||
@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
|
||||
|
|
|
@ -12,6 +12,7 @@ import android.util.Log
|
|||
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 @@ private val TAG = KVBackup::class.java.simpleName
|
|||
|
||||
@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,
|
||||
|
|
|
@ -12,7 +12,8 @@ import com.stevesoltys.seedvault.header.HeaderReader
|
|||
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 @@ private val TAG = FullRestore::class.java.simpleName
|
|||
|
||||
@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 @@ internal class FullRestore(
|
|||
@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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
|
@ -15,7 +15,8 @@ import com.stevesoltys.seedvault.header.HeaderReader
|
|||
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 @@ private val TAG = KVRestore::class.java.simpleName
|
|||
|
||||
@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,
|
||||
|
|
|
@ -21,7 +21,7 @@ import com.stevesoltys.seedvault.metadata.DecryptionFailedException
|
|||
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 @@ internal class RestoreCoordinator(
|
|||
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
|
||||
|
|
|
@ -5,8 +5,8 @@ import org.koin.dsl.module
|
|||
|
||||
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())
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
|
@ -7,7 +7,7 @@ import androidx.lifecycle.viewModelScope
|
|||
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 @@ private val TAG = RestoreStorageViewModel::class.java.simpleName
|
|||
|
||||
internal class RestoreStorageViewModel(
|
||||
private val app: Application,
|
||||
private val backupPlugin: BackupPlugin,
|
||||
private val storagePlugin: StoragePlugin,
|
||||
settingsManager: SettingsManager
|
||||
) : StorageViewModel(app, settingsManager) {
|
||||
|
||||
|
@ -25,7 +25,7 @@ internal class RestoreStorageViewModel(
|
|||
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
|
||||
|
|
|
@ -12,11 +12,11 @@ import kotlinx.coroutines.runBlocking
|
|||
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()
|
|
@ -11,11 +11,11 @@ import com.stevesoltys.seedvault.metadata.ApkSplit
|
|||
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 @@ internal class ApkBackupRestoreTest : TransportTest() {
|
|||
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 @@ internal class ApkBackupRestoreTest : TransportTest() {
|
|||
|
||||
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 @@ internal class ApkBackupRestoreTest : TransportTest() {
|
|||
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 {
|
||||
|
|
|
@ -14,6 +14,8 @@ import com.stevesoltys.seedvault.getRandomString
|
|||
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.IN_PROGRESS
|
|||
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 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
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 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
|
||||
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 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
|
||||
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 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
|
||||
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 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
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 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
|
||||
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 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
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 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
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")
|
||||
|
|
|
@ -16,19 +16,18 @@ import com.stevesoltys.seedvault.metadata.BackupType
|
|||
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 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
|||
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 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
|||
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,
|
||||
|
|
|
@ -22,6 +22,7 @@ import com.stevesoltys.seedvault.metadata.PackageState.NO_DATA
|
|||
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 @@ import kotlin.random.Random
|
|||
@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>()
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.app.backup.BackupTransport.TRANSPORT_PACKAGE_REJECTED
|
|||
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 @@ import kotlin.random.Random
|
|||
@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) }
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.stevesoltys.seedvault.getRandomString
|
|||
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 @@ import kotlin.random.Random
|
|||
@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>()
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@ import com.stevesoltys.seedvault.header.UnsupportedVersionException
|
|||
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 @@ import kotlin.random.Random
|
|||
@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 @@ internal class FullRestoreTest : RestoreTest() {
|
|||
@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))
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,10 @@ import com.stevesoltys.seedvault.header.UnsupportedVersionException
|
|||
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 @@ import kotlin.random.Random
|
|||
@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 =
|
||||
|
|
|
@ -16,8 +16,8 @@ import com.stevesoltys.seedvault.metadata.MetadataReader
|
|||
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 @@ import kotlin.random.Random
|
|||
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>()
|
||||
|
|
|
@ -13,10 +13,11 @@ import com.stevesoltys.seedvault.crypto.KeyManagerTestImpl
|
|||
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 @@ internal class RestoreV0IntegrationTest : TransportTest() {
|
|||
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 @@ internal class RestoreV0IntegrationTest : TransportTest() {
|
|||
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 @@ internal class RestoreV0IntegrationTest : TransportTest() {
|
|||
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 @@ internal class RestoreV0IntegrationTest : TransportTest() {
|
|||
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 @@ internal class RestoreV0IntegrationTest : TransportTest() {
|
|||
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 @@ internal class RestoreV0IntegrationTest : TransportTest() {
|
|||
val inputStream = ByteArrayInputStream(encryptedData)
|
||||
val outputStream = ByteArrayOutputStream()
|
||||
coEvery {
|
||||
fullRestorePlugin.getInputStreamForPackage(
|
||||
legacyPlugin.getInputStreamForPackage(
|
||||
token,
|
||||
packageInfo
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue