Use new storage API for full restore
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/FullRestore.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/FullRestore.kt
index 6769036..67fc1da 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/FullRestore.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/FullRestore.kt
@@ -12,6 +12,7 @@
import com.stevesoltys.seedvault.header.MAX_SEGMENT_LENGTH
import com.stevesoltys.seedvault.header.UnsupportedVersionException
import com.stevesoltys.seedvault.header.getADForFull
+import com.stevesoltys.seedvault.transport.backup.BackupPlugin
import libcore.io.IoUtils.closeQuietly
import java.io.EOFException
import java.io.IOException
@@ -22,6 +23,7 @@
private class FullRestoreState(
val version: Byte,
val token: Long,
+ val name: String,
val packageInfo: PackageInfo
) {
var inputStream: InputStream? = null
@@ -31,7 +33,8 @@
@Suppress("BlockingMethodInNonBlockingContext")
internal class FullRestore(
- private val plugin: FullRestorePlugin,
+ private val plugin: BackupPlugin,
+ private val legacyPlugin: FullRestorePlugin,
private val outputFactory: OutputFactory,
private val headerReader: HeaderReader,
private val crypto: Crypto
@@ -43,10 +46,13 @@
/**
* Return true if there is data stored for the given package.
+ *
+ * Deprecated. Use only for v0 backups.
*/
@Throws(IOException::class)
+ @Deprecated("Use BackupPlugin#hasData() instead")
suspend fun hasDataForPackage(token: Long, packageInfo: PackageInfo): Boolean {
- return plugin.hasDataForPackage(token, packageInfo)
+ return legacyPlugin.hasDataForPackage(token, packageInfo)
}
/**
@@ -55,8 +61,8 @@
* It is possible that the system decides to not restore the package.
* Then a new state will be initialized right away without calling other methods.
*/
- fun initializeState(version: Byte, token: Long, packageInfo: PackageInfo) {
- state = FullRestoreState(version, token, packageInfo)
+ fun initializeState(version: Byte, token: Long, name: String, packageInfo: PackageInfo) {
+ state = FullRestoreState(version, token, name, packageInfo)
}
/**
@@ -93,12 +99,16 @@
if (state.inputStream == null) {
Log.i(TAG, "First Chunk, initializing package input stream.")
try {
- val inputStream = plugin.getInputStreamForPackage(state.token, state.packageInfo)
- val version = headerReader.readVersion(inputStream, state.version)
- if (version == 0.toByte()) {
+ if (state.version == 0.toByte()) {
+ val inputStream =
+ legacyPlugin.getInputStreamForPackage(state.token, state.packageInfo)
+ val version = headerReader.readVersion(inputStream, state.version)
+ @Suppress("deprecation")
crypto.decryptHeader(inputStream, version, packageName)
state.inputStream = inputStream
} else {
+ val inputStream = plugin.getInputStream(state.token, state.name)
+ val version = headerReader.readVersion(inputStream, state.version)
val ad = getADForFull(version, packageName)
state.inputStream = crypto.newDecryptingStream(inputStream, ad)
}
@@ -135,6 +145,7 @@
if (state.version == 0.toByte()) {
// read segment from input stream and decrypt it
val decrypted = try {
+ @Suppress("deprecation")
crypto.decryptSegment(inputStream)
} catch (e: EOFException) {
Log.i(TAG, " EOF")
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinator.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinator.kt
index 5951185..0859cda 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinator.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinator.kt
@@ -216,7 +216,7 @@
val name = crypto.getNameForPackage(state.backupMetadata.salt, packageName)
if (plugin.hasData(state.token, name)) {
Log.i(TAG, "Found full backup data for $packageName.")
- full.initializeState(version, state.token, packageInfo)
+ full.initializeState(version, state.token, name, packageInfo)
state.currentPackage = packageName
TYPE_FULL_STREAM
} else throw IOException("No data found for $packageName. Skipping.")
@@ -232,6 +232,7 @@
return RestoreDescription(packageName, type)
}
+ @Suppress("deprecation")
private suspend fun nextRestorePackageV0(
state: RestoreCoordinatorState,
packageInfo: PackageInfo
@@ -248,7 +249,7 @@
}
full.hasDataForPackage(state.token, packageInfo) -> {
Log.i(TAG, "Found full backup data for $packageName.")
- full.initializeState(0x00, state.token, packageInfo)
+ full.initializeState(0x00, state.token, "", packageInfo)
state.currentPackage = packageName
TYPE_FULL_STREAM
}
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreModule.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreModule.kt
index 833b47d..62756a8 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreModule.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestoreModule.kt
@@ -6,7 +6,7 @@
val restoreModule = module {
single { OutputFactory() }
single { KVRestore(get<RestorePlugin>().kvRestorePlugin, get(), get(), get()) }
- single { FullRestore(get<RestorePlugin>().fullRestorePlugin, get(), get(), get()) }
+ single { FullRestore(get(), get<RestorePlugin>().fullRestorePlugin, get(), get(), get()) }
single {
RestoreCoordinator(androidContext(), get(), get(), get(), get(), get(), get(), get(), get())
}
diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/CoordinatorIntegrationTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/CoordinatorIntegrationTest.kt
index 5098851..7c52c3d 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/transport/CoordinatorIntegrationTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/transport/CoordinatorIntegrationTest.kt
@@ -97,7 +97,7 @@
private val kvRestore = KVRestore(kvRestorePlugin, outputFactory, headerReader, cryptoImpl)
private val fullRestorePlugin = mockk<FullRestorePlugin>()
private val fullRestore =
- FullRestore(fullRestorePlugin, outputFactory, headerReader, cryptoImpl)
+ FullRestore(backupPlugin, fullRestorePlugin, outputFactory, headerReader, cryptoImpl)
private val restore = RestoreCoordinator(
context,
crypto,
@@ -122,7 +122,9 @@
private val key264 = key2.encodeBase64()
init {
+ @Suppress("deprecation")
every { backupPlugin.kvBackupPlugin } returns kvBackupPlugin
+ @Suppress("deprecation")
every { backupPlugin.fullBackupPlugin } returns fullBackupPlugin
}
@@ -349,12 +351,7 @@
// reverse the backup streams into restore input
val rInputStream = ByteArrayInputStream(bOutputStream.toByteArray())
val rOutputStream = ByteArrayOutputStream()
- coEvery {
- fullRestorePlugin.getInputStreamForPackage(
- token,
- packageInfo
- )
- } returns rInputStream
+ coEvery { backupPlugin.getInputStream(token, name) } returns rInputStream
every { outputFactory.getOutputStream(fileDescriptor) } returns rOutputStream
// restore data
diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/restore/FullRestoreTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/restore/FullRestoreTest.kt
index 9764377..e689805 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/transport/restore/FullRestoreTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/transport/restore/FullRestoreTest.kt
@@ -11,6 +11,7 @@
import com.stevesoltys.seedvault.header.VERSION
import com.stevesoltys.seedvault.header.VersionHeader
import com.stevesoltys.seedvault.header.getADForFull
+import com.stevesoltys.seedvault.transport.backup.BackupPlugin
import io.mockk.CapturingSlot
import io.mockk.Runs
import io.mockk.coEvery
@@ -32,8 +33,9 @@
@Suppress("BlockingMethodInNonBlockingContext")
internal class FullRestoreTest : RestoreTest() {
- private val plugin = mockk<FullRestorePlugin>()
- private val restore = FullRestore(plugin, outputFactory, headerReader, crypto)
+ private val plugin = mockk<BackupPlugin>()
+ private val legacyPlugin = mockk<FullRestorePlugin>()
+ private val restore = FullRestore(plugin, legacyPlugin, outputFactory, headerReader, crypto)
private val encrypted = getRandomByteArray()
private val outputStream = ByteArrayOutputStream()
@@ -45,16 +47,17 @@
}
@Test
- fun `hasDataForPackage() delegates to plugin`() = runBlocking {
+ @Suppress("deprecation")
+ fun `v0 hasDataForPackage() delegates to plugin`() = runBlocking {
val result = Random.nextBoolean()
- coEvery { plugin.hasDataForPackage(token, packageInfo) } returns result
+ coEvery { legacyPlugin.hasDataForPackage(token, packageInfo) } returns result
assertEquals(result, restore.hasDataForPackage(token, packageInfo))
}
@Test
fun `initializing state leaves a state`() {
assertFalse(restore.hasState())
- restore.initializeState(VERSION, token, packageInfo)
+ restore.initializeState(VERSION, token, name, packageInfo)
assertTrue(restore.hasState())
}
@@ -68,9 +71,9 @@
@Test
fun `getting InputStream for package when getting first chunk throws`() = runBlocking {
- restore.initializeState(VERSION, token, packageInfo)
+ restore.initializeState(VERSION, token, name, packageInfo)
- coEvery { plugin.getInputStreamForPackage(token, packageInfo) } throws IOException()
+ coEvery { plugin.getInputStream(token, name) } throws IOException()
every { fileDescriptor.close() } just Runs
assertEquals(
@@ -81,9 +84,9 @@
@Test
fun `reading version header when getting first chunk throws`() = runBlocking {
- restore.initializeState(VERSION, token, packageInfo)
+ restore.initializeState(VERSION, token, name, packageInfo)
- coEvery { plugin.getInputStreamForPackage(token, packageInfo) } returns inputStream
+ coEvery { plugin.getInputStream(token, name) } returns inputStream
every { headerReader.readVersion(inputStream, VERSION) } throws IOException()
every { fileDescriptor.close() } just Runs
@@ -95,9 +98,9 @@
@Test
fun `reading unsupported version when getting first chunk`() = runBlocking {
- restore.initializeState(VERSION, token, packageInfo)
+ restore.initializeState(VERSION, token, name, packageInfo)
- coEvery { plugin.getInputStreamForPackage(token, packageInfo) } returns inputStream
+ coEvery { plugin.getInputStream(token, name) } returns inputStream
every {
headerReader.readVersion(inputStream, VERSION)
} throws UnsupportedVersionException(unsupportedVersion)
@@ -111,9 +114,9 @@
@Test
fun `getting decrypted stream when getting first chunk throws`() = runBlocking {
- restore.initializeState(VERSION, token, packageInfo)
+ restore.initializeState(VERSION, token, name, packageInfo)
- coEvery { plugin.getInputStreamForPackage(token, packageInfo) } returns inputStream
+ coEvery { plugin.getInputStream(token, name) } returns inputStream
every { headerReader.readVersion(inputStream, VERSION) } returns VERSION
every { crypto.newDecryptingStream(inputStream, ad) } throws IOException()
every { fileDescriptor.close() } just Runs
@@ -127,9 +130,9 @@
@Test
fun `getting decrypted stream when getting first chunk throws general security exception`() =
runBlocking {
- restore.initializeState(VERSION, token, packageInfo)
+ restore.initializeState(VERSION, token, name, packageInfo)
- coEvery { plugin.getInputStreamForPackage(token, packageInfo) } returns inputStream
+ coEvery { plugin.getInputStream(token, name) } returns inputStream
every { headerReader.readVersion(inputStream, VERSION) } returns VERSION
every { crypto.newDecryptingStream(inputStream, ad) } throws GeneralSecurityException()
every { fileDescriptor.close() } just Runs
@@ -139,7 +142,7 @@
@Test
fun `full chunk gets decrypted`() = runBlocking {
- restore.initializeState(VERSION, token, packageInfo)
+ restore.initializeState(VERSION, token, name, packageInfo)
initInputStream()
readAndEncryptInputStream(encrypted)
@@ -152,10 +155,11 @@
}
@Test
+ @Suppress("deprecation")
fun `full chunk gets decrypted from version 0`() = runBlocking {
- restore.initializeState(0.toByte(), token, packageInfo)
+ restore.initializeState(0.toByte(), token, name, packageInfo)
- coEvery { plugin.getInputStreamForPackage(token, packageInfo) } returns inputStream
+ coEvery { legacyPlugin.getInputStreamForPackage(token, packageInfo) } returns inputStream
every { headerReader.readVersion(inputStream, 0.toByte()) } returns 0.toByte()
every {
crypto.decryptHeader(inputStream, 0.toByte(), packageInfo.packageName)
@@ -174,9 +178,9 @@
@Test
fun `unexpected version aborts with error`() = runBlocking {
- restore.initializeState(Byte.MAX_VALUE, token, packageInfo)
+ restore.initializeState(Byte.MAX_VALUE, token, name, packageInfo)
- coEvery { plugin.getInputStreamForPackage(token, packageInfo) } returns inputStream
+ coEvery { plugin.getInputStream(token, name) } returns inputStream
every {
headerReader.readVersion(inputStream, Byte.MAX_VALUE)
} throws GeneralSecurityException()
@@ -192,9 +196,9 @@
fun `three full chunk get decrypted and then return no more data`() = runBlocking {
val encryptedBytes = Random.nextBytes(MAX_SEGMENT_LENGTH * 2 + 1)
val decryptedInputStream = ByteArrayInputStream(encryptedBytes)
- restore.initializeState(VERSION, token, packageInfo)
+ restore.initializeState(VERSION, token, name, packageInfo)
- coEvery { plugin.getInputStreamForPackage(token, packageInfo) } returns inputStream
+ coEvery { plugin.getInputStream(token, name) } returns inputStream
every { headerReader.readVersion(inputStream, VERSION) } returns VERSION
every { crypto.newDecryptingStream(inputStream, ad) } returns decryptedInputStream
every { outputFactory.getOutputStream(fileDescriptor) } returns outputStream
@@ -213,7 +217,7 @@
@Test
fun `aborting full restore closes stream, resets state`() = runBlocking {
- restore.initializeState(VERSION, token, packageInfo)
+ restore.initializeState(VERSION, token, name, packageInfo)
initInputStream()
readAndEncryptInputStream(encrypted)
@@ -227,7 +231,7 @@
}
private fun initInputStream() {
- coEvery { plugin.getInputStreamForPackage(token, packageInfo) } returns inputStream
+ coEvery { plugin.getInputStream(token, name) } returns inputStream
every { headerReader.readVersion(inputStream, VERSION) } returns VERSION
every { crypto.newDecryptingStream(inputStream, ad) } returns decryptedInputStream
}
diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinatorTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinatorTest.kt
index 1bd51fe..72fe53a 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinatorTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreCoordinatorTest.kt
@@ -242,13 +242,14 @@
}
@Test
+ @Suppress("deprecation")
fun `v0 nextRestorePackage() returns full description if no KV data found`() = runBlocking {
restore.beforeStartRestore(metadata.copy(version = 0x00))
restore.startRestore(token, packageInfoArray)
coEvery { kv.hasDataForPackage(token, packageInfo) } returns false
coEvery { full.hasDataForPackage(token, packageInfo) } returns true
- every { full.initializeState(0x00, token, packageInfo) } just Runs
+ every { full.initializeState(0x00, token, "", packageInfo) } just Runs
val expected = RestoreDescription(packageInfo.packageName, TYPE_FULL_STREAM)
assertEquals(expected, restore.nextRestorePackage())
@@ -276,7 +277,7 @@
every { crypto.getNameForPackage(metadata.salt, packageInfo2.packageName) } returns name2
coEvery { plugin.hasData(token, name2) } returns true
- every { full.initializeState(VERSION, token, packageInfo2) } just Runs
+ every { full.initializeState(VERSION, token, name2, packageInfo2) } just Runs
val expected = RestoreDescription(packageInfo2.packageName, TYPE_FULL_STREAM)
assertEquals(expected, restore.nextRestorePackage())
@@ -298,7 +299,7 @@
every { crypto.getNameForPackage(metadata.salt, packageInfo2.packageName) } returns name2
coEvery { plugin.hasData(token, name2) } returns true
- every { full.initializeState(VERSION, token, packageInfo2) } just Runs
+ every { full.initializeState(VERSION, token, name2, packageInfo2) } just Runs
val expected2 =
RestoreDescription(packageInfo2.packageName, TYPE_FULL_STREAM)
@@ -308,6 +309,7 @@
}
@Test
+ @Suppress("deprecation")
fun `v0 nextRestorePackage() returns all packages from startRestore()`() = runBlocking {
restore.beforeStartRestore(metadata.copy(version = 0x00))
restore.startRestore(token, packageInfoArray2)
@@ -320,7 +322,7 @@
coEvery { kv.hasDataForPackage(token, packageInfo2) } returns false
coEvery { full.hasDataForPackage(token, packageInfo2) } returns true
- every { full.initializeState(0.toByte(), token, packageInfo2) } just Runs
+ every { full.initializeState(0.toByte(), token, "", packageInfo2) } just Runs
val expected2 = RestoreDescription(packageInfo2.packageName, TYPE_FULL_STREAM)
assertEquals(expected2, restore.nextRestorePackage())
@@ -352,6 +354,7 @@
}
@Test
+ @Suppress("deprecation")
fun `v0 when full#hasDataForPackage() throws, it tries next package`() = runBlocking {
restore.beforeStartRestore(metadata.copy(version = 0x00))
restore.startRestore(token, packageInfoArray)
diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreV0IntegrationTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreV0IntegrationTest.kt
index fd1fe72..1b06ed7 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreV0IntegrationTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/transport/restore/RestoreV0IntegrationTest.kt
@@ -52,7 +52,7 @@
private val kvRestore = KVRestore(kvRestorePlugin, outputFactory, headerReader, cryptoImpl)
private val fullRestorePlugin = mockk<FullRestorePlugin>()
private val fullRestore =
- FullRestore(fullRestorePlugin, outputFactory, headerReader, cryptoImpl)
+ FullRestore(backupPlugin, fullRestorePlugin, outputFactory, headerReader, cryptoImpl)
private val restore = RestoreCoordinator(
context,
crypto,