Remove setting for unlimited quota

we set a hard limit for 1 GiB per app for now, but leave code in to make it configurable in the future
This commit is contained in:
Torsten Grote 2024-09-18 16:55:10 -03:00
parent 176a703720
commit 03d2946c93
No known key found for this signature in database
GPG key ID: 3E5F77D92CF891FF
16 changed files with 26 additions and 86 deletions

View file

@ -37,7 +37,7 @@ class KoinInstrumentationTestApp : App() {
single { spyk(BackupNotificationManager(context)) } single { spyk(BackupNotificationManager(context)) }
single { spyk(FullBackup(get(), get(), get(), get())) } single { spyk(FullBackup(get(), get(), get(), get())) }
single { spyk(KVBackup(get(), get(), get(), get())) } single { spyk(KVBackup(get(), get(), get())) }
single { spyk(InputFactory()) } single { spyk(InputFactory()) }
single { spyk(FullRestore(get(), get(), get(), get(), get(), get())) } single { spyk(FullRestore(get(), get(), get(), get(), get(), get())) }

View file

@ -40,7 +40,6 @@ class KvBackupInstrumentationTest : KoinComponent {
private val dbManager: KvDbManager by inject() private val dbManager: KvDbManager by inject()
private val backup = KVBackup( private val backup = KVBackup(
settingsManager = settingsManager,
backupReceiver = backupReceiver, backupReceiver = backupReceiver,
inputFactory = inputFactory, inputFactory = inputFactory,
dbManager = dbManager, dbManager = dbManager,

View file

@ -11,7 +11,6 @@ import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.Preference.OnPreferenceChangeListener import androidx.preference.Preference.OnPreferenceChangeListener
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat
import androidx.preference.TwoStatePreference import androidx.preference.TwoStatePreference
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.mms.ContentType.TEXT_PLAIN import com.google.android.mms.ContentType.TEXT_PLAIN
@ -65,13 +64,6 @@ class ExpertSettingsFragment : PreferenceFragmentCompat() {
createFileLauncher.launch(name) createFileLauncher.launch(name)
true true
} }
val quotaPreference = findPreference<SwitchPreferenceCompat>(PREF_KEY_UNLIMITED_QUOTA)
quotaPreference?.setOnPreferenceChangeListener { _, newValue ->
quotaPreference.isChecked = newValue as Boolean
true
}
} }
override fun onStart() { override fun onStart() {

View file

@ -55,7 +55,6 @@ private const val PREF_KEY_WEBDAV_PASS = "webDavPass"
private const val PREF_KEY_BACKUP_APP_BLACKLIST = "backupAppBlacklist" private const val PREF_KEY_BACKUP_APP_BLACKLIST = "backupAppBlacklist"
private const val PREF_KEY_BACKUP_STORAGE = "backup_storage" private const val PREF_KEY_BACKUP_STORAGE = "backup_storage"
internal const val PREF_KEY_UNLIMITED_QUOTA = "unlimited_quota"
internal const val PREF_KEY_LAST_BACKUP = "lastBackup" internal const val PREF_KEY_LAST_BACKUP = "lastBackup"
class SettingsManager(private val context: Context) { class SettingsManager(private val context: Context) {
@ -246,7 +245,7 @@ class SettingsManager(private val context: Context) {
prefs.edit().putStringSet(PREF_KEY_BACKUP_APP_BLACKLIST, blacklistedApps).apply() prefs.edit().putStringSet(PREF_KEY_BACKUP_APP_BLACKLIST, blacklistedApps).apply()
} }
fun isQuotaUnlimited() = prefs.getBoolean(PREF_KEY_UNLIMITED_QUOTA, false) val quota: Long = 1024 * 1024 * 1024 // 1 GiB for now
/** /**
* This assumes that if there's no storage plugin set, it is the first start. * This assumes that if there's no storage plugin set, it is the first start.

View file

@ -134,7 +134,7 @@ internal class BackupCoordinator(
fun getBackupQuota(packageName: String, isFullBackup: Boolean): Long { fun getBackupQuota(packageName: String, isFullBackup: Boolean): Long {
// report back quota // report back quota
Log.i(TAG, "Get backup quota for $packageName. Is full backup: $isFullBackup.") Log.i(TAG, "Get backup quota for $packageName. Is full backup: $isFullBackup.")
val quota = if (isFullBackup) full.quota else kv.quota val quota = settingsManager.quota
Log.i(TAG, "Reported quota of $quota bytes.") Log.i(TAG, "Reported quota of $quota bytes.")
return quota return quota
} }

View file

@ -21,7 +21,6 @@ val backupModule = module {
single<KvDbManager> { KvDbManagerImpl(androidContext()) } single<KvDbManager> { KvDbManagerImpl(androidContext()) }
single { single {
KVBackup( KVBackup(
settingsManager = get(),
backupReceiver = get(), backupReceiver = get(),
inputFactory = get(), inputFactory = get(),
dbManager = get(), dbManager = get(),

View file

@ -32,8 +32,6 @@ private class FullBackupState(
var size: Long = 0 var size: Long = 0
} }
const val DEFAULT_QUOTA_FULL_BACKUP = (2 * (25 * 1024 * 1024)).toLong()
private val TAG = FullBackup::class.java.simpleName private val TAG = FullBackup::class.java.simpleName
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
@ -48,12 +46,7 @@ internal class FullBackup(
val hasState: Boolean get() = state != null val hasState: Boolean get() = state != null
val currentPackageInfo get() = state?.packageInfo val currentPackageInfo get() = state?.packageInfo
val quota val quota get() = settingsManager.quota
get() = if (settingsManager.isQuotaUnlimited()) {
Long.MAX_VALUE
} else {
DEFAULT_QUOTA_FULL_BACKUP
}
fun checkFullBackupSize(size: Long): Int { fun checkFullBackupSize(size: Long): Int {
Log.i(TAG, "Check full backup size of $size bytes.") Log.i(TAG, "Check full backup size of $size bytes.")

View file

@ -16,7 +16,6 @@ import android.os.ParcelFileDescriptor
import android.util.Log import android.util.Log
import com.stevesoltys.seedvault.repo.BackupData import com.stevesoltys.seedvault.repo.BackupData
import com.stevesoltys.seedvault.repo.BackupReceiver import com.stevesoltys.seedvault.repo.BackupReceiver
import com.stevesoltys.seedvault.settings.SettingsManager
import java.io.IOException import java.io.IOException
class KVBackupState( class KVBackupState(
@ -24,12 +23,9 @@ class KVBackupState(
val db: KVDb, val db: KVDb,
) )
const val DEFAULT_QUOTA_KEY_VALUE_BACKUP = (2 * (5 * 1024 * 1024)).toLong()
private val TAG = KVBackup::class.java.simpleName private val TAG = KVBackup::class.java.simpleName
internal class KVBackup( internal class KVBackup(
private val settingsManager: SettingsManager,
private val backupReceiver: BackupReceiver, private val backupReceiver: BackupReceiver,
private val inputFactory: InputFactory, private val inputFactory: InputFactory,
private val dbManager: KvDbManager, private val dbManager: KvDbManager,
@ -39,12 +35,6 @@ internal class KVBackup(
val hasState get() = state != null val hasState get() = state != null
val currentPackageInfo get() = state?.packageInfo val currentPackageInfo get() = state?.packageInfo
val quota: Long
get() = if (settingsManager.isQuotaUnlimited()) {
Long.MAX_VALUE
} else {
DEFAULT_QUOTA_KEY_VALUE_BACKUP
}
fun performBackup( fun performBackup(
packageInfo: PackageInfo, packageInfo: PackageInfo,

View file

@ -67,8 +67,6 @@
<string name="settings_scheduling_charging_title">Back up only when charging</string> <string name="settings_scheduling_charging_title">Back up only when charging</string>
<string name="settings_expert_title">Expert settings</string> <string name="settings_expert_title">Expert settings</string>
<string name="settings_expert_quota_title">Unlimited app quota</string>
<string name="settings_expert_quota_summary">Do not impose a limitation on the size of app backups.\n\nWarning: This can fill up your storage location quickly. Not needed for most apps.</string>
<string name="settings_expert_logcat_title">Save app log</string> <string name="settings_expert_logcat_title">Save app log</string>
<string name="settings_expert_logcat_summary">Developers can diagnose bugs with these logs.\n\nWarning: The log file might contain personally identifiable information. Review before and delete after sharing!</string> <string name="settings_expert_logcat_summary">Developers can diagnose bugs with these logs.\n\nWarning: The log file might contain personally identifiable information. Review before and delete after sharing!</string>
<string name="settings_expert_logcat_error">Error: Could not save app log</string> <string name="settings_expert_logcat_error">Error: Could not save app log</string>

View file

@ -3,12 +3,6 @@
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
--> -->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="unlimited_quota"
android:summary="@string/settings_expert_quota_summary"
android:title="@string/settings_expert_quota_title" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:defaultValue="true" android:defaultValue="true"
android:icon="@drawable/ic_apps" android:icon="@drawable/ic_apps"

View file

@ -80,7 +80,6 @@ internal class CoordinatorIntegrationTest : TransportTest() {
private val snapshotManager = mockk<SnapshotManager>() private val snapshotManager = mockk<SnapshotManager>()
private val backupReceiver = mockk<BackupReceiver>() private val backupReceiver = mockk<BackupReceiver>()
private val kvBackup = KVBackup( private val kvBackup = KVBackup(
settingsManager = settingsManager,
backupReceiver = backupReceiver, backupReceiver = backupReceiver,
inputFactory = inputFactory, inputFactory = inputFactory,
dbManager = dbManager, dbManager = dbManager,
@ -287,7 +286,7 @@ internal class CoordinatorIntegrationTest : TransportTest() {
val bInputStream = ByteArrayInputStream(appData) val bInputStream = ByteArrayInputStream(appData)
every { inputFactory.getInputStream(fileDescriptor) } returns bInputStream every { inputFactory.getInputStream(fileDescriptor) } returns bInputStream
every { settingsManager.isQuotaUnlimited() } returns false every { settingsManager.quota } returns quota
coEvery { backupReceiver.addBytes(any(), capture(byteSlot)) } answers { coEvery { backupReceiver.addBytes(any(), capture(byteSlot)) } answers {
bOutputStream.writeBytes(byteSlot.captured) bOutputStream.writeBytes(byteSlot.captured)
} }

View file

@ -57,6 +57,7 @@ internal abstract class TransportTest {
protected val sigInfo: SigningInfo = mockk() protected val sigInfo: SigningInfo = mockk()
protected val token = Random.nextLong() protected val token = Random.nextLong()
protected val quota = Random.nextLong(1, Long.MAX_VALUE)
protected val applicationInfo = mockk<ApplicationInfo> { protected val applicationInfo = mockk<ApplicationInfo> {
flags = FLAG_ALLOW_BACKUP or FLAG_INSTALLED flags = FLAG_ALLOW_BACKUP or FLAG_INSTALLED
} }

View file

@ -87,11 +87,7 @@ internal class BackupCoordinatorTest : BackupTest() {
val isFullBackup = Random.nextBoolean() val isFullBackup = Random.nextBoolean()
val quota = Random.nextLong() val quota = Random.nextLong()
if (isFullBackup) { every { settingsManager.quota } returns quota
every { full.quota } returns quota
} else {
every { kv.quota } returns quota
}
assertEquals(quota, backup.getBackupQuota(packageInfo.packageName, isFullBackup)) assertEquals(quota, backup.getBackupQuota(packageInfo.packageName, isFullBackup))
} }
@ -182,9 +178,9 @@ internal class BackupCoordinatorTest : BackupTest() {
coEvery { coEvery {
full.performFullBackup(packageInfo, fileDescriptor, 0) full.performFullBackup(packageInfo, fileDescriptor, 0)
} returns TRANSPORT_OK } returns TRANSPORT_OK
every { full.quota } returns DEFAULT_QUOTA_FULL_BACKUP every { settingsManager.quota } returns quota
every { every {
full.checkFullBackupSize(DEFAULT_QUOTA_FULL_BACKUP + 1) full.checkFullBackupSize(quota + 1)
} returns TRANSPORT_QUOTA_EXCEEDED } returns TRANSPORT_QUOTA_EXCEEDED
every { full.currentPackageInfo } returns packageInfo every { full.currentPackageInfo } returns packageInfo
every { every {
@ -202,14 +198,8 @@ internal class BackupCoordinatorTest : BackupTest() {
TRANSPORT_OK, TRANSPORT_OK,
backup.performFullBackup(packageInfo, fileDescriptor, 0) backup.performFullBackup(packageInfo, fileDescriptor, 0)
) )
assertEquals( assertEquals(quota, backup.getBackupQuota(packageInfo.packageName, true))
DEFAULT_QUOTA_FULL_BACKUP, assertEquals(TRANSPORT_QUOTA_EXCEEDED, backup.checkFullBackupSize(quota + 1))
backup.getBackupQuota(packageInfo.packageName, true)
)
assertEquals(
TRANSPORT_QUOTA_EXCEEDED,
backup.checkFullBackupSize(DEFAULT_QUOTA_FULL_BACKUP + 1)
)
backup.cancelFullBackup() backup.cancelFullBackup()
assertEquals(0L, backup.requestFullBackupTime()) assertEquals(0L, backup.requestFullBackupTime())
@ -227,7 +217,7 @@ internal class BackupCoordinatorTest : BackupTest() {
coEvery { coEvery {
full.performFullBackup(packageInfo, fileDescriptor, 0) full.performFullBackup(packageInfo, fileDescriptor, 0)
} returns TRANSPORT_OK } returns TRANSPORT_OK
every { full.quota } returns DEFAULT_QUOTA_FULL_BACKUP every { settingsManager.quota } returns quota
every { full.checkFullBackupSize(0) } returns TRANSPORT_PACKAGE_REJECTED every { full.checkFullBackupSize(0) } returns TRANSPORT_PACKAGE_REJECTED
every { full.currentPackageInfo } returns packageInfo every { full.currentPackageInfo } returns packageInfo
every { every {
@ -241,14 +231,8 @@ internal class BackupCoordinatorTest : BackupTest() {
every { backendManager.backendProperties } returns safProperties every { backendManager.backendProperties } returns safProperties
every { settingsManager.useMeteredNetwork } returns false every { settingsManager.useMeteredNetwork } returns false
assertEquals( assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, fileDescriptor, 0))
TRANSPORT_OK, assertEquals(quota, backup.getBackupQuota(packageInfo.packageName, true))
backup.performFullBackup(packageInfo, fileDescriptor, 0)
)
assertEquals(
DEFAULT_QUOTA_FULL_BACKUP,
backup.getBackupQuota(packageInfo.packageName, true)
)
assertEquals(TRANSPORT_PACKAGE_REJECTED, backup.checkFullBackupSize(0)) assertEquals(TRANSPORT_PACKAGE_REJECTED, backup.checkFullBackupSize(0))
backup.cancelFullBackup() backup.cancelFullBackup()
assertEquals(0L, backup.requestFullBackupTime()) assertEquals(0L, backup.requestFullBackupTime())

View file

@ -17,6 +17,4 @@ internal abstract class BackupTest : TransportTest() {
protected val outputStream = mockk<OutputStream>() protected val outputStream = mockk<OutputStream>()
protected val encryptedOutputStream = mockk<OutputStream>() protected val encryptedOutputStream = mockk<OutputStream>()
protected val quota = DEFAULT_QUOTA_FULL_BACKUP
} }

View file

@ -49,21 +49,14 @@ internal class FullBackupTest : BackupTest() {
@Test @Test
fun `checkFullBackupSize exceeds quota`() { fun `checkFullBackupSize exceeds quota`() {
every { settingsManager.isQuotaUnlimited() } returns false every { settingsManager.quota } returns quota
assertEquals( assertEquals(
TRANSPORT_QUOTA_EXCEEDED, TRANSPORT_QUOTA_EXCEEDED,
backup.checkFullBackupSize(DEFAULT_QUOTA_FULL_BACKUP + 1) backup.checkFullBackupSize(quota + 1)
) )
} }
@Test
fun `checkFullBackupSize does not exceed quota when unlimited`() {
every { settingsManager.isQuotaUnlimited() } returns true
assertEquals(TRANSPORT_OK, backup.checkFullBackupSize(quota + 1))
}
@Test @Test
fun `checkFullBackupSize for no data`() { fun `checkFullBackupSize for no data`() {
assertEquals(TRANSPORT_PACKAGE_REJECTED, backup.checkFullBackupSize(0)) assertEquals(TRANSPORT_PACKAGE_REJECTED, backup.checkFullBackupSize(0))
@ -76,14 +69,14 @@ internal class FullBackupTest : BackupTest() {
@Test @Test
fun `checkFullBackupSize accepts min data`() { fun `checkFullBackupSize accepts min data`() {
every { settingsManager.isQuotaUnlimited() } returns false every { settingsManager.quota } returns quota
assertEquals(TRANSPORT_OK, backup.checkFullBackupSize(1)) assertEquals(TRANSPORT_OK, backup.checkFullBackupSize(1))
} }
@Test @Test
fun `checkFullBackupSize accepts max data`() { fun `checkFullBackupSize accepts max data`() {
every { settingsManager.isQuotaUnlimited() } returns false every { settingsManager.quota } returns quota
assertEquals(TRANSPORT_OK, backup.checkFullBackupSize(quota)) assertEquals(TRANSPORT_OK, backup.checkFullBackupSize(quota))
} }
@ -104,7 +97,8 @@ internal class FullBackupTest : BackupTest() {
@Test @Test
fun `sendBackupData first call over quota`() = runBlocking { fun `sendBackupData first call over quota`() = runBlocking {
every { settingsManager.isQuotaUnlimited() } returns false val quota = Random.nextInt(1, Int.MAX_VALUE).toLong()
every { settingsManager.quota } returns quota
every { inputFactory.getInputStream(data) } returns inputStream every { inputFactory.getInputStream(data) } returns inputStream
val numBytes = (quota + 1).toInt() val numBytes = (quota + 1).toInt()
expectSendData(numBytes) expectSendData(numBytes)
@ -123,7 +117,8 @@ internal class FullBackupTest : BackupTest() {
@Test @Test
fun `sendBackupData subsequent calls over quota`() = runBlocking { fun `sendBackupData subsequent calls over quota`() = runBlocking {
every { settingsManager.isQuotaUnlimited() } returns false val quota = (50 * 1024 * 1024).toLong()
every { settingsManager.quota } returns quota
every { inputFactory.getInputStream(data) } returns inputStream every { inputFactory.getInputStream(data) } returns inputStream
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0))
@ -155,7 +150,7 @@ internal class FullBackupTest : BackupTest() {
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0))
assertTrue(backup.hasState) assertTrue(backup.hasState)
every { settingsManager.isQuotaUnlimited() } returns false every { settingsManager.quota } returns quota
every { inputStream.read(any(), any(), bytes.size) } throws IOException() every { inputStream.read(any(), any(), bytes.size) } throws IOException()
assertEquals(TRANSPORT_ERROR, backup.sendBackupData(bytes.size)) assertEquals(TRANSPORT_ERROR, backup.sendBackupData(bytes.size))
@ -175,7 +170,7 @@ internal class FullBackupTest : BackupTest() {
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0))
assertTrue(backup.hasState) assertTrue(backup.hasState)
every { settingsManager.isQuotaUnlimited() } returns false every { settingsManager.quota } returns quota
every { inputStream.read(any(), 0, bytes.size) } returns bytes.size every { inputStream.read(any(), 0, bytes.size) } returns bytes.size
coEvery { backupReceiver.addBytes("FullBackup $packageName", any()) } throws IOException() coEvery { backupReceiver.addBytes("FullBackup $packageName", any()) } throws IOException()
@ -196,7 +191,7 @@ internal class FullBackupTest : BackupTest() {
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0))
assertTrue(backup.hasState) assertTrue(backup.hasState)
every { settingsManager.isQuotaUnlimited() } returns false every { settingsManager.quota } returns quota
expectSendData(bytes.size) expectSendData(bytes.size)
assertEquals(TRANSPORT_OK, backup.sendBackupData(bytes.size)) assertEquals(TRANSPORT_OK, backup.sendBackupData(bytes.size))
@ -215,7 +210,7 @@ internal class FullBackupTest : BackupTest() {
@Test @Test
fun `sendBackupData runs ok`() = runBlocking { fun `sendBackupData runs ok`() = runBlocking {
every { settingsManager.isQuotaUnlimited() } returns false every { settingsManager.quota } returns quota
every { inputFactory.getInputStream(data) } returns inputStream every { inputFactory.getInputStream(data) } returns inputStream
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0))

View file

@ -40,7 +40,6 @@ internal class KVBackupTest : BackupTest() {
private val dbManager = mockk<KvDbManager>() private val dbManager = mockk<KvDbManager>()
private val backup = KVBackup( private val backup = KVBackup(
settingsManager = settingsManager,
backupReceiver = backupReceiver, backupReceiver = backupReceiver,
inputFactory = inputFactory, inputFactory = inputFactory,
dbManager = dbManager, dbManager = dbManager,