Use new Backend directly in the app
This commit is contained in:
parent
5bb599e528
commit
0c1dfb316d
46 changed files with 331 additions and 244 deletions
|
@ -9,12 +9,12 @@ import android.content.pm.PackageInfo
|
|||
import android.util.Log
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.MediumTest
|
||||
import com.stevesoltys.seedvault.plugins.StoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.settings.AppStatus
|
||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
@ -32,7 +32,7 @@ class PackageServiceTest : KoinComponent {
|
|||
|
||||
private val storagePluginManager: StoragePluginManager by inject()
|
||||
|
||||
private val storagePlugin: StoragePlugin<*> get() = storagePluginManager.appPlugin
|
||||
private val backend: Backend get() = storagePluginManager.backend
|
||||
|
||||
@Test
|
||||
fun testNotAllowedPackages() {
|
||||
|
@ -65,6 +65,6 @@ class PackageServiceTest : KoinComponent {
|
|||
assertTrue(packageService.shouldIncludeAppInBackup(packageInfo.packageName))
|
||||
|
||||
// Should not backup storage provider
|
||||
assertFalse(packageService.shouldIncludeAppInBackup(storagePlugin.providerPackageName!!))
|
||||
assertFalse(packageService.shouldIncludeAppInBackup(backend.providerPackageName!!))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The Calyx Institute
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.stevesoltys.seedvault.plugins
|
||||
|
||||
import android.util.Log
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||
import java.io.OutputStream
|
||||
|
||||
suspend fun Backend.getMetadataOutputStream(token: Long): OutputStream {
|
||||
return save(LegacyAppBackupFile.Metadata(token))
|
||||
}
|
||||
|
||||
suspend fun Backend.getAvailableBackups(): Sequence<EncryptedMetadata>? {
|
||||
return try {
|
||||
// get all restore set tokens in root folder that have a metadata file
|
||||
val handles = ArrayList<LegacyAppBackupFile.Metadata>()
|
||||
list(null, LegacyAppBackupFile.Metadata::class) { fileInfo ->
|
||||
val handle = fileInfo.fileHandle as LegacyAppBackupFile.Metadata
|
||||
handles.add(handle)
|
||||
}
|
||||
val handleIterator = handles.iterator()
|
||||
return generateSequence {
|
||||
if (!handleIterator.hasNext()) return@generateSequence null // end sequence
|
||||
val handle = handleIterator.next()
|
||||
EncryptedMetadata(handle.token) {
|
||||
load(handle)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("SafBackend", "Error getting available backups: ", e)
|
||||
null
|
||||
}
|
||||
}
|
|
@ -10,11 +10,12 @@ import android.util.Log
|
|||
import androidx.annotation.WorkerThread
|
||||
import com.stevesoltys.seedvault.getStorageContext
|
||||
import com.stevesoltys.seedvault.permitDiskReads
|
||||
import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderStoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.saf.SafFactory
|
||||
import com.stevesoltys.seedvault.plugins.webdav.WebDavFactory
|
||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||
import com.stevesoltys.seedvault.settings.StoragePluginType
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.calyxos.seedvault.core.backends.saf.SafBackend
|
||||
|
||||
class StoragePluginManager(
|
||||
private val context: Context,
|
||||
|
@ -23,14 +24,14 @@ class StoragePluginManager(
|
|||
webDavFactory: WebDavFactory,
|
||||
) {
|
||||
|
||||
private var mAppPlugin: StoragePlugin<*>?
|
||||
private var mBackend: Backend?
|
||||
private var mFilesPlugin: org.calyxos.backup.storage.api.StoragePlugin?
|
||||
private var mStorageProperties: StorageProperties<*>?
|
||||
|
||||
val appPlugin: StoragePlugin<*>
|
||||
val backend: Backend
|
||||
@Synchronized
|
||||
get() {
|
||||
return mAppPlugin ?: error("App plugin was loaded, but still null")
|
||||
return mBackend ?: error("App plugin was loaded, but still null")
|
||||
}
|
||||
|
||||
val filesPlugin: org.calyxos.backup.storage.api.StoragePlugin
|
||||
|
@ -50,7 +51,7 @@ class StoragePluginManager(
|
|||
when (settingsManager.storagePluginType) {
|
||||
StoragePluginType.SAF -> {
|
||||
val safStorage = settingsManager.getSafStorage() ?: error("No SAF storage saved")
|
||||
mAppPlugin = safFactory.createAppStoragePlugin(safStorage)
|
||||
mBackend = safFactory.createBackend(safStorage)
|
||||
mFilesPlugin = safFactory.createFilesStoragePlugin(safStorage)
|
||||
mStorageProperties = safStorage
|
||||
}
|
||||
|
@ -58,13 +59,13 @@ class StoragePluginManager(
|
|||
StoragePluginType.WEB_DAV -> {
|
||||
val webDavProperties =
|
||||
settingsManager.webDavProperties ?: error("No WebDAV config saved")
|
||||
mAppPlugin = webDavFactory.createAppStoragePlugin(webDavProperties.config)
|
||||
mBackend = webDavFactory.createBackend(webDavProperties.config)
|
||||
mFilesPlugin = webDavFactory.createFilesStoragePlugin(webDavProperties.config)
|
||||
mStorageProperties = webDavProperties
|
||||
}
|
||||
|
||||
null -> {
|
||||
mAppPlugin = null
|
||||
mBackend = null
|
||||
mFilesPlugin = null
|
||||
mStorageProperties = null
|
||||
}
|
||||
|
@ -72,8 +73,8 @@ class StoragePluginManager(
|
|||
}
|
||||
|
||||
fun isValidAppPluginSet(): Boolean {
|
||||
if (mAppPlugin == null || mFilesPlugin == null) return false
|
||||
if (mAppPlugin is DocumentsProviderStoragePlugin) {
|
||||
if (mBackend == null || mFilesPlugin == null) return false
|
||||
if (mBackend is SafBackend) {
|
||||
val storage = settingsManager.getSafStorage() ?: return false
|
||||
if (storage.isUsb) return true
|
||||
return permitDiskReads {
|
||||
|
@ -91,12 +92,12 @@ class StoragePluginManager(
|
|||
*/
|
||||
fun <T> changePlugins(
|
||||
storageProperties: StorageProperties<T>,
|
||||
appPlugin: StoragePlugin<T>,
|
||||
backend: Backend,
|
||||
filesPlugin: org.calyxos.backup.storage.api.StoragePlugin,
|
||||
) {
|
||||
settingsManager.setStoragePlugin(appPlugin)
|
||||
settingsManager.setStorageBackend(backend)
|
||||
mStorageProperties = storageProperties
|
||||
mAppPlugin = appPlugin
|
||||
mBackend = backend
|
||||
mFilesPlugin = filesPlugin
|
||||
}
|
||||
|
||||
|
@ -136,7 +137,7 @@ class StoragePluginManager(
|
|||
@WorkerThread
|
||||
suspend fun getFreeSpace(): Long? {
|
||||
return try {
|
||||
appPlugin.getFreeSpace()
|
||||
backend.getFreeSpace()
|
||||
} catch (e: Throwable) { // NoClassDefFound isn't an [Exception], can get thrown by dav4jvm
|
||||
Log.e("StoragePluginManager", "Error getting free space: ", e)
|
||||
null
|
||||
|
|
|
@ -61,7 +61,7 @@ internal class DocumentsProviderStoragePlugin(
|
|||
FILE_BACKUP_ICONS -> LegacyAppBackupFile.IconsFile(token)
|
||||
else -> LegacyAppBackupFile.Blob(token, name)
|
||||
}
|
||||
return delegate.save(handle).outputStream()
|
||||
return delegate.save(handle)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
|
@ -71,7 +71,7 @@ internal class DocumentsProviderStoragePlugin(
|
|||
FILE_BACKUP_ICONS -> LegacyAppBackupFile.IconsFile(token)
|
||||
else -> LegacyAppBackupFile.Blob(token, name)
|
||||
}
|
||||
return delegate.load(handle).inputStream()
|
||||
return delegate.load(handle)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
|
|
|
@ -6,18 +6,16 @@
|
|||
package com.stevesoltys.seedvault.plugins.saf
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import com.stevesoltys.seedvault.plugins.StoragePlugin
|
||||
import com.stevesoltys.seedvault.storage.SeedvaultSafStoragePlugin
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.calyxos.seedvault.core.backends.saf.SafBackend
|
||||
|
||||
class SafFactory(
|
||||
private val context: Context,
|
||||
) {
|
||||
|
||||
internal fun createAppStoragePlugin(
|
||||
safStorage: SafStorage,
|
||||
): StoragePlugin<Uri> {
|
||||
return DocumentsProviderStoragePlugin(context, safStorage)
|
||||
internal fun createBackend(safStorage: SafStorage): Backend {
|
||||
return SafBackend(context, safStorage.toSafConfig())
|
||||
}
|
||||
|
||||
internal fun createFilesStoragePlugin(
|
||||
|
|
|
@ -16,6 +16,7 @@ import androidx.annotation.WorkerThread
|
|||
import com.stevesoltys.seedvault.R
|
||||
import com.stevesoltys.seedvault.isMassStorage
|
||||
import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.plugins.getAvailableBackups
|
||||
import com.stevesoltys.seedvault.settings.FlashDrive
|
||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||
import com.stevesoltys.seedvault.ui.storage.StorageOption
|
||||
|
@ -50,7 +51,7 @@ internal class SafHandler(
|
|||
@WorkerThread
|
||||
@Throws(IOException::class)
|
||||
suspend fun hasAppBackup(safStorage: SafStorage): Boolean {
|
||||
val appPlugin = safFactory.createAppStoragePlugin(safStorage)
|
||||
val appPlugin = safFactory.createBackend(safStorage)
|
||||
val backups = appPlugin.getAvailableBackups()
|
||||
return backups != null && backups.iterator().hasNext()
|
||||
}
|
||||
|
@ -86,7 +87,7 @@ internal class SafHandler(
|
|||
fun setPlugin(safStorage: SafStorage) {
|
||||
storagePluginManager.changePlugins(
|
||||
storageProperties = safStorage,
|
||||
appPlugin = safFactory.createAppStoragePlugin(safStorage),
|
||||
backend = safFactory.createBackend(safStorage),
|
||||
filesPlugin = safFactory.createFilesStoragePlugin(safStorage),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import android.provider.DocumentsContract.Root.COLUMN_ROOT_ID
|
|||
import androidx.annotation.WorkerThread
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import com.stevesoltys.seedvault.plugins.StorageProperties
|
||||
import org.calyxos.seedvault.core.backends.saf.SafConfig
|
||||
|
||||
data class SafStorage(
|
||||
override val config: Uri,
|
||||
|
@ -38,4 +39,12 @@ data class SafStorage(
|
|||
override fun isUnavailableUsb(context: Context): Boolean {
|
||||
return isUsb && !getDocumentFile(context).isDirectory
|
||||
}
|
||||
|
||||
fun toSafConfig() = SafConfig(
|
||||
config = config,
|
||||
name = name,
|
||||
isUsb = isUsb,
|
||||
requiresNetwork = requiresNetwork,
|
||||
rootId = rootId,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,16 +8,15 @@ package com.stevesoltys.seedvault.plugins.webdav
|
|||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.provider.Settings
|
||||
import com.stevesoltys.seedvault.plugins.StoragePlugin
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.calyxos.seedvault.core.backends.webdav.WebDavBackend
|
||||
import org.calyxos.seedvault.core.backends.webdav.WebDavConfig
|
||||
|
||||
class WebDavFactory(
|
||||
private val context: Context,
|
||||
) {
|
||||
|
||||
fun createAppStoragePlugin(config: WebDavConfig): StoragePlugin<WebDavConfig> {
|
||||
return WebDavStoragePlugin(config)
|
||||
}
|
||||
fun createBackend(config: WebDavConfig): Backend = WebDavBackend(config)
|
||||
|
||||
fun createFilesStoragePlugin(
|
||||
config: WebDavConfig,
|
||||
|
|
|
@ -10,10 +10,12 @@ import android.util.Log
|
|||
import androidx.annotation.WorkerThread
|
||||
import com.stevesoltys.seedvault.R
|
||||
import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.plugins.getAvailableBackups
|
||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.calyxos.seedvault.core.backends.webdav.WebDavConfig
|
||||
import java.io.IOException
|
||||
|
||||
|
@ -22,7 +24,7 @@ internal sealed interface WebDavConfigState {
|
|||
object Checking : WebDavConfigState
|
||||
class Success(
|
||||
val properties: WebDavProperties,
|
||||
val plugin: WebDavStoragePlugin,
|
||||
val backend: Backend,
|
||||
) : WebDavConfigState
|
||||
|
||||
class Error(val e: Exception?) : WebDavConfigState
|
||||
|
@ -52,11 +54,11 @@ internal class WebDavHandler(
|
|||
|
||||
suspend fun onConfigReceived(config: WebDavConfig) {
|
||||
mConfigState.value = WebDavConfigState.Checking
|
||||
val plugin = webDavFactory.createAppStoragePlugin(config) as WebDavStoragePlugin
|
||||
val backend = webDavFactory.createBackend(config)
|
||||
try {
|
||||
if (plugin.test()) {
|
||||
if (backend.test()) {
|
||||
val properties = createWebDavProperties(context, config)
|
||||
mConfigState.value = WebDavConfigState.Success(properties, plugin)
|
||||
mConfigState.value = WebDavConfigState.Success(properties, backend)
|
||||
} else {
|
||||
mConfigState.value = WebDavConfigState.Error(null)
|
||||
}
|
||||
|
@ -76,8 +78,8 @@ internal class WebDavHandler(
|
|||
*/
|
||||
@WorkerThread
|
||||
@Throws(IOException::class)
|
||||
suspend fun hasAppBackup(appPlugin: WebDavStoragePlugin): Boolean {
|
||||
val backups = appPlugin.getAvailableBackups()
|
||||
suspend fun hasAppBackup(backend: Backend): Boolean {
|
||||
val backups = backend.getAvailableBackups()
|
||||
return backups != null && backups.iterator().hasNext()
|
||||
}
|
||||
|
||||
|
@ -85,10 +87,10 @@ internal class WebDavHandler(
|
|||
settingsManager.saveWebDavConfig(properties.config)
|
||||
}
|
||||
|
||||
fun setPlugin(properties: WebDavProperties, plugin: WebDavStoragePlugin) {
|
||||
fun setPlugin(properties: WebDavProperties, backend: Backend) {
|
||||
storagePluginManager.changePlugins(
|
||||
storageProperties = properties,
|
||||
appPlugin = plugin,
|
||||
backend = backend,
|
||||
filesPlugin = webDavFactory.createFilesStoragePlugin(properties.config),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ internal class WebDavStoragePlugin(
|
|||
FILE_BACKUP_ICONS -> LegacyAppBackupFile.IconsFile(token)
|
||||
else -> LegacyAppBackupFile.Blob(token, name)
|
||||
}
|
||||
return delegate.save(handle).outputStream()
|
||||
return delegate.save(handle)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
|
@ -59,7 +59,7 @@ internal class WebDavStoragePlugin(
|
|||
FILE_BACKUP_ICONS -> LegacyAppBackupFile.IconsFile(token)
|
||||
else -> LegacyAppBackupFile.Blob(token, name)
|
||||
}
|
||||
return delegate.load(handle).inputStream()
|
||||
return delegate.load(handle)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
|
|
|
@ -101,7 +101,7 @@ internal class AppDataRestoreManager(
|
|||
return
|
||||
}
|
||||
|
||||
val providerPackageName = storagePluginManager.appPlugin.providerPackageName
|
||||
val providerPackageName = storagePluginManager.backend.providerPackageName
|
||||
val observer = RestoreObserver(
|
||||
restoreCoordinator = restoreCoordinator,
|
||||
restorableBackup = restorableBackup,
|
||||
|
|
|
@ -17,7 +17,6 @@ import com.stevesoltys.seedvault.metadata.PackageMetadataMap
|
|||
import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.ui.PACKAGE_NAME_SYSTEM
|
||||
import com.stevesoltys.seedvault.ui.systemData
|
||||
import com.stevesoltys.seedvault.worker.FILE_BACKUP_ICONS
|
||||
import com.stevesoltys.seedvault.worker.IconManager
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
@ -25,6 +24,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||
import java.util.Locale
|
||||
|
||||
internal class SelectedAppsState(
|
||||
|
@ -88,10 +88,10 @@ internal class AppSelectionManager(
|
|||
SelectedAppsState(apps = items, allSelected = isSetupWizard, iconsLoaded = false)
|
||||
// download icons
|
||||
coroutineScope.launch(workDispatcher) {
|
||||
val plugin = pluginManager.appPlugin
|
||||
val backend = pluginManager.backend
|
||||
val token = restorableBackup.token
|
||||
val packagesWithIcons = try {
|
||||
plugin.getInputStream(token, FILE_BACKUP_ICONS).use {
|
||||
backend.load(LegacyAppBackupFile.IconsFile(token)).use {
|
||||
iconManager.downloadIcons(restorableBackup.version, token, it)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
|
|
@ -34,6 +34,7 @@ import kotlinx.coroutines.TimeoutCancellationException
|
|||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.Locale
|
||||
|
@ -54,7 +55,7 @@ internal class ApkRestore(
|
|||
) {
|
||||
|
||||
private val pm = context.packageManager
|
||||
private val storagePlugin get() = pluginManager.appPlugin
|
||||
private val backend get() = pluginManager.backend
|
||||
|
||||
private val mInstallResult = MutableStateFlow(InstallResult())
|
||||
val installResult = mInstallResult.asStateFlow()
|
||||
|
@ -65,7 +66,7 @@ internal class ApkRestore(
|
|||
val packages = backup.packageMetadataMap.mapNotNull { (packageName, metadata) ->
|
||||
// We need to exclude the DocumentsProvider used to retrieve backup data.
|
||||
// Otherwise, it gets killed when we install it, terminating our restoration.
|
||||
if (packageName == storagePlugin.providerPackageName) return@mapNotNull null
|
||||
if (packageName == backend.providerPackageName) return@mapNotNull null
|
||||
// The @pm@ package needs to be included in [backup], but can't be installed like an app
|
||||
if (packageName == MAGIC_PACKAGE_MANAGER) return@mapNotNull null
|
||||
// we don't filter out apps without APK, so the user can manually install them
|
||||
|
@ -294,7 +295,7 @@ internal class ApkRestore(
|
|||
legacyStoragePlugin.getApkInputStream(token, packageName, suffix)
|
||||
} else {
|
||||
val name = crypto.getNameForApk(salt, packageName, suffix)
|
||||
storagePlugin.getInputStream(token, name)
|
||||
backend.load(LegacyAppBackupFile.Blob(token, name))
|
||||
}
|
||||
val sha256 = copyStreamsAndGetHash(inputStream, cachedApk.outputStream())
|
||||
return Pair(cachedApk, sha256)
|
||||
|
|
|
@ -12,13 +12,13 @@ import android.net.Uri
|
|||
import androidx.annotation.UiThread
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.stevesoltys.seedvault.permitDiskReads
|
||||
import com.stevesoltys.seedvault.plugins.StoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderStoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.saf.SafStorage
|
||||
import com.stevesoltys.seedvault.plugins.webdav.WebDavHandler.Companion.createWebDavProperties
|
||||
import com.stevesoltys.seedvault.plugins.webdav.WebDavProperties
|
||||
import com.stevesoltys.seedvault.plugins.webdav.WebDavStoragePlugin
|
||||
import com.stevesoltys.seedvault.transport.backup.BackupCoordinator
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.calyxos.seedvault.core.backends.saf.SafBackend
|
||||
import org.calyxos.seedvault.core.backends.webdav.WebDavBackend
|
||||
import org.calyxos.seedvault.core.backends.webdav.WebDavConfig
|
||||
import java.util.concurrent.ConcurrentSkipListSet
|
||||
|
||||
|
@ -128,10 +128,10 @@ class SettingsManager(private val context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
fun setStoragePlugin(plugin: StoragePlugin<*>) {
|
||||
fun setStorageBackend(plugin: Backend) {
|
||||
val value = when (plugin) {
|
||||
is DocumentsProviderStoragePlugin -> StoragePluginType.SAF
|
||||
is WebDavStoragePlugin -> StoragePluginType.WEB_DAV
|
||||
is SafBackend -> StoragePluginType.SAF
|
||||
is WebDavBackend -> StoragePluginType.WEB_DAV
|
||||
else -> error("Unsupported plugin: ${plugin::class.java.simpleName}")
|
||||
}.name
|
||||
prefs.edit()
|
||||
|
|
|
@ -45,13 +45,13 @@ internal class WebDavStoragePlugin(
|
|||
@Throws(IOException::class)
|
||||
override suspend fun getChunkOutputStream(chunkId: String): OutputStream {
|
||||
val fileHandle = FileBackupFileType.Blob(androidId, chunkId)
|
||||
return delegate.save(fileHandle).outputStream()
|
||||
return delegate.save(fileHandle)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override suspend fun getBackupSnapshotOutputStream(timestamp: Long): OutputStream {
|
||||
val fileHandle = FileBackupFileType.Snapshot(androidId, timestamp)
|
||||
return delegate.save(fileHandle).outputStream()
|
||||
return delegate.save(fileHandle)
|
||||
}
|
||||
|
||||
/************************* Restore *******************************/
|
||||
|
@ -73,7 +73,7 @@ internal class WebDavStoragePlugin(
|
|||
override suspend fun getBackupSnapshotInputStream(storedSnapshot: StoredSnapshot): InputStream {
|
||||
val androidId = storedSnapshot.androidId
|
||||
val handle = FileBackupFileType.Snapshot(androidId, storedSnapshot.timestamp)
|
||||
return delegate.load(handle).inputStream()
|
||||
return delegate.load(handle)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
|
@ -82,7 +82,7 @@ internal class WebDavStoragePlugin(
|
|||
chunkId: String,
|
||||
): InputStream {
|
||||
val handle = FileBackupFileType.Blob(snapshot.androidId, chunkId)
|
||||
return delegate.load(handle).inputStream()
|
||||
return delegate.load(handle)
|
||||
}
|
||||
|
||||
/************************* Pruning *******************************/
|
||||
|
|
|
@ -31,6 +31,7 @@ import com.stevesoltys.seedvault.metadata.PackageState.QUOTA_EXCEEDED
|
|||
import com.stevesoltys.seedvault.metadata.PackageState.UNKNOWN_ERROR
|
||||
import com.stevesoltys.seedvault.plugins.StoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.plugins.getMetadataOutputStream
|
||||
import com.stevesoltys.seedvault.plugins.isOutOfSpace
|
||||
import com.stevesoltys.seedvault.plugins.saf.FILE_BACKUP_METADATA
|
||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||
|
@ -74,7 +75,7 @@ internal class BackupCoordinator(
|
|||
private val nm: BackupNotificationManager,
|
||||
) {
|
||||
|
||||
private val plugin get() = pluginManager.appPlugin
|
||||
private val backend get() = pluginManager.backend
|
||||
private val state = CoordinatorState(
|
||||
calledInitialize = false,
|
||||
calledClearBackupData = false,
|
||||
|
@ -97,7 +98,6 @@ internal class BackupCoordinator(
|
|||
val token = clock.time()
|
||||
Log.i(TAG, "Starting new RestoreSet with token $token...")
|
||||
settingsManager.setNewToken(token)
|
||||
plugin.startNewRestoreSet(token)
|
||||
Log.d(TAG, "Resetting backup metadata...")
|
||||
metadataManager.onDeviceInitialization(token)
|
||||
}
|
||||
|
@ -125,7 +125,6 @@ internal class BackupCoordinator(
|
|||
// instead of simply deleting the current one
|
||||
startNewRestoreSet()
|
||||
Log.i(TAG, "Initialize Device!")
|
||||
plugin.initializeDevice()
|
||||
// [finishBackup] will only be called when we return [TRANSPORT_OK] here
|
||||
// so we remember that we initialized successfully
|
||||
state.calledInitialize = true
|
||||
|
@ -410,7 +409,8 @@ internal class BackupCoordinator(
|
|||
}
|
||||
|
||||
private suspend fun onPackageBackedUp(packageInfo: PackageInfo, type: BackupType, size: Long?) {
|
||||
plugin.getMetadataOutputStream().use {
|
||||
val token = settingsManager.getToken() ?: error("no token")
|
||||
backend.getMetadataOutputStream(token).use {
|
||||
metadataManager.onPackageBackedUp(packageInfo, type, size, it)
|
||||
}
|
||||
}
|
||||
|
@ -418,7 +418,8 @@ internal class BackupCoordinator(
|
|||
private suspend fun onPackageBackupError(packageInfo: PackageInfo, type: BackupType) {
|
||||
val packageName = packageInfo.packageName
|
||||
try {
|
||||
plugin.getMetadataOutputStream().use {
|
||||
val token = settingsManager.getToken() ?: error("no token")
|
||||
backend.getMetadataOutputStream(token).use {
|
||||
metadataManager.onPackageBackupError(packageInfo, state.cancelReason, it, type)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
|
|
|
@ -20,6 +20,7 @@ import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
|||
import com.stevesoltys.seedvault.plugins.isOutOfSpace
|
||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||
import java.io.Closeable
|
||||
import java.io.EOFException
|
||||
import java.io.IOException
|
||||
|
@ -53,7 +54,7 @@ internal class FullBackup(
|
|||
private val crypto: Crypto,
|
||||
) {
|
||||
|
||||
private val plugin get() = pluginManager.appPlugin
|
||||
private val backend get() = pluginManager.backend
|
||||
private var state: FullBackupState? = null
|
||||
|
||||
fun hasState() = state != null
|
||||
|
@ -128,7 +129,7 @@ internal class FullBackup(
|
|||
val name = crypto.getNameForPackage(salt, packageName)
|
||||
// get OutputStream to write backup data into
|
||||
val outputStream = try {
|
||||
plugin.getOutputStream(token, name)
|
||||
backend.save(LegacyAppBackupFile.Blob(token, name))
|
||||
} catch (e: IOException) {
|
||||
"Error getting OutputStream for full backup of $packageName".let {
|
||||
Log.e(TAG, it, e)
|
||||
|
@ -186,7 +187,7 @@ internal class FullBackup(
|
|||
@Throws(IOException::class)
|
||||
suspend fun clearBackupData(packageInfo: PackageInfo, token: Long, salt: String) {
|
||||
val name = crypto.getNameForPackage(salt, packageInfo.packageName)
|
||||
plugin.removeData(token, name)
|
||||
backend.remove(LegacyAppBackupFile.Blob(token, name))
|
||||
}
|
||||
|
||||
suspend fun cancelFullBackup(token: Long, salt: String, ignoreApp: Boolean) {
|
||||
|
|
|
@ -22,6 +22,7 @@ import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
|||
import com.stevesoltys.seedvault.plugins.isOutOfSpace
|
||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||
import java.io.IOException
|
||||
import java.util.zip.GZIPOutputStream
|
||||
|
||||
|
@ -47,7 +48,7 @@ internal class KVBackup(
|
|||
private val dbManager: KvDbManager,
|
||||
) {
|
||||
|
||||
private val plugin get() = pluginManager.appPlugin
|
||||
private val backend get() = pluginManager.backend
|
||||
private var state: KVBackupState? = null
|
||||
|
||||
fun hasState() = state != null
|
||||
|
@ -207,7 +208,7 @@ internal class KVBackup(
|
|||
suspend fun clearBackupData(packageInfo: PackageInfo, token: Long, salt: String) {
|
||||
Log.i(TAG, "Clearing K/V data of ${packageInfo.packageName}")
|
||||
val name = state?.name ?: crypto.getNameForPackage(salt, packageInfo.packageName)
|
||||
plugin.removeData(token, name)
|
||||
backend.remove(LegacyAppBackupFile.Blob(token, name))
|
||||
if (!dbManager.deleteDb(packageInfo.packageName)) throw IOException()
|
||||
}
|
||||
|
||||
|
@ -254,7 +255,8 @@ internal class KVBackup(
|
|||
db.vacuum()
|
||||
db.close()
|
||||
|
||||
plugin.getOutputStream(token, name).use { outputStream ->
|
||||
val handle = LegacyAppBackupFile.Blob(token, name)
|
||||
backend.save(handle).use { outputStream ->
|
||||
outputStream.write(ByteArray(1) { VERSION })
|
||||
val ad = getADForKV(VERSION, packageName)
|
||||
crypto.newEncryptingStream(outputStream, ad).use { encryptedStream ->
|
||||
|
|
|
@ -27,9 +27,9 @@ import android.util.Log
|
|||
import android.util.Log.INFO
|
||||
import androidx.annotation.WorkerThread
|
||||
import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER
|
||||
import com.stevesoltys.seedvault.plugins.StoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
|
||||
private val TAG = PackageService::class.java.simpleName
|
||||
|
||||
|
@ -48,7 +48,7 @@ internal class PackageService(
|
|||
|
||||
private val packageManager: PackageManager = context.packageManager
|
||||
private val myUserId = UserHandle.myUserId()
|
||||
private val plugin: StoragePlugin<*> get() = pluginManager.appPlugin
|
||||
private val backend: Backend get() = pluginManager.backend
|
||||
|
||||
val eligiblePackages: List<String>
|
||||
@WorkerThread
|
||||
|
@ -182,7 +182,7 @@ internal class PackageService(
|
|||
// We need to explicitly exclude DocumentsProvider and Seedvault.
|
||||
// Otherwise, they get killed while backing them up, terminating our backup.
|
||||
val excludedPackages = setOf(
|
||||
plugin.providerPackageName,
|
||||
backend.providerPackageName,
|
||||
context.packageName
|
||||
)
|
||||
|
||||
|
@ -225,7 +225,7 @@ internal class PackageService(
|
|||
*/
|
||||
private fun PackageInfo.doesNotGetBackedUp(): Boolean {
|
||||
if (packageName == MAGIC_PACKAGE_MANAGER || applicationInfo == null) return true
|
||||
if (packageName == plugin.providerPackageName) return true
|
||||
if (packageName == backend.providerPackageName) return true
|
||||
return !allowsBackup() || isStopped()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import com.stevesoltys.seedvault.header.getADForFull
|
|||
import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
||||
import libcore.io.IoUtils.closeQuietly
|
||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||
import java.io.EOFException
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
@ -46,7 +47,7 @@ internal class FullRestore(
|
|||
private val crypto: Crypto,
|
||||
) {
|
||||
|
||||
private val plugin get() = pluginManager.appPlugin
|
||||
private val backend get() = pluginManager.backend
|
||||
private var state: FullRestoreState? = null
|
||||
|
||||
fun hasState() = state != null
|
||||
|
@ -114,7 +115,8 @@ internal class FullRestore(
|
|||
crypto.decryptHeader(inputStream, version, packageName)
|
||||
state.inputStream = inputStream
|
||||
} else {
|
||||
val inputStream = plugin.getInputStream(state.token, state.name)
|
||||
val handle = LegacyAppBackupFile.Blob(state.token, state.name)
|
||||
val inputStream = backend.load(handle)
|
||||
val version = headerReader.readVersion(inputStream, state.version)
|
||||
val ad = getADForFull(version, packageName)
|
||||
state.inputStream = crypto.newDecryptingStream(inputStream, ad)
|
||||
|
|
|
@ -25,6 +25,7 @@ import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
|||
import com.stevesoltys.seedvault.transport.backup.KVDb
|
||||
import com.stevesoltys.seedvault.transport.backup.KvDbManager
|
||||
import libcore.io.IoUtils.closeQuietly
|
||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||
import java.io.IOException
|
||||
import java.security.GeneralSecurityException
|
||||
import java.util.zip.GZIPInputStream
|
||||
|
@ -53,7 +54,7 @@ internal class KVRestore(
|
|||
private val dbManager: KvDbManager,
|
||||
) {
|
||||
|
||||
private val plugin get() = pluginManager.appPlugin
|
||||
private val backend get() = pluginManager.backend
|
||||
private var state: KVRestoreState? = null
|
||||
|
||||
/**
|
||||
|
@ -156,7 +157,8 @@ internal class KVRestore(
|
|||
@Throws(IOException::class, GeneralSecurityException::class, UnsupportedVersionException::class)
|
||||
private suspend fun downloadRestoreDb(state: KVRestoreState): KVDb {
|
||||
val packageName = state.packageInfo.packageName
|
||||
plugin.getInputStream(state.token, state.name).use { inputStream ->
|
||||
val handle = LegacyAppBackupFile.Blob(state.token, state.name)
|
||||
backend.load(handle).use { inputStream ->
|
||||
headerReader.readVersion(inputStream, state.version)
|
||||
val ad = getADForKV(VERSION, packageName)
|
||||
crypto.newDecryptingStream(inputStream, ad).use { decryptedStream ->
|
||||
|
|
|
@ -25,12 +25,13 @@ import com.stevesoltys.seedvault.metadata.BackupType
|
|||
import com.stevesoltys.seedvault.metadata.DecryptionFailedException
|
||||
import com.stevesoltys.seedvault.metadata.MetadataManager
|
||||
import com.stevesoltys.seedvault.metadata.MetadataReader
|
||||
import com.stevesoltys.seedvault.plugins.StoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.plugins.getAvailableBackups
|
||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||
import com.stevesoltys.seedvault.transport.D2D_TRANSPORT_FLAGS
|
||||
import com.stevesoltys.seedvault.transport.DEFAULT_TRANSPORT_FLAGS
|
||||
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
|
@ -67,13 +68,13 @@ internal class RestoreCoordinator(
|
|||
private val metadataReader: MetadataReader,
|
||||
) {
|
||||
|
||||
private val plugin: StoragePlugin<*> get() = pluginManager.appPlugin
|
||||
private val backend: Backend get() = pluginManager.backend
|
||||
private var state: RestoreCoordinatorState? = null
|
||||
private var backupMetadata: BackupMetadata? = null
|
||||
private val failedPackages = ArrayList<String>()
|
||||
|
||||
suspend fun getAvailableMetadata(): Map<Long, BackupMetadata>? {
|
||||
val availableBackups = plugin.getAvailableBackups() ?: return null
|
||||
val availableBackups = backend.getAvailableBackups() ?: return null
|
||||
val metadataMap = HashMap<Long, BackupMetadata>()
|
||||
for (encryptedMetadata in availableBackups) {
|
||||
try {
|
||||
|
|
|
@ -18,7 +18,6 @@ import com.stevesoltys.seedvault.plugins.saf.SafHandler
|
|||
import com.stevesoltys.seedvault.plugins.saf.SafStorage
|
||||
import com.stevesoltys.seedvault.plugins.webdav.WebDavHandler
|
||||
import com.stevesoltys.seedvault.plugins.webdav.WebDavProperties
|
||||
import com.stevesoltys.seedvault.plugins.webdav.WebDavStoragePlugin
|
||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||
import com.stevesoltys.seedvault.storage.StorageBackupJobService
|
||||
import com.stevesoltys.seedvault.transport.backup.BackupInitializer
|
||||
|
@ -27,6 +26,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.launch
|
||||
import org.calyxos.backup.storage.api.StorageBackup
|
||||
import org.calyxos.backup.storage.backup.BackupJobService
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
@ -59,9 +59,9 @@ internal class BackupStorageViewModel(
|
|||
onStorageLocationSet(safStorage.isUsb)
|
||||
}
|
||||
|
||||
override fun onWebDavConfigSet(properties: WebDavProperties, plugin: WebDavStoragePlugin) {
|
||||
override fun onWebDavConfigSet(properties: WebDavProperties, backend: Backend) {
|
||||
webdavHandler.save(properties)
|
||||
webdavHandler.setPlugin(properties, plugin)
|
||||
webdavHandler.setPlugin(properties, backend)
|
||||
scheduleBackupWorkers()
|
||||
onStorageLocationSet(isUsb = false)
|
||||
}
|
||||
|
|
|
@ -15,10 +15,10 @@ import com.stevesoltys.seedvault.plugins.saf.SafHandler
|
|||
import com.stevesoltys.seedvault.plugins.saf.SafStorage
|
||||
import com.stevesoltys.seedvault.plugins.webdav.WebDavHandler
|
||||
import com.stevesoltys.seedvault.plugins.webdav.WebDavProperties
|
||||
import com.stevesoltys.seedvault.plugins.webdav.WebDavStoragePlugin
|
||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import java.io.IOException
|
||||
|
||||
private val TAG = RestoreStorageViewModel::class.java.simpleName
|
||||
|
@ -56,17 +56,17 @@ internal class RestoreStorageViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onWebDavConfigSet(properties: WebDavProperties, plugin: WebDavStoragePlugin) {
|
||||
override fun onWebDavConfigSet(properties: WebDavProperties, backend: Backend) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val hasBackup = try {
|
||||
webdavHandler.hasAppBackup(plugin)
|
||||
webdavHandler.hasAppBackup(backend)
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "Error reading: ${properties.config.url}", e)
|
||||
false
|
||||
}
|
||||
if (hasBackup) {
|
||||
webdavHandler.save(properties)
|
||||
webdavHandler.setPlugin(properties, plugin)
|
||||
webdavHandler.setPlugin(properties, backend)
|
||||
mLocationChecked.postEvent(LocationResult())
|
||||
} else {
|
||||
Log.w(TAG, "Location was rejected: ${properties.config.url}")
|
||||
|
|
|
@ -18,13 +18,13 @@ import com.stevesoltys.seedvault.plugins.saf.SafHandler
|
|||
import com.stevesoltys.seedvault.plugins.saf.SafStorage
|
||||
import com.stevesoltys.seedvault.plugins.webdav.WebDavHandler
|
||||
import com.stevesoltys.seedvault.plugins.webdav.WebDavProperties
|
||||
import com.stevesoltys.seedvault.plugins.webdav.WebDavStoragePlugin
|
||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||
import com.stevesoltys.seedvault.ui.LiveEvent
|
||||
import com.stevesoltys.seedvault.ui.MutableLiveEvent
|
||||
import com.stevesoltys.seedvault.ui.storage.StorageOption.SafOption
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.calyxos.seedvault.core.backends.webdav.WebDavConfig
|
||||
|
||||
internal abstract class StorageViewModel(
|
||||
|
@ -89,7 +89,7 @@ internal abstract class StorageViewModel(
|
|||
}
|
||||
|
||||
abstract fun onSafUriSet(safStorage: SafStorage)
|
||||
abstract fun onWebDavConfigSet(properties: WebDavProperties, plugin: WebDavStoragePlugin)
|
||||
abstract fun onWebDavConfigSet(properties: WebDavProperties, backend: Backend)
|
||||
|
||||
override fun onCleared() {
|
||||
storageOptionFetcher.setRemovableStorageListener(null)
|
||||
|
@ -107,9 +107,9 @@ internal abstract class StorageViewModel(
|
|||
fun resetWebDavConfig() = webdavHandler.resetConfigState()
|
||||
|
||||
@UiThread
|
||||
fun onWebDavConfigSuccess(properties: WebDavProperties, plugin: WebDavStoragePlugin) {
|
||||
fun onWebDavConfigSuccess(properties: WebDavProperties, backend: Backend) {
|
||||
mLocationSet.setEvent(true)
|
||||
onWebDavConfigSet(properties, plugin)
|
||||
onWebDavConfigSet(properties, backend)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ class WebDavConfigFragment : Fragment(), View.OnClickListener {
|
|||
}
|
||||
|
||||
is WebDavConfigState.Success -> {
|
||||
viewModel.onWebDavConfigSuccess(state.properties, state.plugin)
|
||||
viewModel.onWebDavConfigSuccess(state.properties, state.backend)
|
||||
}
|
||||
|
||||
is WebDavConfigState.Error -> {
|
||||
|
|
|
@ -13,6 +13,7 @@ import com.stevesoltys.seedvault.metadata.PackageState.NOT_ALLOWED
|
|||
import com.stevesoltys.seedvault.metadata.PackageState.WAS_STOPPED
|
||||
import com.stevesoltys.seedvault.plugins.StoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.plugins.getMetadataOutputStream
|
||||
import com.stevesoltys.seedvault.plugins.isOutOfSpace
|
||||
import com.stevesoltys.seedvault.plugins.saf.FILE_BACKUP_METADATA
|
||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||
|
@ -21,6 +22,7 @@ import com.stevesoltys.seedvault.transport.backup.isStopped
|
|||
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
||||
import com.stevesoltys.seedvault.ui.notification.getAppName
|
||||
import kotlinx.coroutines.delay
|
||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
|
||||
|
@ -55,7 +57,8 @@ internal class ApkBackupManager(
|
|||
keepTrying {
|
||||
// upload all local changes only at the end,
|
||||
// so we don't have to re-upload the metadata
|
||||
pluginManager.appPlugin.getMetadataOutputStream().use { outputStream ->
|
||||
val token = settingsManager.getToken() ?: error("no token")
|
||||
pluginManager.backend.getMetadataOutputStream(token).use { outputStream ->
|
||||
metadataManager.uploadMetadata(outputStream)
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +104,8 @@ internal class ApkBackupManager(
|
|||
private suspend fun uploadIcons() {
|
||||
try {
|
||||
val token = settingsManager.getToken() ?: throw IOException("no current token")
|
||||
pluginManager.appPlugin.getOutputStream(token, FILE_BACKUP_ICONS).use {
|
||||
val handle = LegacyAppBackupFile.IconsFile(token)
|
||||
pluginManager.backend.save(handle).use {
|
||||
iconManager.uploadIcons(token, it)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
|
@ -119,7 +123,7 @@ internal class ApkBackupManager(
|
|||
return try {
|
||||
apkBackup.backupApkIfNecessary(packageInfo) { name ->
|
||||
val token = settingsManager.getToken() ?: throw IOException("no current token")
|
||||
pluginManager.appPlugin.getOutputStream(token, name)
|
||||
pluginManager.backend.save(LegacyAppBackupFile.Blob(token, name))
|
||||
}?.let { packageMetadata ->
|
||||
metadataManager.onApkBackedUp(packageInfo, packageMetadata)
|
||||
true
|
||||
|
|
|
@ -13,13 +13,11 @@ import com.stevesoltys.seedvault.getRandomString
|
|||
import com.stevesoltys.seedvault.metadata.BackupMetadata
|
||||
import com.stevesoltys.seedvault.metadata.PackageMetadata
|
||||
import com.stevesoltys.seedvault.metadata.PackageMetadataMap
|
||||
import com.stevesoltys.seedvault.plugins.StoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.transport.TransportTest
|
||||
import com.stevesoltys.seedvault.ui.PACKAGE_NAME_CONTACTS
|
||||
import com.stevesoltys.seedvault.ui.PACKAGE_NAME_SETTINGS
|
||||
import com.stevesoltys.seedvault.ui.PACKAGE_NAME_SYSTEM
|
||||
import com.stevesoltys.seedvault.worker.FILE_BACKUP_ICONS
|
||||
import com.stevesoltys.seedvault.worker.IconManager
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
|
@ -28,6 +26,8 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
|
@ -221,10 +221,10 @@ internal class AppSelectionManagerTest : TransportTest() {
|
|||
|
||||
@Test
|
||||
fun `test icon loading fails`() = scope.runTest {
|
||||
val appPlugin: StoragePlugin<*> = mockk()
|
||||
every { storagePluginManager.appPlugin } returns appPlugin
|
||||
val backend: Backend = mockk()
|
||||
every { storagePluginManager.backend } returns backend
|
||||
coEvery {
|
||||
appPlugin.getInputStream(backupMetadata.token, FILE_BACKUP_ICONS)
|
||||
backend.load(LegacyAppBackupFile.IconsFile(backupMetadata.token))
|
||||
} throws IOException()
|
||||
|
||||
appSelectionManager.selectedAppsFlow.test {
|
||||
|
@ -427,11 +427,11 @@ internal class AppSelectionManagerTest : TransportTest() {
|
|||
}
|
||||
|
||||
private fun expectIconLoading(icons: Set<String> = setOf(packageName1, packageName2)) {
|
||||
val appPlugin: StoragePlugin<*> = mockk()
|
||||
val backend: Backend = mockk()
|
||||
val inputStream = ByteArrayInputStream(Random.nextBytes(42))
|
||||
every { storagePluginManager.appPlugin } returns appPlugin
|
||||
every { storagePluginManager.backend } returns backend
|
||||
coEvery {
|
||||
appPlugin.getInputStream(backupMetadata.token, FILE_BACKUP_ICONS)
|
||||
backend.load(LegacyAppBackupFile.IconsFile(backupMetadata.token))
|
||||
} returns inputStream
|
||||
every {
|
||||
iconManager.downloadIcons(backupMetadata.version, backupMetadata.token, inputStream)
|
||||
|
|
|
@ -21,7 +21,6 @@ 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.plugins.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.restore.RestorableBackup
|
||||
import com.stevesoltys.seedvault.restore.install.ApkInstallState.IN_PROGRESS
|
||||
|
@ -36,6 +35,8 @@ import io.mockk.mockkStatic
|
|||
import io.mockk.slot
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||
import org.junit.jupiter.api.Assertions.assertArrayEquals
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
|
@ -65,7 +66,7 @@ internal class ApkBackupRestoreTest : TransportTest() {
|
|||
|
||||
@Suppress("Deprecation")
|
||||
private val legacyStoragePlugin: LegacyStoragePlugin = mockk()
|
||||
private val storagePlugin: StoragePlugin<*> = mockk()
|
||||
private val backend: Backend = mockk()
|
||||
private val splitCompatChecker: ApkSplitCompatibilityChecker = mockk()
|
||||
private val apkInstaller: ApkInstaller = mockk()
|
||||
private val installRestriction: InstallRestriction = mockk()
|
||||
|
@ -111,7 +112,7 @@ internal class ApkBackupRestoreTest : TransportTest() {
|
|||
|
||||
init {
|
||||
mockkStatic(PackageUtils::class)
|
||||
every { storagePluginManager.appPlugin } returns storagePlugin
|
||||
every { storagePluginManager.backend } returns backend
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -147,7 +148,7 @@ internal class ApkBackupRestoreTest : TransportTest() {
|
|||
every { metadataManager.salt } returns salt
|
||||
every { crypto.getNameForApk(salt, packageName) } returns name
|
||||
every { crypto.getNameForApk(salt, packageName, splitName) } returns suffixName
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
|
||||
apkBackup.backupApkIfNecessary(packageInfo, outputStreamGetter)
|
||||
|
||||
|
@ -164,7 +165,7 @@ internal class ApkBackupRestoreTest : TransportTest() {
|
|||
every { pm.getPackageInfo(packageName, any<Int>()) } throws NameNotFoundException()
|
||||
every { strictContext.cacheDir } returns tmpFile
|
||||
every { crypto.getNameForApk(salt, packageName, "") } returns name
|
||||
coEvery { storagePlugin.getInputStream(token, name) } returns inputStream
|
||||
coEvery { backend.load(LegacyAppBackupFile.Blob(token, name)) } returns inputStream
|
||||
every { pm.getPackageArchiveInfo(capture(apkPath), any<Int>()) } returns packageInfo
|
||||
every { applicationInfo.loadIcon(pm) } returns icon
|
||||
every { pm.getApplicationLabel(packageInfo.applicationInfo!!) } returns appName
|
||||
|
@ -172,7 +173,9 @@ internal class ApkBackupRestoreTest : TransportTest() {
|
|||
splitCompatChecker.isCompatible(metadata.deviceName, listOf(splitName))
|
||||
} returns true
|
||||
every { crypto.getNameForApk(salt, packageName, splitName) } returns suffixName
|
||||
coEvery { storagePlugin.getInputStream(token, suffixName) } returns splitInputStream
|
||||
coEvery {
|
||||
backend.load(LegacyAppBackupFile.Blob(token, suffixName))
|
||||
} returns splitInputStream
|
||||
val resultMap = mapOf(
|
||||
packageName to ApkInstallResult(
|
||||
packageName,
|
||||
|
|
|
@ -25,7 +25,6 @@ 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.plugins.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.restore.RestorableBackup
|
||||
import com.stevesoltys.seedvault.restore.install.ApkInstallState.FAILED
|
||||
|
@ -44,6 +43,8 @@ import io.mockk.mockkStatic
|
|||
import io.mockk.verifyOrder
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
|
@ -67,7 +68,7 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
private val backupManager: IBackupManager = mockk()
|
||||
private val backupStateManager: BackupStateManager = mockk()
|
||||
private val storagePluginManager: StoragePluginManager = mockk()
|
||||
private val storagePlugin: StoragePlugin<*> = mockk()
|
||||
private val backend: Backend = mockk()
|
||||
private val legacyStoragePlugin: LegacyStoragePlugin = mockk()
|
||||
private val splitCompatChecker: ApkSplitCompatibilityChecker = mockk()
|
||||
private val apkInstaller: ApkInstaller = mockk()
|
||||
|
@ -108,7 +109,7 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
// as we don't do strict signature checking, we can use a relaxed mock
|
||||
packageInfo.signingInfo = mockk(relaxed = true)
|
||||
|
||||
every { storagePluginManager.appPlugin } returns storagePlugin
|
||||
every { storagePluginManager.backend } returns backend
|
||||
|
||||
// related to starting/stopping service
|
||||
every { strictContext.packageName } returns "org.foo.bar"
|
||||
|
@ -128,8 +129,8 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
every { backupStateManager.isAutoRestoreEnabled } returns false
|
||||
every { strictContext.cacheDir } returns File(tmpDir.toString())
|
||||
every { crypto.getNameForApk(salt, packageName, "") } returns name
|
||||
coEvery { storagePlugin.getInputStream(token, name) } returns apkInputStream
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
coEvery { backend.load(handle) } returns apkInputStream
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
|
||||
apkRestore.installResult.test {
|
||||
awaitItem() // initial empty state
|
||||
|
@ -151,7 +152,7 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
|
||||
every { installRestriction.isAllowedToInstallApks() } returns true
|
||||
every { backupStateManager.isAutoRestoreEnabled } returns false
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
every { pm.getPackageInfo(packageName, any<Int>()) } throws NameNotFoundException()
|
||||
|
||||
apkRestore.installResult.test {
|
||||
|
@ -177,7 +178,7 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
|
||||
every { installRestriction.isAllowedToInstallApks() } returns true
|
||||
every { backupStateManager.isAutoRestoreEnabled } returns false
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
|
||||
val packageInfo: PackageInfo = mockk()
|
||||
every { pm.getPackageInfo(packageName, any<Int>()) } returns packageInfo
|
||||
|
@ -202,9 +203,9 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
every { backupStateManager.isAutoRestoreEnabled } returns false
|
||||
every { strictContext.cacheDir } returns File(tmpDir.toString())
|
||||
every { crypto.getNameForApk(salt, packageName, "") } returns name
|
||||
coEvery { storagePlugin.getInputStream(token, name) } returns apkInputStream
|
||||
coEvery { backend.load(handle) } returns apkInputStream
|
||||
every { pm.getPackageArchiveInfo(any(), any<Int>()) } returns packageInfo
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
|
||||
apkRestore.installResult.test {
|
||||
awaitItem() // initial empty state
|
||||
|
@ -222,7 +223,7 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
coEvery {
|
||||
apkInstaller.install(match { it.size == 1 }, packageName, installerName, any())
|
||||
} throws SecurityException()
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
|
||||
apkRestore.installResult.test {
|
||||
awaitItem() // initial empty state
|
||||
|
@ -249,7 +250,7 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
coEvery {
|
||||
apkInstaller.install(match { it.size == 1 }, packageName, installerName, any())
|
||||
} returns installResult
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
|
||||
apkRestore.installResult.test {
|
||||
awaitItem() // initial empty state
|
||||
|
@ -285,7 +286,7 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
coEvery {
|
||||
apkInstaller.install(match { it.size == 1 }, packageName, installerName, any())
|
||||
} returns installResult
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
|
||||
apkRestore.installResult.test {
|
||||
awaitItem() // initial empty state
|
||||
|
@ -300,7 +301,7 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
mockkStatic("com.stevesoltys.seedvault.worker.ApkBackupKt")
|
||||
every { installRestriction.isAllowedToInstallApks() } returns true
|
||||
every { backupStateManager.isAutoRestoreEnabled } returns false
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
every { pm.getPackageInfo(packageName, any<Int>()) } returns packageInfo
|
||||
every { packageInfo.signingInfo.getSignatures() } returns packageMetadata.signatures!!
|
||||
every {
|
||||
|
@ -329,7 +330,7 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
mockkStatic("com.stevesoltys.seedvault.worker.ApkBackupKt")
|
||||
every { installRestriction.isAllowedToInstallApks() } returns true
|
||||
every { backupStateManager.isAutoRestoreEnabled } returns false
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
every { pm.getPackageInfo(packageName, any<Int>()) } returns packageInfo
|
||||
every { packageInfo.signingInfo.getSignatures() } returns packageMetadata.signatures!!
|
||||
every { packageInfo.longVersionCode } returns packageMetadata.version!! - 1
|
||||
|
@ -369,7 +370,7 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
mockkStatic("com.stevesoltys.seedvault.worker.ApkBackupKt")
|
||||
every { installRestriction.isAllowedToInstallApks() } returns true
|
||||
every { backupStateManager.isAutoRestoreEnabled } returns false
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
every { pm.getPackageInfo(packageName, any<Int>()) } returns packageInfo
|
||||
every { packageInfo.signingInfo.getSignatures() } returns listOf("foobar")
|
||||
|
||||
|
@ -401,7 +402,7 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
every { backupStateManager.isAutoRestoreEnabled } returns false
|
||||
every { pm.getPackageInfo(packageName, any<Int>()) } throws NameNotFoundException()
|
||||
cacheBaseApkAndGetInfo(tmpDir)
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
|
||||
if (willFail) {
|
||||
every {
|
||||
|
@ -476,7 +477,7 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
every {
|
||||
splitCompatChecker.isCompatible(deviceName, listOf(split1Name, split2Name))
|
||||
} returns false
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
|
||||
apkRestore.installResult.test {
|
||||
awaitItem() // initial empty state
|
||||
|
@ -502,9 +503,9 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
every { splitCompatChecker.isCompatible(deviceName, listOf(splitName)) } returns true
|
||||
every { crypto.getNameForApk(salt, packageName, splitName) } returns suffixName
|
||||
coEvery {
|
||||
storagePlugin.getInputStream(token, suffixName)
|
||||
backend.load(LegacyAppBackupFile.Blob(token, suffixName))
|
||||
} returns ByteArrayInputStream(getRandomByteArray())
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
|
||||
apkRestore.installResult.test {
|
||||
awaitItem() // initial empty state
|
||||
|
@ -531,8 +532,10 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
|
||||
every { splitCompatChecker.isCompatible(deviceName, listOf(splitName)) } returns true
|
||||
every { crypto.getNameForApk(salt, packageName, splitName) } returns suffixName
|
||||
coEvery { storagePlugin.getInputStream(token, suffixName) } throws IOException()
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
coEvery {
|
||||
backend.load(LegacyAppBackupFile.Blob(token, suffixName))
|
||||
} throws IOException()
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
|
||||
apkRestore.installResult.test {
|
||||
awaitItem() // initial empty state
|
||||
|
@ -573,10 +576,14 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
val suffixName1 = getRandomString()
|
||||
val suffixName2 = getRandomString()
|
||||
every { crypto.getNameForApk(salt, packageName, split1Name) } returns suffixName1
|
||||
coEvery { storagePlugin.getInputStream(token, suffixName1) } returns split1InputStream
|
||||
coEvery {
|
||||
backend.load(LegacyAppBackupFile.Blob(token, suffixName1))
|
||||
} returns split1InputStream
|
||||
every { crypto.getNameForApk(salt, packageName, split2Name) } returns suffixName2
|
||||
coEvery { storagePlugin.getInputStream(token, suffixName2) } returns split2InputStream
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
coEvery {
|
||||
backend.load(LegacyAppBackupFile.Blob(token, suffixName2))
|
||||
} returns split2InputStream
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
|
||||
val resultMap = mapOf(
|
||||
packageName to ApkInstallResult(
|
||||
|
@ -602,7 +609,7 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
every { backupStateManager.isAutoRestoreEnabled } returns false
|
||||
// set the storage provider package name to match our current package name,
|
||||
// and ensure that the current package is therefore skipped.
|
||||
every { storagePlugin.providerPackageName } returns packageName
|
||||
every { backend.providerPackageName } returns packageName
|
||||
|
||||
apkRestore.installResult.test {
|
||||
awaitItem() // initial empty state
|
||||
|
@ -627,7 +634,7 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
|
||||
every { installRestriction.isAllowedToInstallApks() } returns true
|
||||
every { backupStateManager.isAutoRestoreEnabled } returns false
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
|
||||
apkRestore.installResult.test {
|
||||
awaitItem() // initial empty state
|
||||
|
@ -656,7 +663,7 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
|
||||
every { installRestriction.isAllowedToInstallApks() } returns true
|
||||
every { backupStateManager.isAutoRestoreEnabled } returns true
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
every { backupManager.setAutoRestore(false) } just Runs
|
||||
every { pm.getPackageInfo(packageName, any<Int>()) } throws NameNotFoundException()
|
||||
// cache APK and get icon as well as app name
|
||||
|
@ -680,7 +687,7 @@ internal class ApkRestoreTest : TransportTest() {
|
|||
@Test
|
||||
fun `no apks get installed when blocked by policy`() = runBlocking {
|
||||
every { installRestriction.isAllowedToInstallApks() } returns false
|
||||
every { storagePlugin.providerPackageName } returns storageProviderPackageName
|
||||
every { backend.providerPackageName } returns storageProviderPackageName
|
||||
|
||||
apkRestore.installResult.test {
|
||||
awaitItem() // initial empty state
|
||||
|
@ -703,7 +710,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 { storagePlugin.getInputStream(token, name) } returns apkInputStream
|
||||
coEvery { backend.load(handle) } returns apkInputStream
|
||||
every { pm.getPackageArchiveInfo(any(), any<Int>()) } returns packageInfo
|
||||
every { applicationInfo.loadIcon(pm) } returns icon
|
||||
every { pm.getApplicationLabel(packageInfo.applicationInfo!!) } returns appName
|
||||
|
|
|
@ -21,9 +21,7 @@ import com.stevesoltys.seedvault.metadata.BackupType
|
|||
import com.stevesoltys.seedvault.metadata.MetadataReaderImpl
|
||||
import com.stevesoltys.seedvault.metadata.PackageMetadata
|
||||
import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.StoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.plugins.saf.FILE_BACKUP_METADATA
|
||||
import com.stevesoltys.seedvault.transport.backup.BackupCoordinator
|
||||
import com.stevesoltys.seedvault.transport.backup.FullBackup
|
||||
import com.stevesoltys.seedvault.transport.backup.InputFactory
|
||||
|
@ -44,6 +42,8 @@ import io.mockk.just
|
|||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||
import org.junit.jupiter.api.Assertions.assertArrayEquals
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.fail
|
||||
|
@ -67,7 +67,7 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
|||
|
||||
@Suppress("Deprecation")
|
||||
private val legacyPlugin = mockk<LegacyStoragePlugin>()
|
||||
private val backupPlugin = mockk<StoragePlugin<*>>()
|
||||
private val backend = mockk<Backend>()
|
||||
private val kvBackup = KVBackup(
|
||||
pluginManager = storagePluginManager,
|
||||
settingsManager = settingsManager,
|
||||
|
@ -132,7 +132,7 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
|||
private val realName = cryptoImpl.getNameForPackage(salt, packageInfo.packageName)
|
||||
|
||||
init {
|
||||
every { storagePluginManager.appPlugin } returns backupPlugin
|
||||
every { storagePluginManager.backend } returns backend
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -161,7 +161,7 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
|||
apkBackup.backupApkIfNecessary(packageInfo, any())
|
||||
} returns packageMetadata
|
||||
coEvery {
|
||||
backupPlugin.getOutputStream(token, FILE_BACKUP_METADATA)
|
||||
backend.save(LegacyAppBackupFile.Metadata(token))
|
||||
} returns metadataOutputStream
|
||||
every {
|
||||
metadataManager.onApkBackedUp(packageInfo, packageMetadata)
|
||||
|
@ -179,7 +179,9 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
|||
assertEquals(TRANSPORT_OK, backup.performIncrementalBackup(packageInfo, fileDescriptor, 0))
|
||||
|
||||
// upload DB
|
||||
coEvery { backupPlugin.getOutputStream(token, realName) } returns bOutputStream
|
||||
coEvery {
|
||||
backend.save(LegacyAppBackupFile.Blob(token, realName))
|
||||
} returns bOutputStream
|
||||
|
||||
// finish K/V backup
|
||||
assertEquals(TRANSPORT_OK, backup.finishBackup())
|
||||
|
@ -198,7 +200,9 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
|||
// restore finds the backed up key and writes the decrypted value
|
||||
val backupDataOutput = mockk<BackupDataOutput>()
|
||||
val rInputStream = ByteArrayInputStream(bOutputStream.toByteArray())
|
||||
coEvery { backupPlugin.getInputStream(token, name) } returns rInputStream
|
||||
coEvery {
|
||||
backend.load(LegacyAppBackupFile.Blob(token, name))
|
||||
} returns rInputStream
|
||||
every { outputFactory.getBackupDataOutput(fileDescriptor) } returns backupDataOutput
|
||||
every { backupDataOutput.writeEntityHeader(key, appData.size) } returns 1137
|
||||
every { backupDataOutput.writeEntityData(appData, appData.size) } returns appData.size
|
||||
|
@ -237,7 +241,7 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
|||
coEvery { apkBackup.backupApkIfNecessary(packageInfo, any()) } returns null
|
||||
every { settingsManager.getToken() } returns token
|
||||
coEvery {
|
||||
backupPlugin.getOutputStream(token, FILE_BACKUP_METADATA)
|
||||
backend.save(LegacyAppBackupFile.Metadata(token))
|
||||
} returns metadataOutputStream
|
||||
every {
|
||||
metadataManager.onPackageBackedUp(
|
||||
|
@ -252,7 +256,9 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
|||
assertEquals(TRANSPORT_OK, backup.performIncrementalBackup(packageInfo, fileDescriptor, 0))
|
||||
|
||||
// upload DB
|
||||
coEvery { backupPlugin.getOutputStream(token, realName) } returns bOutputStream
|
||||
coEvery {
|
||||
backend.save(LegacyAppBackupFile.Blob(token, realName))
|
||||
} returns bOutputStream
|
||||
|
||||
// finish K/V backup
|
||||
assertEquals(TRANSPORT_OK, backup.finishBackup())
|
||||
|
@ -271,7 +277,9 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
|||
// restore finds the backed up key and writes the decrypted value
|
||||
val backupDataOutput = mockk<BackupDataOutput>()
|
||||
val rInputStream = ByteArrayInputStream(bOutputStream.toByteArray())
|
||||
coEvery { backupPlugin.getInputStream(token, name) } returns rInputStream
|
||||
coEvery {
|
||||
backend.load(LegacyAppBackupFile.Blob(token, name))
|
||||
} returns rInputStream
|
||||
every { outputFactory.getBackupDataOutput(fileDescriptor) } returns backupDataOutput
|
||||
every { backupDataOutput.writeEntityHeader(key, appData.size) } returns 1137
|
||||
every { backupDataOutput.writeEntityData(appData, appData.size) } returns appData.size
|
||||
|
@ -294,14 +302,16 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
|||
// return streams from plugin and app data
|
||||
val bOutputStream = ByteArrayOutputStream()
|
||||
val bInputStream = ByteArrayInputStream(appData)
|
||||
coEvery { backupPlugin.getOutputStream(token, realName) } returns bOutputStream
|
||||
coEvery {
|
||||
backend.save(LegacyAppBackupFile.Blob(token, realName))
|
||||
} returns bOutputStream
|
||||
every { inputFactory.getInputStream(fileDescriptor) } returns bInputStream
|
||||
every { settingsManager.isQuotaUnlimited() } returns false
|
||||
coEvery { apkBackup.backupApkIfNecessary(packageInfo, any()) } returns packageMetadata
|
||||
every { settingsManager.getToken() } returns token
|
||||
every { metadataManager.salt } returns salt
|
||||
coEvery {
|
||||
backupPlugin.getOutputStream(token, FILE_BACKUP_METADATA)
|
||||
backend.save(LegacyAppBackupFile.Metadata(token))
|
||||
} returns metadataOutputStream
|
||||
every { metadataManager.onApkBackedUp(packageInfo, packageMetadata) } just Runs
|
||||
every {
|
||||
|
@ -333,7 +343,9 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
|||
// reverse the backup streams into restore input
|
||||
val rInputStream = ByteArrayInputStream(bOutputStream.toByteArray())
|
||||
val rOutputStream = ByteArrayOutputStream()
|
||||
coEvery { backupPlugin.getInputStream(token, name) } returns rInputStream
|
||||
coEvery {
|
||||
backend.load(LegacyAppBackupFile.Blob(token, name))
|
||||
} returns rInputStream
|
||||
every { outputFactory.getOutputStream(fileDescriptor) } returns rOutputStream
|
||||
|
||||
// restore data
|
||||
|
|
|
@ -28,6 +28,7 @@ import io.mockk.every
|
|||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.slot
|
||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD
|
||||
import kotlin.random.Random
|
||||
|
@ -73,6 +74,7 @@ internal abstract class TransportTest {
|
|||
protected val name = getRandomString(12)
|
||||
protected val name2 = getRandomString(23)
|
||||
protected val storageProviderPackageName = getRandomString(23)
|
||||
protected val handle = LegacyAppBackupFile.Blob(token, name)
|
||||
|
||||
init {
|
||||
mockkStatic(Log::class)
|
||||
|
|
|
@ -20,9 +20,7 @@ import com.stevesoltys.seedvault.metadata.BackupType
|
|||
import com.stevesoltys.seedvault.metadata.PackageMetadata
|
||||
import com.stevesoltys.seedvault.metadata.PackageState.NO_DATA
|
||||
import com.stevesoltys.seedvault.metadata.PackageState.QUOTA_EXCEEDED
|
||||
import com.stevesoltys.seedvault.plugins.StoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.plugins.saf.FILE_BACKUP_METADATA
|
||||
import com.stevesoltys.seedvault.plugins.saf.SafStorage
|
||||
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
||||
import com.stevesoltys.seedvault.worker.ApkBackup
|
||||
|
@ -33,6 +31,8 @@ import io.mockk.just
|
|||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.IOException
|
||||
|
@ -60,7 +60,7 @@ internal class BackupCoordinatorTest : BackupTest() {
|
|||
nm = notificationManager,
|
||||
)
|
||||
|
||||
private val plugin = mockk<StoragePlugin<*>>()
|
||||
private val backend = mockk<Backend>()
|
||||
private val metadataOutputStream = mockk<OutputStream>()
|
||||
private val fileDescriptor: ParcelFileDescriptor = mockk()
|
||||
private val packageMetadata: PackageMetadata = mockk()
|
||||
|
@ -73,13 +73,12 @@ internal class BackupCoordinatorTest : BackupTest() {
|
|||
)
|
||||
|
||||
init {
|
||||
every { pluginManager.appPlugin } returns plugin
|
||||
every { pluginManager.backend } returns backend
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `device initialization succeeds and delegates to plugin`() = runBlocking {
|
||||
expectStartNewRestoreSet()
|
||||
coEvery { plugin.initializeDevice() } just Runs
|
||||
every { kv.hasState() } returns false
|
||||
every { full.hasState() } returns false
|
||||
|
||||
|
@ -87,10 +86,9 @@ internal class BackupCoordinatorTest : BackupTest() {
|
|||
assertEquals(TRANSPORT_OK, backup.finishBackup())
|
||||
}
|
||||
|
||||
private suspend fun expectStartNewRestoreSet() {
|
||||
private fun expectStartNewRestoreSet() {
|
||||
every { clock.time() } returns token
|
||||
every { settingsManager.setNewToken(token) } just Runs
|
||||
coEvery { plugin.startNewRestoreSet(token) } just Runs
|
||||
every { metadataManager.onDeviceInitialization(token) } just Runs
|
||||
}
|
||||
|
||||
|
@ -98,8 +96,9 @@ internal class BackupCoordinatorTest : BackupTest() {
|
|||
fun `error notification when device initialization fails`() = runBlocking {
|
||||
val maybeTrue = Random.nextBoolean()
|
||||
|
||||
expectStartNewRestoreSet()
|
||||
coEvery { plugin.initializeDevice() } throws IOException()
|
||||
every { clock.time() } returns token
|
||||
every { settingsManager.setNewToken(token) } just Runs
|
||||
every { metadataManager.onDeviceInitialization(token) } throws IOException()
|
||||
every { metadataManager.requiresInit } returns maybeTrue
|
||||
every { pluginManager.canDoBackupNow() } returns !maybeTrue
|
||||
every { notificationManager.onBackupError() } just Runs
|
||||
|
@ -117,8 +116,9 @@ internal class BackupCoordinatorTest : BackupTest() {
|
|||
@Test
|
||||
fun `no error notification when device initialization fails when no backup possible`() =
|
||||
runBlocking {
|
||||
expectStartNewRestoreSet()
|
||||
coEvery { plugin.initializeDevice() } throws IOException()
|
||||
every { clock.time() } returns token
|
||||
every { settingsManager.setNewToken(token) } just Runs
|
||||
every { metadataManager.onDeviceInitialization(token) } throws IOException()
|
||||
every { metadataManager.requiresInit } returns false
|
||||
every { pluginManager.canDoBackupNow() } returns false
|
||||
|
||||
|
@ -142,7 +142,6 @@ internal class BackupCoordinatorTest : BackupTest() {
|
|||
// start new restore set
|
||||
every { clock.time() } returns token + 1
|
||||
every { settingsManager.setNewToken(token + 1) } just Runs
|
||||
coEvery { plugin.startNewRestoreSet(token + 1) } just Runs
|
||||
every { metadataManager.onDeviceInitialization(token + 1) } just Runs
|
||||
|
||||
every { data.close() } just Runs
|
||||
|
@ -210,7 +209,7 @@ internal class BackupCoordinatorTest : BackupTest() {
|
|||
every { kv.getCurrentPackage() } returns packageInfo
|
||||
coEvery { kv.finishBackup() } returns TRANSPORT_OK
|
||||
every { settingsManager.getToken() } returns token
|
||||
coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream
|
||||
coEvery { backend.save(LegacyAppBackupFile.Metadata(token)) } returns metadataOutputStream
|
||||
every { kv.getCurrentSize() } returns size
|
||||
every {
|
||||
metadataManager.onPackageBackedUp(
|
||||
|
@ -250,7 +249,7 @@ internal class BackupCoordinatorTest : BackupTest() {
|
|||
every { full.getCurrentPackage() } returns packageInfo
|
||||
every { full.finishBackup() } returns result
|
||||
every { settingsManager.getToken() } returns token
|
||||
coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream
|
||||
coEvery { backend.save(LegacyAppBackupFile.Metadata(token)) } returns metadataOutputStream
|
||||
every { full.getCurrentSize() } returns size
|
||||
every {
|
||||
metadataManager.onPackageBackedUp(
|
||||
|
@ -385,7 +384,7 @@ internal class BackupCoordinatorTest : BackupTest() {
|
|||
private fun expectApkBackupAndMetadataWrite() {
|
||||
coEvery { apkBackup.backupApkIfNecessary(any(), any()) } returns packageMetadata
|
||||
every { settingsManager.getToken() } returns token
|
||||
coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream
|
||||
coEvery { backend.save(LegacyAppBackupFile.Metadata(token)) } returns metadataOutputStream
|
||||
every { metadataManager.onApkBackedUp(any(), packageMetadata) } just Runs
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ 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 com.stevesoltys.seedvault.plugins.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
||||
import io.mockk.Runs
|
||||
|
@ -20,6 +19,8 @@ import io.mockk.every
|
|||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
|
@ -31,7 +32,7 @@ import kotlin.random.Random
|
|||
internal class FullBackupTest : BackupTest() {
|
||||
|
||||
private val storagePluginManager: StoragePluginManager = mockk()
|
||||
private val plugin = mockk<StoragePlugin<*>>()
|
||||
private val backend = mockk<Backend>()
|
||||
private val notificationManager = mockk<BackupNotificationManager>()
|
||||
private val backup = FullBackup(
|
||||
pluginManager = storagePluginManager,
|
||||
|
@ -46,7 +47,7 @@ internal class FullBackupTest : BackupTest() {
|
|||
private val ad = getADForFull(VERSION, packageInfo.packageName)
|
||||
|
||||
init {
|
||||
every { storagePluginManager.appPlugin } returns plugin
|
||||
every { storagePluginManager.backend } returns backend
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -167,7 +168,7 @@ internal class FullBackupTest : BackupTest() {
|
|||
|
||||
every { settingsManager.isQuotaUnlimited() } returns false
|
||||
every { crypto.getNameForPackage(salt, packageInfo.packageName) } returns name
|
||||
coEvery { plugin.getOutputStream(token, name) } throws IOException()
|
||||
coEvery { backend.save(handle) } throws IOException()
|
||||
expectClearState()
|
||||
|
||||
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0, token, salt))
|
||||
|
@ -184,7 +185,7 @@ internal class FullBackupTest : BackupTest() {
|
|||
|
||||
every { settingsManager.isQuotaUnlimited() } returns false
|
||||
every { crypto.getNameForPackage(salt, packageInfo.packageName) } returns name
|
||||
coEvery { plugin.getOutputStream(token, name) } returns outputStream
|
||||
coEvery { backend.save(handle) } returns outputStream
|
||||
every { inputFactory.getInputStream(data) } returns inputStream
|
||||
every { outputStream.write(ByteArray(1) { VERSION }) } throws IOException()
|
||||
expectClearState()
|
||||
|
@ -240,7 +241,7 @@ internal class FullBackupTest : BackupTest() {
|
|||
@Test
|
||||
fun `clearBackupData delegates to plugin`() = runBlocking {
|
||||
every { crypto.getNameForPackage(salt, packageInfo.packageName) } returns name
|
||||
coEvery { plugin.removeData(token, name) } just Runs
|
||||
coEvery { backend.remove(handle) } just Runs
|
||||
|
||||
backup.clearBackupData(packageInfo, token, salt)
|
||||
}
|
||||
|
@ -251,7 +252,7 @@ internal class FullBackupTest : BackupTest() {
|
|||
expectInitializeOutputStream()
|
||||
expectClearState()
|
||||
every { crypto.getNameForPackage(salt, packageInfo.packageName) } returns name
|
||||
coEvery { plugin.removeData(token, name) } just Runs
|
||||
coEvery { backend.remove(handle) } just Runs
|
||||
|
||||
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0, token, salt))
|
||||
assertTrue(backup.hasState())
|
||||
|
@ -265,7 +266,7 @@ internal class FullBackupTest : BackupTest() {
|
|||
expectInitializeOutputStream()
|
||||
expectClearState()
|
||||
every { crypto.getNameForPackage(salt, packageInfo.packageName) } returns name
|
||||
coEvery { plugin.removeData(token, name) } throws IOException()
|
||||
coEvery { backend.remove(handle) } throws IOException()
|
||||
|
||||
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0, token, salt))
|
||||
assertTrue(backup.hasState())
|
||||
|
@ -336,7 +337,9 @@ internal class FullBackupTest : BackupTest() {
|
|||
|
||||
private fun expectInitializeOutputStream() {
|
||||
every { crypto.getNameForPackage(salt, packageInfo.packageName) } returns name
|
||||
coEvery { plugin.getOutputStream(token, name) } returns outputStream
|
||||
coEvery {
|
||||
backend.save(LegacyAppBackupFile.Blob(token, name))
|
||||
} returns outputStream
|
||||
every { outputStream.write(ByteArray(1) { VERSION }) } just Runs
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ 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 com.stevesoltys.seedvault.plugins.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
||||
import io.mockk.CapturingSlot
|
||||
|
@ -29,6 +28,7 @@ import io.mockk.just
|
|||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
|
@ -54,7 +54,7 @@ internal class KVBackupTest : BackupTest() {
|
|||
)
|
||||
|
||||
private val db = mockk<KVDb>()
|
||||
private val plugin = mockk<StoragePlugin<*>>()
|
||||
private val backend = mockk<Backend>()
|
||||
private val packageName = packageInfo.packageName
|
||||
private val key = getRandomString(MAX_KEY_LENGTH_SIZE)
|
||||
private val dataValue = Random.nextBytes(23)
|
||||
|
@ -62,7 +62,7 @@ internal class KVBackupTest : BackupTest() {
|
|||
private val inputStream = ByteArrayInputStream(dbBytes)
|
||||
|
||||
init {
|
||||
every { pluginManager.appPlugin } returns plugin
|
||||
every { pluginManager.backend } returns backend
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -96,7 +96,7 @@ internal class KVBackupTest : BackupTest() {
|
|||
@Test
|
||||
fun `non-incremental backup with data clears old data first`() = runBlocking {
|
||||
singleRecordBackup(true)
|
||||
coEvery { plugin.removeData(token, name) } just Runs
|
||||
coEvery { backend.remove(handle) } just Runs
|
||||
every { dbManager.deleteDb(packageName) } returns true
|
||||
|
||||
assertEquals(
|
||||
|
@ -112,7 +112,7 @@ internal class KVBackupTest : BackupTest() {
|
|||
fun `ignoring exception when clearing data when non-incremental backup has data`() =
|
||||
runBlocking {
|
||||
singleRecordBackup(true)
|
||||
coEvery { plugin.removeData(token, name) } throws IOException()
|
||||
coEvery { backend.remove(handle) } throws IOException()
|
||||
|
||||
assertEquals(
|
||||
TRANSPORT_OK,
|
||||
|
@ -210,7 +210,7 @@ internal class KVBackupTest : BackupTest() {
|
|||
|
||||
every { db.vacuum() } just Runs
|
||||
every { db.close() } just Runs
|
||||
coEvery { plugin.getOutputStream(token, name) } returns outputStream
|
||||
coEvery { backend.save(handle) } returns outputStream
|
||||
every { outputStream.write(ByteArray(1) { VERSION }) } throws IOException()
|
||||
every { outputStream.close() } just Runs
|
||||
assertEquals(TRANSPORT_ERROR, backup.finishBackup())
|
||||
|
@ -230,7 +230,7 @@ internal class KVBackupTest : BackupTest() {
|
|||
|
||||
every { db.vacuum() } just Runs
|
||||
every { db.close() } just Runs
|
||||
coEvery { plugin.getOutputStream(token, name) } returns outputStream
|
||||
coEvery { backend.save(handle) } returns outputStream
|
||||
every { outputStream.write(ByteArray(1) { VERSION }) } just Runs
|
||||
val ad = getADForKV(VERSION, packageInfo.packageName)
|
||||
every { crypto.newEncryptingStream(outputStream, ad) } returns encryptedOutputStream
|
||||
|
@ -264,7 +264,7 @@ internal class KVBackupTest : BackupTest() {
|
|||
assertFalse(backup.hasState())
|
||||
|
||||
coVerify(exactly = 0) {
|
||||
plugin.getOutputStream(token, name)
|
||||
backend.save(handle)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -301,7 +301,7 @@ internal class KVBackupTest : BackupTest() {
|
|||
every { db.vacuum() } just Runs
|
||||
every { db.close() } just Runs
|
||||
|
||||
coEvery { plugin.getOutputStream(token, name) } returns outputStream
|
||||
coEvery { backend.save(handle) } returns outputStream
|
||||
every { outputStream.write(ByteArray(1) { VERSION }) } just Runs
|
||||
val ad = getADForKV(VERSION, packageInfo.packageName)
|
||||
every { crypto.newEncryptingStream(outputStream, ad) } returns encryptedOutputStream
|
||||
|
|
|
@ -17,7 +17,6 @@ import com.stevesoltys.seedvault.header.VERSION
|
|||
import com.stevesoltys.seedvault.header.VersionHeader
|
||||
import com.stevesoltys.seedvault.header.getADForFull
|
||||
import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.StoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
||||
import io.mockk.CapturingSlot
|
||||
import io.mockk.Runs
|
||||
|
@ -26,6 +25,7 @@ import io.mockk.every
|
|||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.junit.jupiter.api.Assertions.assertArrayEquals
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
|
@ -40,7 +40,7 @@ import kotlin.random.Random
|
|||
internal class FullRestoreTest : RestoreTest() {
|
||||
|
||||
private val storagePluginManager: StoragePluginManager = mockk()
|
||||
private val plugin = mockk<StoragePlugin<*>>()
|
||||
private val backend = mockk<Backend>()
|
||||
private val legacyPlugin = mockk<LegacyStoragePlugin>()
|
||||
private val restore = FullRestore(
|
||||
pluginManager = storagePluginManager,
|
||||
|
@ -55,7 +55,7 @@ internal class FullRestoreTest : RestoreTest() {
|
|||
private val ad = getADForFull(VERSION, packageInfo.packageName)
|
||||
|
||||
init {
|
||||
every { storagePluginManager.appPlugin } returns plugin
|
||||
every { storagePluginManager.backend } returns backend
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -90,7 +90,7 @@ internal class FullRestoreTest : RestoreTest() {
|
|||
fun `getting InputStream for package when getting first chunk throws`() = runBlocking {
|
||||
restore.initializeState(VERSION, token, name, packageInfo)
|
||||
|
||||
coEvery { plugin.getInputStream(token, name) } throws IOException()
|
||||
coEvery { backend.load(handle) } throws IOException()
|
||||
every { fileDescriptor.close() } just Runs
|
||||
|
||||
assertEquals(
|
||||
|
@ -103,7 +103,7 @@ internal class FullRestoreTest : RestoreTest() {
|
|||
fun `reading version header when getting first chunk throws`() = runBlocking {
|
||||
restore.initializeState(VERSION, token, name, packageInfo)
|
||||
|
||||
coEvery { plugin.getInputStream(token, name) } returns inputStream
|
||||
coEvery { backend.load(handle) } returns inputStream
|
||||
every { headerReader.readVersion(inputStream, VERSION) } throws IOException()
|
||||
every { fileDescriptor.close() } just Runs
|
||||
|
||||
|
@ -117,7 +117,7 @@ internal class FullRestoreTest : RestoreTest() {
|
|||
fun `reading unsupported version when getting first chunk`() = runBlocking {
|
||||
restore.initializeState(VERSION, token, name, packageInfo)
|
||||
|
||||
coEvery { plugin.getInputStream(token, name) } returns inputStream
|
||||
coEvery { backend.load(handle) } returns inputStream
|
||||
every {
|
||||
headerReader.readVersion(inputStream, VERSION)
|
||||
} throws UnsupportedVersionException(unsupportedVersion)
|
||||
|
@ -133,7 +133,7 @@ internal class FullRestoreTest : RestoreTest() {
|
|||
fun `getting decrypted stream when getting first chunk throws`() = runBlocking {
|
||||
restore.initializeState(VERSION, token, name, packageInfo)
|
||||
|
||||
coEvery { plugin.getInputStream(token, name) } returns inputStream
|
||||
coEvery { backend.load(handle) } returns inputStream
|
||||
every { headerReader.readVersion(inputStream, VERSION) } returns VERSION
|
||||
every { crypto.newDecryptingStream(inputStream, ad) } throws IOException()
|
||||
every { fileDescriptor.close() } just Runs
|
||||
|
@ -149,7 +149,7 @@ internal class FullRestoreTest : RestoreTest() {
|
|||
runBlocking {
|
||||
restore.initializeState(VERSION, token, name, packageInfo)
|
||||
|
||||
coEvery { plugin.getInputStream(token, name) } returns inputStream
|
||||
coEvery { backend.load(handle) } returns inputStream
|
||||
every { headerReader.readVersion(inputStream, VERSION) } returns VERSION
|
||||
every { crypto.newDecryptingStream(inputStream, ad) } throws GeneralSecurityException()
|
||||
every { fileDescriptor.close() } just Runs
|
||||
|
@ -197,7 +197,7 @@ internal class FullRestoreTest : RestoreTest() {
|
|||
fun `unexpected version aborts with error`() = runBlocking {
|
||||
restore.initializeState(Byte.MAX_VALUE, token, name, packageInfo)
|
||||
|
||||
coEvery { plugin.getInputStream(token, name) } returns inputStream
|
||||
coEvery { backend.load(handle) } returns inputStream
|
||||
every {
|
||||
headerReader.readVersion(inputStream, Byte.MAX_VALUE)
|
||||
} throws GeneralSecurityException()
|
||||
|
@ -215,7 +215,7 @@ internal class FullRestoreTest : RestoreTest() {
|
|||
val decryptedInputStream = ByteArrayInputStream(encryptedBytes)
|
||||
restore.initializeState(VERSION, token, name, packageInfo)
|
||||
|
||||
coEvery { plugin.getInputStream(token, name) } returns inputStream
|
||||
coEvery { backend.load(handle) } returns inputStream
|
||||
every { headerReader.readVersion(inputStream, VERSION) } returns VERSION
|
||||
every { crypto.newDecryptingStream(inputStream, ad) } returns decryptedInputStream
|
||||
every { outputFactory.getOutputStream(fileDescriptor) } returns outputStream
|
||||
|
@ -248,7 +248,7 @@ internal class FullRestoreTest : RestoreTest() {
|
|||
}
|
||||
|
||||
private fun initInputStream() {
|
||||
coEvery { plugin.getInputStream(token, name) } returns inputStream
|
||||
coEvery { backend.load(handle) } returns inputStream
|
||||
every { headerReader.readVersion(inputStream, VERSION) } returns VERSION
|
||||
every { crypto.newDecryptingStream(inputStream, ad) } returns decryptedInputStream
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ import com.stevesoltys.seedvault.header.VERSION
|
|||
import com.stevesoltys.seedvault.header.VersionHeader
|
||||
import com.stevesoltys.seedvault.header.getADForKV
|
||||
import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.StoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.transport.backup.KVDb
|
||||
import com.stevesoltys.seedvault.transport.backup.KvDbManager
|
||||
|
@ -29,6 +28,7 @@ import io.mockk.mockkStatic
|
|||
import io.mockk.verify
|
||||
import io.mockk.verifyAll
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.ByteArrayInputStream
|
||||
|
@ -42,7 +42,7 @@ import kotlin.random.Random
|
|||
internal class KVRestoreTest : RestoreTest() {
|
||||
|
||||
private val storagePluginManager: StoragePluginManager = mockk()
|
||||
private val plugin = mockk<StoragePlugin<*>>()
|
||||
private val backend = mockk<Backend>()
|
||||
@Suppress("DEPRECATION")
|
||||
private val legacyPlugin = mockk<LegacyStoragePlugin>()
|
||||
private val dbManager = mockk<KvDbManager>()
|
||||
|
@ -74,7 +74,7 @@ internal class KVRestoreTest : RestoreTest() {
|
|||
// for InputStream#readBytes()
|
||||
mockkStatic("kotlin.io.ByteStreamsKt")
|
||||
|
||||
every { storagePluginManager.appPlugin } returns plugin
|
||||
every { storagePluginManager.backend } returns backend
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -88,7 +88,7 @@ internal class KVRestoreTest : RestoreTest() {
|
|||
fun `unexpected version aborts with error`() = runBlocking {
|
||||
restore.initializeState(VERSION, token, name, packageInfo)
|
||||
|
||||
coEvery { plugin.getInputStream(token, name) } returns inputStream
|
||||
coEvery { backend.load(handle) } returns inputStream
|
||||
every {
|
||||
headerReader.readVersion(inputStream, VERSION)
|
||||
} throws UnsupportedVersionException(Byte.MAX_VALUE)
|
||||
|
@ -103,7 +103,7 @@ internal class KVRestoreTest : RestoreTest() {
|
|||
fun `newDecryptingStream throws`() = runBlocking {
|
||||
restore.initializeState(VERSION, token, name, packageInfo)
|
||||
|
||||
coEvery { plugin.getInputStream(token, name) } returns inputStream
|
||||
coEvery { backend.load(handle) } returns inputStream
|
||||
every { headerReader.readVersion(inputStream, VERSION) } returns VERSION
|
||||
every { crypto.newDecryptingStream(inputStream, ad) } throws GeneralSecurityException()
|
||||
every { dbManager.deleteDb(packageInfo.packageName, true) } returns true
|
||||
|
@ -121,7 +121,7 @@ internal class KVRestoreTest : RestoreTest() {
|
|||
fun `writeEntityHeader throws`() = runBlocking {
|
||||
restore.initializeState(VERSION, token, name, packageInfo)
|
||||
|
||||
coEvery { plugin.getInputStream(token, name) } returns inputStream
|
||||
coEvery { backend.load(handle) } returns inputStream
|
||||
every { headerReader.readVersion(inputStream, VERSION) } returns VERSION
|
||||
every { crypto.newDecryptingStream(inputStream, ad) } returns decryptInputStream
|
||||
every {
|
||||
|
@ -146,7 +146,7 @@ internal class KVRestoreTest : RestoreTest() {
|
|||
fun `two records get restored`() = runBlocking {
|
||||
restore.initializeState(VERSION, token, name, packageInfo)
|
||||
|
||||
coEvery { plugin.getInputStream(token, name) } returns inputStream
|
||||
coEvery { backend.load(handle) } returns inputStream
|
||||
every { headerReader.readVersion(inputStream, VERSION) } returns VERSION
|
||||
every { crypto.newDecryptingStream(inputStream, ad) } returns decryptInputStream
|
||||
every {
|
||||
|
|
|
@ -20,8 +20,8 @@ import com.stevesoltys.seedvault.metadata.BackupType
|
|||
import com.stevesoltys.seedvault.metadata.MetadataReader
|
||||
import com.stevesoltys.seedvault.metadata.PackageMetadata
|
||||
import com.stevesoltys.seedvault.plugins.EncryptedMetadata
|
||||
import com.stevesoltys.seedvault.plugins.StoragePlugin
|
||||
import com.stevesoltys.seedvault.plugins.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.plugins.getAvailableBackups
|
||||
import com.stevesoltys.seedvault.plugins.saf.SafStorage
|
||||
import com.stevesoltys.seedvault.transport.TransportTest
|
||||
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
||||
|
@ -30,8 +30,10 @@ import io.mockk.coEvery
|
|||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
import org.junit.jupiter.api.Assertions.assertThrows
|
||||
|
@ -45,7 +47,7 @@ internal class RestoreCoordinatorTest : TransportTest() {
|
|||
|
||||
private val notificationManager: BackupNotificationManager = mockk()
|
||||
private val storagePluginManager: StoragePluginManager = mockk()
|
||||
private val plugin = mockk<StoragePlugin<*>>()
|
||||
private val backend = mockk<Backend>()
|
||||
private val kv = mockk<KVRestore>()
|
||||
private val full = mockk<FullRestore>()
|
||||
private val metadataReader = mockk<MetadataReader>()
|
||||
|
@ -78,14 +80,15 @@ internal class RestoreCoordinatorTest : TransportTest() {
|
|||
metadata.packageMetadataMap[packageInfo2.packageName] =
|
||||
PackageMetadata(backupType = BackupType.FULL)
|
||||
|
||||
every { storagePluginManager.appPlugin } returns plugin
|
||||
mockkStatic("com.stevesoltys.seedvault.plugins.BackendExtKt")
|
||||
every { storagePluginManager.backend } returns backend
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getAvailableRestoreSets() builds set from plugin response`() = runBlocking {
|
||||
val encryptedMetadata = EncryptedMetadata(token) { inputStream }
|
||||
|
||||
coEvery { plugin.getAvailableBackups() } returns sequenceOf(
|
||||
coEvery { backend.getAvailableBackups() } returns sequenceOf(
|
||||
encryptedMetadata,
|
||||
EncryptedMetadata(token + 1) { inputStream }
|
||||
)
|
||||
|
@ -123,7 +126,7 @@ internal class RestoreCoordinatorTest : TransportTest() {
|
|||
|
||||
@Test
|
||||
fun `startRestore() fetches metadata if missing`() = runBlocking {
|
||||
coEvery { plugin.getAvailableBackups() } returns sequenceOf(
|
||||
coEvery { backend.getAvailableBackups() } returns sequenceOf(
|
||||
EncryptedMetadata(token) { inputStream },
|
||||
EncryptedMetadata(token + 1) { inputStream }
|
||||
)
|
||||
|
@ -136,7 +139,7 @@ internal class RestoreCoordinatorTest : TransportTest() {
|
|||
|
||||
@Test
|
||||
fun `startRestore() errors if metadata is not matching token`() = runBlocking {
|
||||
coEvery { plugin.getAvailableBackups() } returns sequenceOf(
|
||||
coEvery { backend.getAvailableBackups() } returns sequenceOf(
|
||||
EncryptedMetadata(token + 42) { inputStream }
|
||||
)
|
||||
every { metadataReader.readMetadata(inputStream, token + 42) } returns metadata
|
||||
|
|
|
@ -30,6 +30,7 @@ import io.mockk.every
|
|||
import io.mockk.mockk
|
||||
import io.mockk.verifyOrder
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.junit.jupiter.api.Assertions.assertArrayEquals
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.fail
|
||||
|
@ -58,7 +59,7 @@ internal class RestoreV0IntegrationTest : TransportTest() {
|
|||
|
||||
@Suppress("Deprecation")
|
||||
private val legacyPlugin = mockk<LegacyStoragePlugin>()
|
||||
private val backupPlugin = mockk<StoragePlugin<*>>()
|
||||
private val backend = mockk<Backend>()
|
||||
private val kvRestore = KVRestore(
|
||||
pluginManager = storagePluginManager,
|
||||
legacyPlugin = legacyPlugin,
|
||||
|
@ -123,7 +124,7 @@ internal class RestoreV0IntegrationTest : TransportTest() {
|
|||
private val key264 = key2.encodeBase64()
|
||||
|
||||
init {
|
||||
every { storagePluginManager.appPlugin } returns backupPlugin
|
||||
every { storagePluginManager.backend } returns backend
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -14,9 +14,7 @@ import com.stevesoltys.seedvault.metadata.PackageMetadata
|
|||
import com.stevesoltys.seedvault.metadata.PackageState.NOT_ALLOWED
|
||||
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.StoragePluginManager
|
||||
import com.stevesoltys.seedvault.plugins.saf.FILE_BACKUP_METADATA
|
||||
import com.stevesoltys.seedvault.transport.TransportTest
|
||||
import com.stevesoltys.seedvault.transport.backup.PackageService
|
||||
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
||||
|
@ -30,6 +28,8 @@ import io.mockk.mockk
|
|||
import io.mockk.verify
|
||||
import io.mockk.verifyAll
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
|
@ -41,7 +41,7 @@ internal class ApkBackupManagerTest : TransportTest() {
|
|||
private val apkBackup: ApkBackup = mockk()
|
||||
private val iconManager: IconManager = mockk()
|
||||
private val storagePluginManager: StoragePluginManager = mockk()
|
||||
private val plugin: StoragePlugin<*> = mockk()
|
||||
private val backend: Backend = mockk()
|
||||
private val nm: BackupNotificationManager = mockk()
|
||||
|
||||
private val apkBackupManager = ApkBackupManager(
|
||||
|
@ -59,7 +59,7 @@ internal class ApkBackupManagerTest : TransportTest() {
|
|||
private val packageMetadata: PackageMetadata = mockk()
|
||||
|
||||
init {
|
||||
every { storagePluginManager.appPlugin } returns plugin
|
||||
every { storagePluginManager.backend } returns backend
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -258,7 +258,7 @@ internal class ApkBackupManagerTest : TransportTest() {
|
|||
|
||||
// final upload
|
||||
every { settingsManager.getToken() } returns token
|
||||
coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream
|
||||
coEvery { backend.save(LegacyAppBackupFile.Metadata(token)) } returns metadataOutputStream
|
||||
every {
|
||||
metadataManager.uploadMetadata(metadataOutputStream)
|
||||
} throws IOException() andThenThrows SecurityException() andThenJust Runs
|
||||
|
@ -277,7 +277,7 @@ internal class ApkBackupManagerTest : TransportTest() {
|
|||
private suspend fun expectUploadIcons() {
|
||||
every { settingsManager.getToken() } returns token
|
||||
val stream = ByteArrayOutputStream()
|
||||
coEvery { plugin.getOutputStream(token, FILE_BACKUP_ICONS) } returns stream
|
||||
coEvery { backend.save(LegacyAppBackupFile.IconsFile(token)) } returns stream
|
||||
every { iconManager.uploadIcons(token, stream) } just Runs
|
||||
}
|
||||
|
||||
|
@ -288,7 +288,7 @@ internal class ApkBackupManagerTest : TransportTest() {
|
|||
|
||||
private fun expectFinalUpload() {
|
||||
every { settingsManager.getToken() } returns token
|
||||
coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream
|
||||
coEvery { backend.save(LegacyAppBackupFile.Metadata(token)) } returns metadataOutputStream
|
||||
every { metadataManager.uploadMetadata(metadataOutputStream) } just Runs
|
||||
every { metadataOutputStream.close() } just Runs
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
org.slf4j.simpleLogger.defaultLogLevel=trace
|
||||
#org.slf4j.simpleLogger.defaultLogLevel=debug
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
package org.calyxos.seedvault.core.backends
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import okio.BufferedSink
|
||||
import okio.BufferedSource
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
public interface Backend {
|
||||
|
@ -25,9 +25,9 @@ public interface Backend {
|
|||
*/
|
||||
public suspend fun getFreeSpace(): Long?
|
||||
|
||||
public suspend fun save(handle: FileHandle): BufferedSink
|
||||
public suspend fun save(handle: FileHandle): OutputStream
|
||||
|
||||
public suspend fun load(handle: FileHandle): BufferedSource
|
||||
public suspend fun load(handle: FileHandle): InputStream
|
||||
|
||||
public suspend fun list(
|
||||
topLevelFolder: TopLevelFolder?,
|
||||
|
|
|
@ -56,8 +56,8 @@ public abstract class BackendTest {
|
|||
assertNotNull(metadata)
|
||||
assertNotNull(snapshot)
|
||||
|
||||
assertArrayEquals(bytes1, plugin.load(metadata as FileHandle).readByteArray())
|
||||
assertArrayEquals(bytes2, plugin.load(snapshot as FileHandle).readByteArray())
|
||||
assertArrayEquals(bytes1, plugin.load(metadata as FileHandle).readAllBytes())
|
||||
assertArrayEquals(bytes2, plugin.load(snapshot as FileHandle).readAllBytes())
|
||||
|
||||
val blobName = Random.nextBytes(32).toHexString()
|
||||
var blob: FileBackupFileType.Blob? = null
|
||||
|
@ -77,7 +77,7 @@ public abstract class BackendTest {
|
|||
}
|
||||
}
|
||||
assertNotNull(blob)
|
||||
assertArrayEquals(bytes3, plugin.load(blob as FileHandle).readByteArray())
|
||||
assertArrayEquals(bytes3, plugin.load(blob as FileHandle).readAllBytes())
|
||||
|
||||
// try listing with top-level folder, should find two files of FileBackupFileType in there
|
||||
var numFiles = 0
|
||||
|
|
|
@ -15,11 +15,6 @@ import android.provider.DocumentsContract.renameDocument
|
|||
import androidx.core.database.getIntOrNull
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import okio.BufferedSink
|
||||
import okio.BufferedSource
|
||||
import okio.buffer
|
||||
import okio.sink
|
||||
import okio.source
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.calyxos.seedvault.core.backends.Constants.DIRECTORY_ROOT
|
||||
import org.calyxos.seedvault.core.backends.Constants.FILE_BACKUP_METADATA
|
||||
|
@ -35,6 +30,8 @@ import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
|||
import org.calyxos.seedvault.core.backends.TopLevelFolder
|
||||
import org.calyxos.seedvault.core.getBackendContext
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
internal const val AUTHORITY_STORAGE = "com.android.externalstorage.documents"
|
||||
|
@ -84,14 +81,14 @@ public class SafBackend(
|
|||
} else bytesAvailable
|
||||
}
|
||||
|
||||
override suspend fun save(handle: FileHandle): BufferedSink {
|
||||
override suspend fun save(handle: FileHandle): OutputStream {
|
||||
val file = cache.getFile(handle)
|
||||
return file.getOutputStream(context.contentResolver).sink().buffer()
|
||||
return file.getOutputStream(context.contentResolver)
|
||||
}
|
||||
|
||||
override suspend fun load(handle: FileHandle): BufferedSource {
|
||||
override suspend fun load(handle: FileHandle): InputStream {
|
||||
val file = cache.getFile(handle)
|
||||
return file.getInputStream(context.contentResolver).source().buffer()
|
||||
return file.getInputStream(context.contentResolver)
|
||||
}
|
||||
|
||||
override suspend fun list(
|
||||
|
|
|
@ -26,9 +26,6 @@ import okhttp3.MediaType.Companion.toMediaType
|
|||
import okhttp3.OkHttpClient
|
||||
import okhttp3.RequestBody
|
||||
import okio.BufferedSink
|
||||
import okio.BufferedSource
|
||||
import okio.buffer
|
||||
import okio.sink
|
||||
import org.calyxos.seedvault.core.backends.Backend
|
||||
import org.calyxos.seedvault.core.backends.Constants.DIRECTORY_ROOT
|
||||
import org.calyxos.seedvault.core.backends.Constants.FILE_BACKUP_METADATA
|
||||
|
@ -43,6 +40,8 @@ import org.calyxos.seedvault.core.backends.FileInfo
|
|||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||
import org.calyxos.seedvault.core.backends.TopLevelFolder
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.io.PipedInputStream
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.coroutines.resume
|
||||
|
@ -123,7 +122,7 @@ public class WebDavBackend(
|
|||
return availableBytes
|
||||
}
|
||||
|
||||
override suspend fun save(handle: FileHandle): BufferedSink {
|
||||
override suspend fun save(handle: FileHandle): OutputStream {
|
||||
val location = handle.toHttpUrl()
|
||||
val davCollection = DavCollection(okHttpClient, location)
|
||||
davCollection.ensureFoldersExist(log, folders)
|
||||
|
@ -152,10 +151,10 @@ public class WebDavBackend(
|
|||
deferred.await()
|
||||
}
|
||||
}
|
||||
return pipedOutputStream.sink().buffer()
|
||||
return pipedOutputStream
|
||||
}
|
||||
|
||||
override suspend fun load(handle: FileHandle): BufferedSource {
|
||||
override suspend fun load(handle: FileHandle): InputStream {
|
||||
val location = handle.toHttpUrl()
|
||||
val davCollection = DavCollection(okHttpClient, location)
|
||||
|
||||
|
@ -167,7 +166,7 @@ public class WebDavBackend(
|
|||
}
|
||||
log.debugLog { "load($location) = $response" }
|
||||
if (response.code / 100 != 2) throw IOException("HTTP error ${response.code}")
|
||||
return response.body?.source() ?: throw IOException("Body was null for $location")
|
||||
return response.body?.byteStream() ?: throw IOException("Body was null for $location")
|
||||
}
|
||||
|
||||
override suspend fun list(
|
||||
|
|
|
@ -56,13 +56,13 @@ public abstract class SafStoragePlugin(
|
|||
@Throws(IOException::class)
|
||||
override suspend fun getChunkOutputStream(chunkId: String): OutputStream {
|
||||
val fileHandle = FileBackupFileType.Blob(androidId, chunkId)
|
||||
return delegate.save(fileHandle).outputStream()
|
||||
return delegate.save(fileHandle)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override suspend fun getBackupSnapshotOutputStream(timestamp: Long): OutputStream {
|
||||
val fileHandle = FileBackupFileType.Snapshot(androidId, timestamp)
|
||||
return delegate.save(fileHandle).outputStream()
|
||||
return delegate.save(fileHandle)
|
||||
}
|
||||
|
||||
/************************* Restore *******************************/
|
||||
|
@ -84,7 +84,7 @@ public abstract class SafStoragePlugin(
|
|||
override suspend fun getBackupSnapshotInputStream(storedSnapshot: StoredSnapshot): InputStream {
|
||||
val androidId = storedSnapshot.androidId
|
||||
val handle = FileBackupFileType.Snapshot(androidId, storedSnapshot.timestamp)
|
||||
return delegate.load(handle).inputStream()
|
||||
return delegate.load(handle)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
|
@ -93,7 +93,7 @@ public abstract class SafStoragePlugin(
|
|||
chunkId: String,
|
||||
): InputStream {
|
||||
val handle = FileBackupFileType.Blob(snapshot.androidId, chunkId)
|
||||
return delegate.load(handle).inputStream()
|
||||
return delegate.load(handle)
|
||||
}
|
||||
|
||||
/************************* Pruning *******************************/
|
||||
|
|
Loading…
Reference in a new issue