Stop writing out old metadata to backend
We'll probably keep metadata around for internal information about backup state
This commit is contained in:
parent
c2ad309f93
commit
7f9e84fdb6
5 changed files with 6 additions and 74 deletions
|
@ -11,11 +11,6 @@ import org.calyxos.seedvault.core.backends.Backend
|
||||||
import org.calyxos.seedvault.core.backends.FileHandle
|
import org.calyxos.seedvault.core.backends.FileHandle
|
||||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.OutputStream
|
|
||||||
|
|
||||||
suspend fun Backend.getMetadataOutputStream(token: Long): OutputStream {
|
|
||||||
return save(LegacyAppBackupFile.Metadata(token))
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun Backend.getAvailableBackupFileHandles(): List<FileHandle> {
|
suspend fun Backend.getAvailableBackupFileHandles(): List<FileHandle> {
|
||||||
// v1 get all restore set tokens in root folder that have a metadata file
|
// v1 get all restore set tokens in root folder that have a metadata file
|
||||||
|
|
|
@ -100,7 +100,6 @@ internal class MetadataManager(
|
||||||
* Call this after a package's APK has been backed up successfully.
|
* Call this after a package's APK has been backed up successfully.
|
||||||
*
|
*
|
||||||
* It updates the packages' metadata to the internal cache.
|
* It updates the packages' metadata to the internal cache.
|
||||||
* You still need to call [uploadMetadata] to persist all local modifications.
|
|
||||||
*/
|
*/
|
||||||
@Synchronized
|
@Synchronized
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
|
@ -188,11 +187,10 @@ internal class MetadataManager(
|
||||||
internal fun onPackageBackupError(
|
internal fun onPackageBackupError(
|
||||||
packageInfo: PackageInfo,
|
packageInfo: PackageInfo,
|
||||||
packageState: PackageState,
|
packageState: PackageState,
|
||||||
metadataOutputStream: OutputStream,
|
|
||||||
backupType: BackupType? = null,
|
backupType: BackupType? = null,
|
||||||
) {
|
) {
|
||||||
check(packageState != APK_AND_DATA) { "Backup Error with non-error package state." }
|
check(packageState != APK_AND_DATA) { "Backup Error with non-error package state." }
|
||||||
modifyMetadata(metadataOutputStream) {
|
modifyCachedMetadata {
|
||||||
metadata.packageMetadataMap.getOrPut(packageInfo.packageName) {
|
metadata.packageMetadataMap.getOrPut(packageInfo.packageName) {
|
||||||
val isSystemApp = packageInfo.isSystemApp()
|
val isSystemApp = packageInfo.isSystemApp()
|
||||||
PackageMetadata(
|
PackageMetadata(
|
||||||
|
@ -212,7 +210,6 @@ internal class MetadataManager(
|
||||||
* Call this for all packages we can not back up for some reason.
|
* Call this for all packages we can not back up for some reason.
|
||||||
*
|
*
|
||||||
* It updates the packages' local metadata.
|
* It updates the packages' local metadata.
|
||||||
* You still need to call [uploadMetadata] to persist all local modifications.
|
|
||||||
*/
|
*/
|
||||||
@Synchronized
|
@Synchronized
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
|
@ -239,15 +236,6 @@ internal class MetadataManager(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Uploads metadata to given [metadataOutputStream] after performing local modifications.
|
|
||||||
*/
|
|
||||||
@Synchronized
|
|
||||||
@Throws(IOException::class)
|
|
||||||
fun uploadMetadata(metadataOutputStream: OutputStream) {
|
|
||||||
metadataWriter.write(metadata, metadataOutputStream)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
private fun modifyCachedMetadata(modFun: () -> Unit) {
|
private fun modifyCachedMetadata(modFun: () -> Unit) {
|
||||||
val oldMetadata = metadata.copy( // copy map, otherwise it will re-use same reference
|
val oldMetadata = metadata.copy( // copy map, otherwise it will re-use same reference
|
||||||
|
@ -262,24 +250,7 @@ internal class MetadataManager(
|
||||||
metadata = oldMetadata
|
metadata = oldMetadata
|
||||||
throw IOException(e)
|
throw IOException(e)
|
||||||
}
|
}
|
||||||
}
|
mLastBackupTime.postValue(metadata.time) // TODO only do after snapshot was written
|
||||||
|
|
||||||
@Throws(IOException::class)
|
|
||||||
private fun modifyMetadata(metadataOutputStream: OutputStream, modFun: () -> Unit) {
|
|
||||||
val oldMetadata = metadata.copy( // copy map, otherwise it will re-use same reference
|
|
||||||
packageMetadataMap = PackageMetadataMap(metadata.packageMetadataMap),
|
|
||||||
)
|
|
||||||
try {
|
|
||||||
modFun.invoke()
|
|
||||||
metadataWriter.write(metadata, metadataOutputStream)
|
|
||||||
writeMetadataToCache()
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Log.w(TAG, "Error writing metadata to storage", e)
|
|
||||||
// revert metadata and do not write it to cache
|
|
||||||
metadata = oldMetadata
|
|
||||||
throw IOException(e)
|
|
||||||
}
|
|
||||||
mLastBackupTime.postValue(metadata.time)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -23,7 +23,6 @@ import android.util.Log
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
import com.stevesoltys.seedvault.Clock
|
import com.stevesoltys.seedvault.Clock
|
||||||
import com.stevesoltys.seedvault.backend.BackendManager
|
import com.stevesoltys.seedvault.backend.BackendManager
|
||||||
import com.stevesoltys.seedvault.backend.getMetadataOutputStream
|
|
||||||
import com.stevesoltys.seedvault.backend.isOutOfSpace
|
import com.stevesoltys.seedvault.backend.isOutOfSpace
|
||||||
import com.stevesoltys.seedvault.metadata.BackupType
|
import com.stevesoltys.seedvault.metadata.BackupType
|
||||||
import com.stevesoltys.seedvault.metadata.MetadataManager
|
import com.stevesoltys.seedvault.metadata.MetadataManager
|
||||||
|
@ -386,13 +385,10 @@ internal class BackupCoordinator(
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO is this only nice to have info, or do we need to do more?
|
// TODO is this only nice to have info, or do we need to do more?
|
||||||
private suspend fun onPackageBackupError(packageInfo: PackageInfo, type: BackupType) {
|
private fun onPackageBackupError(packageInfo: PackageInfo, type: BackupType) {
|
||||||
val packageName = packageInfo.packageName
|
val packageName = packageInfo.packageName
|
||||||
try {
|
try {
|
||||||
val token = settingsManager.getToken() ?: error("no token")
|
metadataManager.onPackageBackupError(packageInfo, state.cancelReason, type)
|
||||||
backend.getMetadataOutputStream(token).use {
|
|
||||||
metadataManager.onPackageBackupError(packageInfo, state.cancelReason, it, type)
|
|
||||||
}
|
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.e(TAG, "Error while writing metadata for $packageName", e)
|
Log.e(TAG, "Error while writing metadata for $packageName", e)
|
||||||
}
|
}
|
||||||
|
|
|
@ -473,7 +473,7 @@ class MetadataManagerTest {
|
||||||
expectReadFromCache()
|
expectReadFromCache()
|
||||||
expectModifyMetadata(updatedMetadata)
|
expectModifyMetadata(updatedMetadata)
|
||||||
|
|
||||||
manager.onPackageBackupError(packageInfo, NO_DATA, storageOutputStream, BackupType.KV)
|
manager.onPackageBackupError(packageInfo, NO_DATA, BackupType.KV)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -486,22 +486,7 @@ class MetadataManagerTest {
|
||||||
expectReadFromCache()
|
expectReadFromCache()
|
||||||
expectModifyMetadata(updatedMetadata)
|
expectModifyMetadata(updatedMetadata)
|
||||||
|
|
||||||
manager.onPackageBackupError(packageInfo, WAS_STOPPED, storageOutputStream)
|
manager.onPackageBackupError(packageInfo, WAS_STOPPED)
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test uploadMetadata() uploads`() {
|
|
||||||
expectReadFromCache()
|
|
||||||
every { metadataWriter.write(initialMetadata, storageOutputStream) } just Runs
|
|
||||||
|
|
||||||
manager.uploadMetadata(storageOutputStream)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test getBackupToken() on first run`() {
|
|
||||||
every { context.openFileInput(METADATA_CACHE_FILE) } throws FileNotFoundException()
|
|
||||||
|
|
||||||
assertEquals(0L, manager.getBackupToken())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -32,12 +32,10 @@ import io.mockk.mockk
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.calyxos.seedvault.core.backends.Backend
|
import org.calyxos.seedvault.core.backends.Backend
|
||||||
import org.calyxos.seedvault.core.backends.LegacyAppBackupFile
|
|
||||||
import org.calyxos.seedvault.core.backends.saf.SafProperties
|
import org.calyxos.seedvault.core.backends.saf.SafProperties
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.OutputStream
|
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
internal class BackupCoordinatorTest : BackupTest() {
|
internal class BackupCoordinatorTest : BackupTest() {
|
||||||
|
@ -64,7 +62,6 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
)
|
)
|
||||||
|
|
||||||
private val backend = mockk<Backend>()
|
private val backend = mockk<Backend>()
|
||||||
private val metadataOutputStream = mockk<OutputStream>()
|
|
||||||
private val fileDescriptor: ParcelFileDescriptor = mockk()
|
private val fileDescriptor: ParcelFileDescriptor = mockk()
|
||||||
private val packageMetadata: PackageMetadata = mockk()
|
private val packageMetadata: PackageMetadata = mockk()
|
||||||
private val safProperties = SafProperties(
|
private val safProperties = SafProperties(
|
||||||
|
@ -211,12 +208,9 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
metadataManager.onPackageBackupError(
|
metadataManager.onPackageBackupError(
|
||||||
packageInfo,
|
packageInfo,
|
||||||
UNKNOWN_ERROR,
|
UNKNOWN_ERROR,
|
||||||
metadataOutputStream,
|
|
||||||
BackupType.KV,
|
BackupType.KV,
|
||||||
)
|
)
|
||||||
} just Runs
|
} just Runs
|
||||||
coEvery { backend.save(LegacyAppBackupFile.Metadata(token)) } returns metadataOutputStream
|
|
||||||
every { metadataOutputStream.close() } just Runs
|
|
||||||
|
|
||||||
assertEquals(TRANSPORT_PACKAGE_REJECTED, backup.finishBackup())
|
assertEquals(TRANSPORT_PACKAGE_REJECTED, backup.finishBackup())
|
||||||
}
|
}
|
||||||
|
@ -270,14 +264,12 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
metadataManager.onPackageBackupError(
|
metadataManager.onPackageBackupError(
|
||||||
packageInfo,
|
packageInfo,
|
||||||
QUOTA_EXCEEDED,
|
QUOTA_EXCEEDED,
|
||||||
metadataOutputStream,
|
|
||||||
BackupType.FULL
|
BackupType.FULL
|
||||||
)
|
)
|
||||||
} just Runs
|
} just Runs
|
||||||
coEvery { full.cancelFullBackup() } just Runs
|
coEvery { full.cancelFullBackup() } just Runs
|
||||||
every { backendManager.backendProperties } returns safProperties
|
every { backendManager.backendProperties } returns safProperties
|
||||||
every { settingsManager.useMeteredNetwork } returns false
|
every { settingsManager.useMeteredNetwork } returns false
|
||||||
every { metadataOutputStream.close() } just Runs
|
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
TRANSPORT_OK,
|
TRANSPORT_OK,
|
||||||
|
@ -298,11 +290,9 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
metadataManager.onPackageBackupError(
|
metadataManager.onPackageBackupError(
|
||||||
packageInfo,
|
packageInfo,
|
||||||
QUOTA_EXCEEDED,
|
QUOTA_EXCEEDED,
|
||||||
metadataOutputStream,
|
|
||||||
BackupType.FULL
|
BackupType.FULL
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
verify { metadataOutputStream.close() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -318,14 +308,12 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
metadataManager.onPackageBackupError(
|
metadataManager.onPackageBackupError(
|
||||||
packageInfo,
|
packageInfo,
|
||||||
NO_DATA,
|
NO_DATA,
|
||||||
metadataOutputStream,
|
|
||||||
BackupType.FULL
|
BackupType.FULL
|
||||||
)
|
)
|
||||||
} just Runs
|
} just Runs
|
||||||
coEvery { full.cancelFullBackup() } just Runs
|
coEvery { full.cancelFullBackup() } just Runs
|
||||||
every { backendManager.backendProperties } returns safProperties
|
every { backendManager.backendProperties } returns safProperties
|
||||||
every { settingsManager.useMeteredNetwork } returns false
|
every { settingsManager.useMeteredNetwork } returns false
|
||||||
every { metadataOutputStream.close() } just Runs
|
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
TRANSPORT_OK,
|
TRANSPORT_OK,
|
||||||
|
@ -343,11 +331,9 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
metadataManager.onPackageBackupError(
|
metadataManager.onPackageBackupError(
|
||||||
packageInfo,
|
packageInfo,
|
||||||
NO_DATA,
|
NO_DATA,
|
||||||
metadataOutputStream,
|
|
||||||
BackupType.FULL
|
BackupType.FULL
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
verify { metadataOutputStream.close() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -357,7 +343,6 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
private fun expectApkBackupAndMetadataWrite() {
|
private fun expectApkBackupAndMetadataWrite() {
|
||||||
coEvery { apkBackup.backupApkIfNecessary(packageInfo) } just Runs
|
coEvery { apkBackup.backupApkIfNecessary(packageInfo) } just Runs
|
||||||
every { settingsManager.getToken() } returns token
|
every { settingsManager.getToken() } returns token
|
||||||
coEvery { backend.save(LegacyAppBackupFile.Metadata(token)) } returns metadataOutputStream
|
|
||||||
every { metadataManager.onApkBackedUp(any(), packageMetadata) } just Runs
|
every { metadataManager.onApkBackedUp(any(), packageMetadata) } just Runs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue