diff --git a/app/src/main/java/com/stevesoltys/seedvault/metadata/Metadata.kt b/app/src/main/java/com/stevesoltys/seedvault/metadata/Metadata.kt index 03f1568b..3dacf693 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/metadata/Metadata.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/metadata/Metadata.kt @@ -13,6 +13,7 @@ typealias PackageMetadataMap = HashMap data class BackupMetadata( internal val version: Byte = VERSION, internal val token: Long, + internal val salt: String, internal var time: Long = 0L, internal val androidVersion: Int = Build.VERSION.SDK_INT, internal val androidIncremental: String = Build.VERSION.INCREMENTAL, @@ -23,6 +24,7 @@ data class BackupMetadata( internal const val JSON_METADATA = "@meta@" internal const val JSON_METADATA_VERSION = "version" internal const val JSON_METADATA_TOKEN = "token" +internal const val JSON_METADATA_SALT = "salt" internal const val JSON_METADATA_TIME = "time" internal const val JSON_METADATA_SDK_INT = "sdk_int" internal const val JSON_METADATA_INCREMENTAL = "incremental" @@ -69,6 +71,7 @@ data class PackageMetadata( */ internal var time: Long = 0L, internal var state: PackageState = UNKNOWN_ERROR, + internal var backupType: BackupType? = null, internal val system: Boolean = false, internal val version: Long? = null, internal val installer: String? = null, @@ -87,7 +90,10 @@ data class ApkSplit( // There's also a revisionCode, but it doesn't seem to be used just yet ) +enum class BackupType { KV, FULL } + internal const val JSON_PACKAGE_TIME = "time" +internal const val JSON_PACKAGE_BACKUP_TYPE = "backupType" internal const val JSON_PACKAGE_STATE = "state" internal const val JSON_PACKAGE_SYSTEM = "system" internal const val JSON_PACKAGE_VERSION = "version" diff --git a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataManager.kt b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataManager.kt index a006a306..648816b4 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataManager.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataManager.kt @@ -10,6 +10,8 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.distinctUntilChanged import com.stevesoltys.seedvault.Clock +import com.stevesoltys.seedvault.crypto.Crypto +import com.stevesoltys.seedvault.encodeBase64 import com.stevesoltys.seedvault.header.VERSION import com.stevesoltys.seedvault.metadata.PackageState.APK_AND_DATA import com.stevesoltys.seedvault.metadata.PackageState.NOT_ALLOWED @@ -24,16 +26,18 @@ private val TAG = MetadataManager::class.java.simpleName @VisibleForTesting internal const val METADATA_CACHE_FILE = "metadata.cache" +internal const val METADATA_SALT_SIZE = 32 @WorkerThread -class MetadataManager( +internal class MetadataManager( private val context: Context, private val clock: Clock, + private val crypto: Crypto, private val metadataWriter: MetadataWriter, private val metadataReader: MetadataReader ) { - private val uninitializedMetadata = BackupMetadata(token = 0L) + private val uninitializedMetadata = BackupMetadata(token = 0L, salt = "") private var metadata: BackupMetadata = uninitializedMetadata get() { if (field == uninitializedMetadata) { @@ -57,8 +61,9 @@ class MetadataManager( @Synchronized @Throws(IOException::class) fun onDeviceInitialization(token: Long, metadataOutputStream: OutputStream) { + val salt = crypto.getRandomBytes(METADATA_SALT_SIZE).encodeBase64() modifyMetadata(metadataOutputStream) { - metadata = BackupMetadata(token = token) + metadata = BackupMetadata(token = token, salt = salt) } } @@ -121,7 +126,11 @@ class MetadataManager( */ @Synchronized @Throws(IOException::class) - fun onPackageBackedUp(packageInfo: PackageInfo, metadataOutputStream: OutputStream) { + fun onPackageBackedUp( + packageInfo: PackageInfo, + type: BackupType, + metadataOutputStream: OutputStream + ) { val packageName = packageInfo.packageName modifyMetadata(metadataOutputStream) { val now = clock.time() @@ -129,10 +138,12 @@ class MetadataManager( if (metadata.packageMetadataMap.containsKey(packageName)) { metadata.packageMetadataMap[packageName]!!.time = now metadata.packageMetadataMap[packageName]!!.state = APK_AND_DATA + metadata.packageMetadataMap[packageName]!!.backupType = type } else { metadata.packageMetadataMap[packageName] = PackageMetadata( time = now, state = APK_AND_DATA, + backupType = type, system = packageInfo.isSystemApp() ) } @@ -150,7 +161,8 @@ class MetadataManager( internal fun onPackageBackupError( packageInfo: PackageInfo, packageState: PackageState, - metadataOutputStream: OutputStream + metadataOutputStream: OutputStream, + backupType: BackupType? = null ) { check(packageState != APK_AND_DATA) { "Backup Error with non-error package state." } val packageName = packageInfo.packageName @@ -161,6 +173,7 @@ class MetadataManager( metadata.packageMetadataMap[packageName] = PackageMetadata( time = 0L, state = packageState, + backupType = backupType, system = packageInfo.isSystemApp() ) } diff --git a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataModule.kt b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataModule.kt index 1ba64b33..68c723a4 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataModule.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataModule.kt @@ -4,7 +4,7 @@ import org.koin.android.ext.koin.androidContext import org.koin.dsl.module val metadataModule = module { - single { MetadataManager(androidContext(), get(), get(), get()) } + single { MetadataManager(androidContext(), get(), get(), get(), get()) } single { MetadataWriterImpl(get()) } single { MetadataReaderImpl(get()) } } diff --git a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataReader.kt b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataReader.kt index d1e30ac4..99598fbe 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataReader.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataReader.kt @@ -112,6 +112,13 @@ internal class MetadataReaderImpl(private val crypto: Crypto) : MetadataReader { WAS_STOPPED.name -> WAS_STOPPED else -> UNKNOWN_ERROR } + val pBackupType = when (p.optString(JSON_PACKAGE_BACKUP_TYPE)) { + BackupType.KV.name -> BackupType.KV + BackupType.FULL.name -> BackupType.FULL + // we can't fail when format version is 0, + // because when only backing up the APK for example, there's no type + else -> null + } val pSystem = p.optBoolean(JSON_PACKAGE_SYSTEM, false) val pVersion = p.optLong(JSON_PACKAGE_VERSION, 0L) val pInstaller = p.optString(JSON_PACKAGE_INSTALLER) @@ -127,6 +134,7 @@ internal class MetadataReaderImpl(private val crypto: Crypto) : MetadataReader { packageMetadataMap[packageName] = PackageMetadata( time = p.getLong(JSON_PACKAGE_TIME), state = pState, + backupType = pBackupType, system = pSystem, version = if (pVersion == 0L) null else pVersion, installer = if (pInstaller == "") null else pInstaller, @@ -138,6 +146,7 @@ internal class MetadataReaderImpl(private val crypto: Crypto) : MetadataReader { return BackupMetadata( version = version, token = token, + salt = if (version == 0.toByte()) "" else meta.getString(JSON_METADATA_SALT), time = meta.getLong(JSON_METADATA_TIME), androidVersion = meta.getInt(JSON_METADATA_SDK_INT), androidIncremental = meta.getString(JSON_METADATA_INCREMENTAL), diff --git a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataWriter.kt b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataWriter.kt index 9ace924e..1359c112 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataWriter.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataWriter.kt @@ -30,6 +30,7 @@ internal class MetadataWriterImpl(private val crypto: Crypto) : MetadataWriter { put(JSON_METADATA, JSONObject().apply { put(JSON_METADATA_VERSION, metadata.version.toInt()) put(JSON_METADATA_TOKEN, metadata.token) + put(JSON_METADATA_SALT, metadata.salt) put(JSON_METADATA_TIME, metadata.time) put(JSON_METADATA_SDK_INT, metadata.androidVersion) put(JSON_METADATA_INCREMENTAL, metadata.androidIncremental) @@ -42,6 +43,11 @@ internal class MetadataWriterImpl(private val crypto: Crypto) : MetadataWriter { if (packageMetadata.state != APK_AND_DATA) { put(JSON_PACKAGE_STATE, packageMetadata.state.name) } + // We can't require a backup type in metadata at this point, + // only when version > 0 and we have actual restore data + if (packageMetadata.backupType != null) { + put(JSON_PACKAGE_BACKUP_TYPE, packageMetadata.backupType!!.name) + } if (packageMetadata.system) { put(JSON_PACKAGE_SYSTEM, packageMetadata.system) } diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/ApkBackup.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/ApkBackup.kt index adf2eb61..b38fbc7e 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/ApkBackup.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/ApkBackup.kt @@ -25,7 +25,7 @@ import java.security.MessageDigest private val TAG = ApkBackup::class.java.simpleName @Suppress("BlockingMethodInNonBlockingContext") -class ApkBackup( +internal class ApkBackup( private val pm: PackageManager, private val settingsManager: SettingsManager, private val metadataManager: MetadataManager diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinator.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinator.kt index 3b934aff..e0b0a939 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinator.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinator.kt @@ -21,6 +21,7 @@ import androidx.annotation.VisibleForTesting.PRIVATE import androidx.annotation.WorkerThread import com.stevesoltys.seedvault.Clock import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER +import com.stevesoltys.seedvault.metadata.BackupType import com.stevesoltys.seedvault.metadata.MetadataManager import com.stevesoltys.seedvault.metadata.PackageState import com.stevesoltys.seedvault.metadata.PackageState.NOT_ALLOWED @@ -334,7 +335,7 @@ internal class BackupCoordinator( TAG, "Cancel full backup of ${packageInfo.packageName}" + " because of ${state.cancelReason}" ) - onPackageBackupError(packageInfo) + onPackageBackupError(packageInfo, BackupType.FULL) full.cancelFullBackup() } @@ -381,14 +382,16 @@ internal class BackupCoordinator( check(!full.hasState()) { "K/V backup has state, but full backup has dangling state as well" } - onPackageBackedUp(kv.getCurrentPackage()!!) // not-null because we have state + // getCurrentPackage() not-null because we have state + onPackageBackedUp(kv.getCurrentPackage()!!, BackupType.KV) kv.finishBackup() } full.hasState() -> { check(!kv.hasState()) { "Full backup has state, but K/V backup has dangling state as well" } - onPackageBackedUp(full.getCurrentPackage()!!) // not-null because we have state + // getCurrentPackage() not-null because we have state + onPackageBackedUp(full.getCurrentPackage()!!, BackupType.FULL) full.finishBackup() } state.expectFinish -> { @@ -456,10 +459,10 @@ internal class BackupCoordinator( } } - private suspend fun onPackageBackedUp(packageInfo: PackageInfo) { + private suspend fun onPackageBackedUp(packageInfo: PackageInfo, type: BackupType) { try { plugin.getMetadataOutputStream().use { - metadataManager.onPackageBackedUp(packageInfo, it) + metadataManager.onPackageBackedUp(packageInfo, type, it) } } catch (e: IOException) { Log.e(TAG, "Error while writing metadata for ${packageInfo.packageName}", e) @@ -468,13 +471,13 @@ internal class BackupCoordinator( } } - private suspend fun onPackageBackupError(packageInfo: PackageInfo) { + private suspend fun onPackageBackupError(packageInfo: PackageInfo, type: BackupType) { // don't bother with system apps that have no data if (state.cancelReason == NO_DATA && packageInfo.isSystemApp()) return val packageName = packageInfo.packageName try { plugin.getMetadataOutputStream().use { - metadataManager.onPackageBackupError(packageInfo, state.cancelReason, it) + metadataManager.onPackageBackupError(packageInfo, state.cancelReason, it, type) } } catch (e: IOException) { Log.e(TAG, "Error while writing metadata for $packageName", e) diff --git a/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataManagerTest.kt b/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataManagerTest.kt index 0aae4113..0e120ff2 100644 --- a/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataManagerTest.kt +++ b/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataManagerTest.kt @@ -9,6 +9,8 @@ import android.content.pm.PackageInfo import androidx.test.ext.junit.runners.AndroidJUnit4 import com.stevesoltys.seedvault.Clock import com.stevesoltys.seedvault.TestApp +import com.stevesoltys.seedvault.crypto.Crypto +import com.stevesoltys.seedvault.encodeBase64 import com.stevesoltys.seedvault.getRandomByteArray import com.stevesoltys.seedvault.getRandomString import com.stevesoltys.seedvault.metadata.PackageState.APK_AND_DATA @@ -46,10 +48,11 @@ class MetadataManagerTest { private val context: Context = mockk() private val clock: Clock = mockk() + private val crypto: Crypto = mockk() private val metadataWriter: MetadataWriter = mockk() private val metadataReader: MetadataReader = mockk() - private val manager = MetadataManager(context, clock, metadataWriter, metadataReader) + private val manager = MetadataManager(context, clock, crypto, metadataWriter, metadataReader) private val time = 42L private val token = Random.nextLong() @@ -58,7 +61,9 @@ class MetadataManagerTest { packageName = this@MetadataManagerTest.packageName applicationInfo = ApplicationInfo().apply { flags = FLAG_ALLOW_BACKUP } } - private val initialMetadata = BackupMetadata(token = token) + private val saltBytes = Random.nextBytes(METADATA_SALT_SIZE) + private val salt = saltBytes.encodeBase64() + private val initialMetadata = BackupMetadata(token = token, salt = salt) private val storageOutputStream = ByteArrayOutputStream() private val cacheOutputStream: FileOutputStream = mockk() private val cacheInputStream: FileInputStream = mockk() @@ -72,6 +77,7 @@ class MetadataManagerTest { @Test fun `test onDeviceInitialization()`() { every { clock.time() } returns time + every { crypto.getRandomBytes(METADATA_SALT_SIZE) } returns saltBytes expectReadFromCache() expectModifyMetadata(initialMetadata) @@ -233,10 +239,10 @@ class MetadataManagerTest { every { clock.time() } returns time expectModifyMetadata(initialMetadata) - manager.onPackageBackedUp(packageInfo, storageOutputStream) + manager.onPackageBackedUp(packageInfo, BackupType.FULL, storageOutputStream) assertEquals( - packageMetadata.copy(state = APK_AND_DATA, system = true), + packageMetadata.copy(state = APK_AND_DATA, backupType = BackupType.FULL, system = true), manager.getPackageMetadata(packageName) ) assertEquals(time, manager.getLastBackupTime()) @@ -254,14 +260,15 @@ class MetadataManagerTest { time = updateTime, packageMetadataMap = PackageMetadataMap() // otherwise this isn't copied, but referenced ) - updatedMetadata.packageMetadataMap[packageName] = PackageMetadata(updateTime, APK_AND_DATA) + updatedMetadata.packageMetadataMap[packageName] = + PackageMetadata(updateTime, APK_AND_DATA, BackupType.KV) expectReadFromCache() every { clock.time() } returns updateTime every { metadataWriter.write(updatedMetadata, storageOutputStream) } throws IOException() try { - manager.onPackageBackedUp(packageInfo, storageOutputStream) + manager.onPackageBackedUp(packageInfo, BackupType.KV, storageOutputStream) fail() } catch (e: IOException) { // expected @@ -294,7 +301,7 @@ class MetadataManagerTest { every { clock.time() } returns time expectModifyMetadata(updatedMetadata) - manager.onPackageBackedUp(packageInfo, storageOutputStream) + manager.onPackageBackedUp(packageInfo, BackupType.FULL, storageOutputStream) assertEquals(time, manager.getLastBackupTime()) assertEquals(PackageMetadata(time), manager.getPackageMetadata(cachedPackageName)) diff --git a/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataReadWriteTest.kt b/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataReadWriteTest.kt index db9881be..f0afbb86 100644 --- a/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataReadWriteTest.kt +++ b/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataReadWriteTest.kt @@ -4,6 +4,7 @@ import com.stevesoltys.seedvault.crypto.CipherFactoryImpl import com.stevesoltys.seedvault.crypto.CryptoImpl import com.stevesoltys.seedvault.crypto.KEY_SIZE_BYTES import com.stevesoltys.seedvault.crypto.KeyManagerTestImpl +import com.stevesoltys.seedvault.getRandomBase64 import com.stevesoltys.seedvault.getRandomString import com.stevesoltys.seedvault.header.HeaderReaderImpl import com.stevesoltys.seedvault.header.VERSION @@ -33,8 +34,8 @@ internal class MetadataReadWriteTest { private val reader = MetadataReaderImpl(cryptoImpl) private val packages = HashMap().apply { - put(getRandomString(), PackageMetadata(Random.nextLong(), APK_AND_DATA)) - put(getRandomString(), PackageMetadata(Random.nextLong(), WAS_STOPPED)) + put(getRandomString(), PackageMetadata(Random.nextLong(), APK_AND_DATA, BackupType.FULL)) + put(getRandomString(), PackageMetadata(Random.nextLong(), WAS_STOPPED, BackupType.KV)) } @Test @@ -55,6 +56,7 @@ internal class MetadataReadWriteTest { return BackupMetadata( version = VERSION, token = Random.nextLong(), + salt = getRandomBase64(32), time = Random.nextLong(), androidVersion = Random.nextInt(), androidIncremental = getRandomString(), diff --git a/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataReaderTest.kt b/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataReaderTest.kt index a49e9f0d..8b9216a8 100644 --- a/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataReaderTest.kt +++ b/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataReaderTest.kt @@ -2,6 +2,7 @@ package com.stevesoltys.seedvault.metadata import com.stevesoltys.seedvault.Utf8 import com.stevesoltys.seedvault.crypto.Crypto +import com.stevesoltys.seedvault.getRandomBase64 import com.stevesoltys.seedvault.getRandomString import com.stevesoltys.seedvault.metadata.PackageState.QUOTA_EXCEEDED import com.stevesoltys.seedvault.metadata.PackageState.UNKNOWN_ERROR @@ -90,6 +91,7 @@ class MetadataReaderTest { "org.example", PackageMetadata( time = Random.nextLong(), state = QUOTA_EXCEEDED, + backupType = BackupType.FULL, version = Random.nextLong(), installer = getRandomString(), sha256 = getRandomString(), @@ -123,6 +125,7 @@ class MetadataReaderTest { json.put("org.example", JSONObject().apply { put(JSON_PACKAGE_TIME, Random.nextLong()) put(JSON_PACKAGE_STATE, getRandomString()) + put(JSON_PACKAGE_BACKUP_TYPE, BackupType.FULL.name) put(JSON_PACKAGE_VERSION, Random.nextLong()) put(JSON_PACKAGE_INSTALLER, getRandomString()) put(JSON_PACKAGE_SHA256, getRandomString()) @@ -130,7 +133,9 @@ class MetadataReaderTest { }) val jsonBytes = json.toString().toByteArray(Utf8) val metadata = decoder.decode(jsonBytes, metadata.version, metadata.token) + assertEquals(this.metadata.salt, metadata.salt) assertEquals(UNKNOWN_ERROR, metadata.packageMetadataMap["org.example"]!!.state) + assertEquals(BackupType.FULL, metadata.packageMetadataMap["org.example"]!!.backupType) } @Test @@ -142,6 +147,7 @@ class MetadataReaderTest { val jsonBytes = json.toString().toByteArray(Utf8) val metadata = decoder.decode(jsonBytes, metadata.version, metadata.token) assertFalse(metadata.packageMetadataMap["org.example"]!!.system) + assertNull(metadata.packageMetadataMap["org.example"]!!.backupType) } @Test @@ -149,12 +155,14 @@ class MetadataReaderTest { val json = JSONObject(metadataByteArray.toString(Utf8)) json.put("org.example", JSONObject().apply { put(JSON_PACKAGE_TIME, Random.nextLong()) + put(JSON_PACKAGE_BACKUP_TYPE, BackupType.KV.name) }) val jsonBytes = json.toString().toByteArray(Utf8) val result = decoder.decode(jsonBytes, metadata.version, metadata.token) assertEquals(1, result.packageMetadataMap.size) val packageMetadata = result.packageMetadataMap.getOrElse("org.example") { fail() } + assertEquals(BackupType.KV, packageMetadata.backupType) assertNull(packageMetadata.version) assertNull(packageMetadata.installer) assertNull(packageMetadata.signatures) @@ -166,6 +174,7 @@ class MetadataReaderTest { return BackupMetadata( version = 1.toByte(), token = Random.nextLong(), + salt = getRandomBase64(METADATA_SALT_SIZE), time = Random.nextLong(), androidVersion = Random.nextInt(), androidIncremental = getRandomString(), diff --git a/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataV0ReadTest.kt b/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataV0ReadTest.kt index 0cf33d5b..d5796ae3 100644 --- a/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataV0ReadTest.kt +++ b/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataV0ReadTest.kt @@ -54,6 +54,7 @@ internal class MetadataV0ReadTest { ) = BackupMetadata( version = 0x00, token = 1337L, + salt = "", time = 2342L, androidVersion = 30, androidIncremental = "sdfqefpojlfj", diff --git a/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataWriterDecoderTest.kt b/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataWriterDecoderTest.kt index a85bbe6e..aa1c23f0 100644 --- a/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataWriterDecoderTest.kt +++ b/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataWriterDecoderTest.kt @@ -1,6 +1,7 @@ package com.stevesoltys.seedvault.metadata import com.stevesoltys.seedvault.crypto.Crypto +import com.stevesoltys.seedvault.getRandomBase64 import com.stevesoltys.seedvault.getRandomString import com.stevesoltys.seedvault.metadata.PackageState.APK_AND_DATA import com.stevesoltys.seedvault.metadata.PackageState.NOT_ALLOWED @@ -35,8 +36,8 @@ internal class MetadataWriterDecoderTest { fun `encoded metadata matches decoded metadata (with package, no apk info)`() { val time = Random.nextLong() val packages = HashMap().apply { - put(getRandomString(), PackageMetadata(time, APK_AND_DATA)) - put(getRandomString(), PackageMetadata(time, WAS_STOPPED)) + put(getRandomString(), PackageMetadata(time, APK_AND_DATA, BackupType.FULL)) + put(getRandomString(), PackageMetadata(time, WAS_STOPPED, BackupType.KV)) } val metadata = getMetadata(packages) assertEquals( @@ -52,6 +53,7 @@ internal class MetadataWriterDecoderTest { getRandomString(), PackageMetadata( time = Random.nextLong(), state = APK_AND_DATA, + backupType = BackupType.FULL, version = Random.nextLong(), installer = getRandomString(), splits = listOf( @@ -78,6 +80,7 @@ internal class MetadataWriterDecoderTest { getRandomString(), PackageMetadata( time = Random.nextLong(), state = QUOTA_EXCEEDED, + backupType = BackupType.FULL, system = Random.nextBoolean(), version = Random.nextLong(), installer = getRandomString(), @@ -89,6 +92,7 @@ internal class MetadataWriterDecoderTest { getRandomString(), PackageMetadata( time = Random.nextLong(), state = NO_DATA, + backupType = BackupType.KV, system = Random.nextBoolean(), version = Random.nextLong(), installer = getRandomString(), @@ -121,6 +125,7 @@ internal class MetadataWriterDecoderTest { return BackupMetadata( version = Random.nextBytes(1)[0], token = Random.nextLong(), + salt = getRandomBase64(32), time = Random.nextLong(), androidVersion = Random.nextInt(), androidIncremental = getRandomString(), 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 234e8d9d..52f20e47 100644 --- a/app/src/test/java/com/stevesoltys/seedvault/transport/CoordinatorIntegrationTest.kt +++ b/app/src/test/java/com/stevesoltys/seedvault/transport/CoordinatorIntegrationTest.kt @@ -13,6 +13,7 @@ import com.stevesoltys.seedvault.crypto.KeyManagerTestImpl import com.stevesoltys.seedvault.encodeBase64 import com.stevesoltys.seedvault.header.HeaderReaderImpl import com.stevesoltys.seedvault.header.MAX_SEGMENT_CLEARTEXT_LENGTH +import com.stevesoltys.seedvault.metadata.BackupType import com.stevesoltys.seedvault.metadata.MetadataReaderImpl import com.stevesoltys.seedvault.metadata.PackageMetadata import com.stevesoltys.seedvault.metadata.PackageState.UNKNOWN_ERROR @@ -33,7 +34,6 @@ import com.stevesoltys.seedvault.transport.restore.KVRestore import com.stevesoltys.seedvault.transport.restore.KVRestorePlugin import com.stevesoltys.seedvault.transport.restore.OutputFactory import com.stevesoltys.seedvault.transport.restore.RestoreCoordinator -import com.stevesoltys.seedvault.transport.restore.RestorePlugin import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager import io.mockk.CapturingSlot import io.mockk.Runs @@ -93,7 +93,6 @@ internal class CoordinatorIntegrationTest : TransportTest() { notificationManager ) - private val restorePlugin = mockk() private val kvRestorePlugin = mockk() private val kvRestore = KVRestore(kvRestorePlugin, outputFactory, headerReader, cryptoImpl) private val fullRestorePlugin = mockk() @@ -172,13 +171,11 @@ internal class CoordinatorIntegrationTest : TransportTest() { backupPlugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream every { - metadataManager.onApkBackedUp( - packageInfo, - packageMetadata, - metadataOutputStream - ) + metadataManager.onApkBackedUp(packageInfo, packageMetadata, metadataOutputStream) + } just Runs + every { + metadataManager.onPackageBackedUp(packageInfo, BackupType.KV, metadataOutputStream) } just Runs - every { metadataManager.onPackageBackedUp(packageInfo, metadataOutputStream) } just Runs // start and finish K/V backup assertEquals(TRANSPORT_OK, backup.performIncrementalBackup(packageInfo, fileDescriptor, 0)) @@ -252,7 +249,9 @@ internal class CoordinatorIntegrationTest : TransportTest() { coEvery { backupPlugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream - every { metadataManager.onPackageBackedUp(packageInfo, metadataOutputStream) } just Runs + every { + metadataManager.onPackageBackedUp(packageInfo, BackupType.KV, metadataOutputStream) + } just Runs // start and finish K/V backup assertEquals(TRANSPORT_OK, backup.performIncrementalBackup(packageInfo, fileDescriptor, 0)) @@ -314,7 +313,9 @@ internal class CoordinatorIntegrationTest : TransportTest() { metadataOutputStream ) } just Runs - every { metadataManager.onPackageBackedUp(packageInfo, metadataOutputStream) } just Runs + every { + metadataManager.onPackageBackedUp(packageInfo, BackupType.FULL, metadataOutputStream) + } just Runs // perform backup to output stream assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, fileDescriptor, 0)) diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/TransportTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/TransportTest.kt index 6691804a..74795d5d 100644 --- a/app/src/test/java/com/stevesoltys/seedvault/transport/TransportTest.kt +++ b/app/src/test/java/com/stevesoltys/seedvault/transport/TransportTest.kt @@ -10,8 +10,10 @@ import android.util.Log import com.stevesoltys.seedvault.Clock import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER import com.stevesoltys.seedvault.crypto.Crypto +import com.stevesoltys.seedvault.getRandomBase64 import com.stevesoltys.seedvault.getRandomString import com.stevesoltys.seedvault.metadata.BackupMetadata +import com.stevesoltys.seedvault.metadata.METADATA_SALT_SIZE import com.stevesoltys.seedvault.metadata.MetadataManager import com.stevesoltys.seedvault.settings.SettingsManager import io.mockk.every @@ -45,6 +47,7 @@ internal abstract class TransportTest { } protected val metadata = BackupMetadata( token = token, + salt = getRandomBase64(METADATA_SALT_SIZE), androidVersion = Random.nextInt(), androidIncremental = getRandomString(), deviceName = getRandomString() diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinatorTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinatorTest.kt index bac93f40..089c5737 100644 --- a/app/src/test/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinatorTest.kt +++ b/app/src/test/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinatorTest.kt @@ -15,6 +15,7 @@ import android.os.ParcelFileDescriptor import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER import com.stevesoltys.seedvault.coAssertThrows import com.stevesoltys.seedvault.getRandomString +import com.stevesoltys.seedvault.metadata.BackupType import com.stevesoltys.seedvault.metadata.PackageMetadata import com.stevesoltys.seedvault.metadata.PackageState.NOT_ALLOWED import com.stevesoltys.seedvault.metadata.PackageState.NO_DATA @@ -254,7 +255,9 @@ internal class BackupCoordinatorTest : BackupTest() { every { kv.getCurrentPackage() } returns packageInfo every { settingsManager.getToken() } returns token coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream - every { metadataManager.onPackageBackedUp(packageInfo, metadataOutputStream) } just Runs + every { + metadataManager.onPackageBackedUp(packageInfo, BackupType.KV, metadataOutputStream) + } just Runs every { kv.finishBackup() } returns result every { metadataOutputStream.close() } just Runs @@ -272,7 +275,9 @@ internal class BackupCoordinatorTest : BackupTest() { every { full.getCurrentPackage() } returns packageInfo every { settingsManager.getToken() } returns token coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream - every { metadataManager.onPackageBackedUp(packageInfo, metadataOutputStream) } just Runs + every { + metadataManager.onPackageBackedUp(packageInfo, BackupType.FULL, metadataOutputStream) + } just Runs every { full.finishBackup() } returns result every { metadataOutputStream.close() } just Runs @@ -302,7 +307,8 @@ internal class BackupCoordinatorTest : BackupTest() { metadataManager.onPackageBackupError( packageInfo, QUOTA_EXCEEDED, - metadataOutputStream + metadataOutputStream, + BackupType.FULL ) } just Runs coEvery { full.cancelFullBackup() } just Runs @@ -325,7 +331,12 @@ internal class BackupCoordinatorTest : BackupTest() { assertEquals(0L, backup.requestFullBackupTime()) verify(exactly = 1) { - metadataManager.onPackageBackupError(packageInfo, QUOTA_EXCEEDED, metadataOutputStream) + metadataManager.onPackageBackupError( + packageInfo, + QUOTA_EXCEEDED, + metadataOutputStream, + BackupType.FULL + ) } verify { metadataOutputStream.close() } } @@ -341,7 +352,8 @@ internal class BackupCoordinatorTest : BackupTest() { metadataManager.onPackageBackupError( packageInfo, NO_DATA, - metadataOutputStream + metadataOutputStream, + BackupType.FULL ) } just Runs coEvery { full.cancelFullBackup() } just Runs @@ -361,7 +373,12 @@ internal class BackupCoordinatorTest : BackupTest() { assertEquals(0L, backup.requestFullBackupTime()) verify(exactly = 1) { - metadataManager.onPackageBackupError(packageInfo, NO_DATA, metadataOutputStream) + metadataManager.onPackageBackupError( + packageInfo, + NO_DATA, + metadataOutputStream, + BackupType.FULL + ) } verify { metadataOutputStream.close() } }