Always run storage backups in a foreground service

otherwise we get killed for using too much CPU
This commit is contained in:
Torsten Grote 2021-07-02 15:57:37 -03:00 committed by Chirayu Desai
parent 056bdc3bf3
commit d13b9ea3fd
5 changed files with 39 additions and 11 deletions

View file

@ -19,6 +19,7 @@ import com.stevesoltys.seedvault.metadata.MetadataManager
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 @@ class UsbIntentReceiver : UsbMonitor() {
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()
}
}

View file

@ -3,6 +3,7 @@ package com.stevesoltys.seedvault.settings
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.util.Log
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.crypto.KeyManager
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.Dispatchers
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 @@ internal class SettingsViewModel(
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)
}
}

View file

@ -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 @@ force running with:
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() {

View file

@ -68,6 +68,8 @@ class MainViewModel(application: Application) : BackupContentViewModel(applicati
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)

View file

@ -21,14 +21,15 @@ public abstract class BackupService : Service() {
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 @@ public abstract class BackupService : Service() {
)
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
}