Always run storage backups in a foreground service
otherwise we get killed for using too much CPU
diff --git a/app/src/main/java/com/stevesoltys/seedvault/UsbIntentReceiver.kt b/app/src/main/java/com/stevesoltys/seedvault/UsbIntentReceiver.kt
index a4738fd..8f175a3 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/UsbIntentReceiver.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/UsbIntentReceiver.kt
@@ -19,6 +19,7 @@
import com.stevesoltys.seedvault.settings.FlashDrive
import com.stevesoltys.seedvault.settings.SettingsManager
import com.stevesoltys.seedvault.storage.StorageBackupService
+import com.stevesoltys.seedvault.storage.StorageBackupService.Companion.EXTRA_START_APP_BACKUP
import com.stevesoltys.seedvault.transport.requestBackup
import com.stevesoltys.seedvault.ui.storage.AUTHORITY_STORAGE
import org.koin.core.context.KoinContextHandler.get
@@ -57,14 +58,15 @@
override fun onStatusChanged(context: Context, action: String, device: UsbDevice) {
if (settingsManager.isStorageBackupEnabled()) {
- // TODO is it safe to start this at the same time as app backup?
val i = Intent(context, StorageBackupService::class.java)
+ // this starts an app backup afterwards
+ i.putExtra(EXTRA_START_APP_BACKUP, true)
startForegroundService(context, i)
+ } else {
+ Thread {
+ requestBackup(context)
+ }.start()
}
-
- Thread {
- requestBackup(context)
- }.start()
}
}
diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt
index 1179c80..e393a7d 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt
@@ -3,6 +3,7 @@
import android.app.Application
import android.app.job.JobInfo.NETWORK_TYPE_NONE
import android.app.job.JobInfo.NETWORK_TYPE_UNMETERED
+import android.content.Intent
import android.database.ContentObserver
import android.net.ConnectivityManager
import android.net.Network
@@ -14,6 +15,7 @@
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import androidx.annotation.UiThread
+import androidx.core.content.ContextCompat.startForegroundService
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations.switchMap
@@ -25,6 +27,8 @@
import com.stevesoltys.seedvault.metadata.MetadataManager
import com.stevesoltys.seedvault.permitDiskReads
import com.stevesoltys.seedvault.storage.StorageBackupJobService
+import com.stevesoltys.seedvault.storage.StorageBackupService
+import com.stevesoltys.seedvault.storage.StorageBackupService.Companion.EXTRA_START_APP_BACKUP
import com.stevesoltys.seedvault.transport.requestBackup
import com.stevesoltys.seedvault.ui.RequireProvisioningViewModel
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
@@ -32,7 +36,6 @@
import kotlinx.coroutines.launch
import org.calyxos.backup.storage.api.StorageBackup
import org.calyxos.backup.storage.backup.BackupJobService
-import org.calyxos.backup.storage.backup.NotificationBackupObserver
import java.util.concurrent.TimeUnit.HOURS
private const val TAG = "SettingsViewModel"
@@ -156,10 +159,13 @@
Toast.makeText(app, R.string.notification_backup_already_running, LENGTH_LONG).show()
} else viewModelScope.launch(Dispatchers.IO) {
if (settingsManager.isStorageBackupEnabled()) {
- val backupObserver = NotificationBackupObserver(app)
- storageBackup.runBackup(backupObserver)
+ val i = Intent(app, StorageBackupService::class.java)
+ // this starts an app backup afterwards
+ i.putExtra(EXTRA_START_APP_BACKUP, true)
+ startForegroundService(app, i)
+ } else {
+ requestBackup(app)
}
- requestBackup(app)
}
}
diff --git a/app/src/main/java/com/stevesoltys/seedvault/storage/Services.kt b/app/src/main/java/com/stevesoltys/seedvault/storage/Services.kt
index 5f55cae..1c54beb 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/storage/Services.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/storage/Services.kt
@@ -1,5 +1,7 @@
package com.stevesoltys.seedvault.storage
+import android.content.Intent
+import com.stevesoltys.seedvault.transport.requestBackup
import org.calyxos.backup.storage.api.BackupObserver
import org.calyxos.backup.storage.api.RestoreObserver
import org.calyxos.backup.storage.api.StorageBackup
@@ -24,12 +26,23 @@
internal class StorageBackupJobService : BackupJobService(StorageBackupService::class.java)
internal class StorageBackupService : BackupService() {
+
+ companion object {
+ internal const val EXTRA_START_APP_BACKUP = "startAppBackup"
+ }
+
override val storageBackup: StorageBackup by inject()
// use lazy delegate because context isn't available during construction time
override val backupObserver: BackupObserver by lazy {
NotificationBackupObserver(applicationContext)
}
+
+ override fun onBackupFinished(intent: Intent, success: Boolean) {
+ if (intent.getBooleanExtra(EXTRA_START_APP_BACKUP, false)) {
+ requestBackup(applicationContext)
+ }
+ }
}
internal class StorageRestoreService : RestoreService() {
diff --git a/storage/demo/src/main/java/de/grobox/storagebackuptester/MainViewModel.kt b/storage/demo/src/main/java/de/grobox/storagebackuptester/MainViewModel.kt
index d68d76c..777247b 100644
--- a/storage/demo/src/main/java/de/grobox/storagebackuptester/MainViewModel.kt
+++ b/storage/demo/src/main/java/de/grobox/storagebackuptester/MainViewModel.kt
@@ -68,6 +68,8 @@
viewModelScope.launch(Dispatchers.IO) {
val text = storageBackup.getUriSummaryString()
_backupLog.postValue(BackupProgress(0, 0, "Scanning: $text\n"))
+ // FIXME: This might get killed if we navigate away from the activity.
+ // A foreground service would avoid that.
if (storageBackup.runBackup(backupObserver)) {
// only prune old backups when backup run was successful
storageBackup.pruneOldBackups(backupObserver)
diff --git a/storage/lib/src/main/java/org/calyxos/backup/storage/backup/BackupService.kt b/storage/lib/src/main/java/org/calyxos/backup/storage/backup/BackupService.kt
index 17b772e..318bcbd 100644
--- a/storage/lib/src/main/java/org/calyxos/backup/storage/backup/BackupService.kt
+++ b/storage/lib/src/main/java/org/calyxos/backup/storage/backup/BackupService.kt
@@ -21,14 +21,15 @@
protected abstract val storageBackup: StorageBackup
protected abstract val backupObserver: BackupObserver?
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
Log.d(TAG, "onStartCommand $intent $flags $startId")
startForeground(
NOTIFICATION_ID_BACKUP,
n.getBackupNotification(R.string.notification_backup_scanning)
)
GlobalScope.launch {
- if (storageBackup.runBackup(backupObserver)) {
+ val success = storageBackup.runBackup(backupObserver)
+ if (success) {
// only prune old backups when backup run was successful
startForeground(
NOTIFICATION_ID_PRUNE,
@@ -36,11 +37,15 @@
)
storageBackup.pruneOldBackups(backupObserver)
}
+ onBackupFinished(intent, success)
stopSelf(startId)
}
return super.onStartCommand(intent, flags, startId)
}
+ protected open fun onBackupFinished(intent: Intent, success: Boolean) {
+ }
+
override fun onBind(intent: Intent?): IBinder? {
return null
}