Tolerate backup attempts when flash drive is not plugged in
Also remove hardcoding of PACKAGE_MANAGER_SENTINEL constant
This commit is contained in:
parent
08018fcc9b
commit
470b5a2ccf
8 changed files with 59 additions and 10 deletions
|
@ -1,6 +1,7 @@
|
|||
package com.stevesoltys.backup
|
||||
|
||||
import android.app.Application
|
||||
import android.app.backup.BackupManager.PACKAGE_MANAGER_SENTINEL
|
||||
import android.app.backup.IBackupManager
|
||||
import android.content.Context.BACKUP_SERVICE
|
||||
import android.os.Build
|
||||
|
@ -33,4 +34,6 @@ class Backup : Application() {
|
|||
|
||||
}
|
||||
|
||||
const val MAGIC_PACKAGE_MANAGER = PACKAGE_MANAGER_SENTINEL
|
||||
|
||||
fun isDebugBuild() = Build.TYPE == "userdebug"
|
||||
|
|
|
@ -68,7 +68,7 @@ class NotificationBackupObserver(context: Context, private val userInitiated: Bo
|
|||
}
|
||||
|
||||
fun getAppName(pm: PackageManager, packageId: String): CharSequence {
|
||||
if (packageId == "@pm@") return packageId
|
||||
if (packageId == MAGIC_PACKAGE_MANAGER) return packageId
|
||||
val appInfo = pm.getApplicationInfo(packageId, 0)
|
||||
return pm.getApplicationLabel(appInfo)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.os.UserHandle
|
|||
import android.util.Log
|
||||
import com.google.android.collect.Sets.newArraySet
|
||||
import com.stevesoltys.backup.Backup
|
||||
import com.stevesoltys.backup.MAGIC_PACKAGE_MANAGER
|
||||
import java.util.*
|
||||
|
||||
private val TAG = PackageService::class.java.simpleName
|
||||
|
@ -49,7 +50,7 @@ class PackageService {
|
|||
|
||||
// add magic @pm@ package (PACKAGE_MANAGER_SENTINEL) which holds package manager data
|
||||
val packageArray = eligibleApps.toMutableList()
|
||||
packageArray.add("@pm@")
|
||||
packageArray.add(MAGIC_PACKAGE_MANAGER)
|
||||
|
||||
return packageArray.toTypedArray()
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package com.stevesoltys.backup.settings
|
|||
|
||||
import android.content.ContentResolver
|
||||
import android.provider.Settings
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.TimeUnit.DAYS
|
||||
|
||||
private val SETTING = Settings.Secure.BACKUP_MANAGER_CONSTANTS
|
||||
private const val DELIMITER = ','
|
||||
|
@ -11,13 +13,19 @@ private const val FULL_BACKUP_INTERVAL_MILLISECONDS = "full_backup_interval_mill
|
|||
|
||||
object BackupManagerSettings {
|
||||
|
||||
/**
|
||||
* This clears the backup settings, so that default values will be used.
|
||||
*/
|
||||
fun enableAutomaticBackups(resolver: ContentResolver) {
|
||||
// setting this to null will cause the BackupManagerConstants to use default values
|
||||
setSettingValue(resolver, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* This sets the backup intervals to a longer than default value. Currently 30 days
|
||||
*/
|
||||
fun disableAutomaticBackups(resolver: ContentResolver) {
|
||||
val value = Long.MAX_VALUE
|
||||
val value = DAYS.toMillis(30)
|
||||
val kv = "$KEY_VALUE_BACKUP_INTERVAL_MILLISECONDS=$value"
|
||||
val full = "$FULL_BACKUP_INTERVAL_MILLISECONDS=$value"
|
||||
setSettingValue(resolver, "$kv$DELIMITER$full")
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
package com.stevesoltys.backup.transport.backup
|
||||
|
||||
import android.app.backup.BackupTransport.TRANSPORT_ERROR
|
||||
import android.app.backup.BackupTransport.TRANSPORT_OK
|
||||
import android.app.backup.BackupTransport.*
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageInfo
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.util.Log
|
||||
import com.stevesoltys.backup.BackupNotificationManager
|
||||
import com.stevesoltys.backup.MAGIC_PACKAGE_MANAGER
|
||||
import com.stevesoltys.backup.metadata.MetadataWriter
|
||||
import com.stevesoltys.backup.settings.SettingsManager
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.TimeUnit.MINUTES
|
||||
import java.util.concurrent.TimeUnit.DAYS
|
||||
|
||||
private val TAG = BackupCoordinator::class.java.simpleName
|
||||
|
||||
|
@ -63,7 +63,8 @@ class BackupCoordinator(
|
|||
TRANSPORT_OK
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "Error initializing device", e)
|
||||
nm.onBackupError()
|
||||
// Show error notification if we were ready for backups
|
||||
if (getBackupBackoff() == 0L) nm.onBackupError()
|
||||
TRANSPORT_ERROR
|
||||
}
|
||||
}
|
||||
|
@ -101,6 +102,12 @@ class BackupCoordinator(
|
|||
}
|
||||
|
||||
fun performIncrementalBackup(packageInfo: PackageInfo, data: ParcelFileDescriptor, flags: Int): Int {
|
||||
// backups of package manager metadata do not respect backoff
|
||||
// we need to reject them manually when now is not a good time for a backup
|
||||
if (packageInfo.packageName == MAGIC_PACKAGE_MANAGER && getBackupBackoff() != 0L) {
|
||||
return TRANSPORT_PACKAGE_REJECTED
|
||||
}
|
||||
|
||||
val result = kv.performBackup(packageInfo, data, flags)
|
||||
if (result == TRANSPORT_OK) settingsManager.saveNewBackupTime()
|
||||
return result
|
||||
|
@ -194,7 +201,7 @@ class BackupCoordinator(
|
|||
|
||||
private fun getBackupBackoff(): Long {
|
||||
val noBackoff = 0L
|
||||
val defaultBackoff = MINUTES.toMillis(10)
|
||||
val defaultBackoff = DAYS.toMillis(30)
|
||||
|
||||
// back off if there's no storage set
|
||||
val storage = settingsManager.getStorage() ?: return defaultBackoff
|
||||
|
|
|
@ -42,7 +42,7 @@ class DocumentsProviderBackupPlugin(
|
|||
}
|
||||
|
||||
override val providerPackageName: String? by lazy {
|
||||
val authority = storage.rootBackupDir?.uri?.authority ?: return@lazy null
|
||||
val authority = storage.getAuthority() ?: return@lazy null
|
||||
val providerInfo = packageManager.resolveContentProvider(authority, 0) ?: return@lazy null
|
||||
providerInfo.packageName
|
||||
}
|
||||
|
|
|
@ -72,6 +72,8 @@ class DocumentsStorage(private val context: Context, private val settingsManager
|
|||
}
|
||||
}
|
||||
|
||||
fun getAuthority(): String? = storage?.uri?.authority
|
||||
|
||||
fun getSetDir(token: Long = currentToken): DocumentFile? {
|
||||
if (token == currentToken) return currentSetDir
|
||||
return rootBackupDir?.findFile(token.toString())
|
||||
|
|
|
@ -2,8 +2,12 @@ package com.stevesoltys.backup.transport.backup
|
|||
|
||||
import android.app.backup.BackupTransport.TRANSPORT_ERROR
|
||||
import android.app.backup.BackupTransport.TRANSPORT_OK
|
||||
import android.net.Uri
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import com.stevesoltys.backup.BackupNotificationManager
|
||||
import com.stevesoltys.backup.getRandomString
|
||||
import com.stevesoltys.backup.metadata.MetadataWriter
|
||||
import com.stevesoltys.backup.settings.Storage
|
||||
import io.mockk.Runs
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
|
@ -40,8 +44,11 @@ internal class BackupCoordinatorTest: BackupTest() {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `device initialization fails`() {
|
||||
fun `error notification when device initialization fails`() {
|
||||
val storage = Storage(Uri.EMPTY, getRandomString(), false)
|
||||
|
||||
every { plugin.initializeDevice() } throws IOException()
|
||||
every { settingsManager.getStorage() } returns storage
|
||||
every { notificationManager.onBackupError() } just Runs
|
||||
|
||||
assertEquals(TRANSPORT_ERROR, backup.initializeDevice())
|
||||
|
@ -54,6 +61,27 @@ internal class BackupCoordinatorTest: BackupTest() {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no error notification when device initialization fails on unplugged USB storage`() {
|
||||
val storage = mockk<Storage>()
|
||||
val documentFile = mockk<DocumentFile>()
|
||||
|
||||
every { plugin.initializeDevice() } throws IOException()
|
||||
every { settingsManager.getStorage() } returns storage
|
||||
every { storage.isUsb } returns true
|
||||
every { storage.getDocumentFile(context) } returns documentFile
|
||||
every { documentFile.isDirectory } returns false
|
||||
|
||||
assertEquals(TRANSPORT_ERROR, backup.initializeDevice())
|
||||
|
||||
// finish will only be called when TRANSPORT_OK is returned, so it should throw
|
||||
every { kv.hasState() } returns false
|
||||
every { full.hasState() } returns false
|
||||
assertThrows(IllegalStateException::class.java) {
|
||||
backup.finishBackup()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getBackupQuota() delegates to right plugin`() {
|
||||
val isFullBackup = Random.nextBoolean()
|
||||
|
|
Loading…
Add table
Reference in a new issue