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