Try to recover data for force stopped apps from latest snapshot
The system doesn't allow us to backup forced stopped apps, but if we had data for them once, we can at least carry it along.
This commit is contained in:
parent
c09ea7c075
commit
682afa1c69
5 changed files with 51 additions and 5 deletions
|
@ -114,8 +114,10 @@ internal class SnapshotCreator(
|
|||
* If we do *not* have data for the given [packageName],
|
||||
* we try to extract data from the given [snapshot] (ideally we latest we have) and
|
||||
* add it to the current snapshot under construction.
|
||||
*
|
||||
* @param warnNoData log a warning, if [snapshot] had no data for the given [packageName].
|
||||
*/
|
||||
fun onNoDataInCurrentRun(snapshot: Snapshot, packageName: String) {
|
||||
fun onNoDataInCurrentRun(snapshot: Snapshot, packageName: String, isStopped: Boolean = false) {
|
||||
log.info { "onKvPackageNotChanged(${snapshot.token}, $packageName)" }
|
||||
|
||||
if (appBuilderMap.containsKey(packageName)) {
|
||||
|
@ -125,7 +127,9 @@ internal class SnapshotCreator(
|
|||
}
|
||||
val app = snapshot.appsMap[packageName]
|
||||
if (app == null) {
|
||||
log.error { " No changed data for $packageName, but we had no data for it" }
|
||||
if (!isStopped) log.error {
|
||||
" No changed data for $packageName, but we had no data for it"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -145,10 +149,12 @@ internal class SnapshotCreator(
|
|||
appBuilderMap[packageName] = app.toBuilder()
|
||||
blobsMap.putAll(blobMap)
|
||||
|
||||
// record local metadata
|
||||
// record local metadata if this is not a stopped app
|
||||
if (!isStopped) {
|
||||
val packageInfo = PackageInfo().apply { this.packageName = packageName }
|
||||
metadataManager.onPackageBackedUp(packageInfo, app.type.toBackupType(), app.size)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this after all blobs for the app icons have been saved to the backend.
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
package com.stevesoltys.seedvault.settings
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||
|
@ -24,6 +25,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import androidx.recyclerview.widget.RecyclerView.Adapter
|
||||
import androidx.recyclerview.widget.RecyclerView.NO_POSITION
|
||||
import com.stevesoltys.seedvault.R
|
||||
import com.stevesoltys.seedvault.ui.AppBackupState.FAILED_WAS_STOPPED
|
||||
import com.stevesoltys.seedvault.ui.AppBackupState.SUCCEEDED
|
||||
import com.stevesoltys.seedvault.ui.AppViewHolder
|
||||
import com.stevesoltys.seedvault.ui.toRelativeTime
|
||||
|
@ -122,7 +124,17 @@ internal class AppStatusAdapter(private val toggleListener: AppStatusToggleListe
|
|||
" (${formatShortFileSize(v.context, item.size)})"
|
||||
}
|
||||
appInfo.visibility = VISIBLE
|
||||
} else if (item.status == FAILED_WAS_STOPPED && item.time > 0) {
|
||||
@SuppressLint("SetTextI18n")
|
||||
appInfo.text = if (item.size == null) {
|
||||
item.time.toRelativeTime(context).toString()
|
||||
} else {
|
||||
item.time.toRelativeTime(context).toString() +
|
||||
" (${formatShortFileSize(v.context, item.size)})"
|
||||
} + "\n${item.status.getBackupText(context)}"
|
||||
appInfo.visibility = VISIBLE
|
||||
}
|
||||
// setState() above sets appInfo state for other cases already
|
||||
checkBox.visibility = INVISIBLE
|
||||
}
|
||||
// show disabled items differently
|
||||
|
|
|
@ -11,6 +11,7 @@ import android.util.Log
|
|||
import com.stevesoltys.seedvault.metadata.MetadataManager
|
||||
import com.stevesoltys.seedvault.metadata.PackageState.NOT_ALLOWED
|
||||
import com.stevesoltys.seedvault.metadata.PackageState.WAS_STOPPED
|
||||
import com.stevesoltys.seedvault.repo.AppBackupManager
|
||||
import com.stevesoltys.seedvault.repo.SnapshotManager
|
||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||
import com.stevesoltys.seedvault.transport.backup.PackageService
|
||||
|
@ -22,6 +23,7 @@ import java.io.IOException
|
|||
|
||||
internal class ApkBackupManager(
|
||||
private val context: Context,
|
||||
private val appBackupManager: AppBackupManager,
|
||||
private val settingsManager: SettingsManager,
|
||||
private val snapshotManager: SnapshotManager,
|
||||
private val metadataManager: MetadataManager,
|
||||
|
@ -84,6 +86,15 @@ internal class ApkBackupManager(
|
|||
} catch (e: IOException) {
|
||||
Log.e(TAG, "Error storing new metadata for $packageName: ", e)
|
||||
}
|
||||
// see if there's data in latest snapshot for this app and re-use it
|
||||
// this can be helpful for backing up recently STOPPED apps
|
||||
snapshotManager.latestSnapshot?.let { snapshot ->
|
||||
appBackupManager.snapshotCreator?.onNoDataInCurrentRun(
|
||||
snapshot = snapshot,
|
||||
packageName = packageName,
|
||||
isStopped = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ val workerModule = module {
|
|||
single {
|
||||
ApkBackupManager(
|
||||
context = androidContext(),
|
||||
appBackupManager = get(),
|
||||
settingsManager = get(),
|
||||
snapshotManager = get(),
|
||||
metadataManager = get(),
|
||||
|
|
|
@ -15,6 +15,8 @@ import com.stevesoltys.seedvault.metadata.PackageMetadata
|
|||
import com.stevesoltys.seedvault.metadata.PackageState.NOT_ALLOWED
|
||||
import com.stevesoltys.seedvault.metadata.PackageState.UNKNOWN_ERROR
|
||||
import com.stevesoltys.seedvault.metadata.PackageState.WAS_STOPPED
|
||||
import com.stevesoltys.seedvault.repo.AppBackupManager
|
||||
import com.stevesoltys.seedvault.repo.SnapshotCreator
|
||||
import com.stevesoltys.seedvault.repo.SnapshotManager
|
||||
import com.stevesoltys.seedvault.transport.TransportTest
|
||||
import com.stevesoltys.seedvault.transport.backup.PackageService
|
||||
|
@ -33,6 +35,7 @@ import org.junit.jupiter.api.Test
|
|||
|
||||
internal class ApkBackupManagerTest : TransportTest() {
|
||||
|
||||
private val appBackupManager: AppBackupManager = mockk()
|
||||
private val snapshotManager: SnapshotManager = mockk()
|
||||
private val packageService: PackageService = mockk()
|
||||
private val apkBackup: ApkBackup = mockk()
|
||||
|
@ -43,6 +46,7 @@ internal class ApkBackupManagerTest : TransportTest() {
|
|||
|
||||
private val apkBackupManager = ApkBackupManager(
|
||||
context = context,
|
||||
appBackupManager = appBackupManager,
|
||||
settingsManager = settingsManager,
|
||||
snapshotManager = snapshotManager,
|
||||
metadataManager = metadataManager,
|
||||
|
@ -53,9 +57,11 @@ internal class ApkBackupManagerTest : TransportTest() {
|
|||
)
|
||||
|
||||
private val packageMetadata: PackageMetadata = mockk()
|
||||
private val snapshotCreator: SnapshotCreator = mockk()
|
||||
|
||||
init {
|
||||
every { backendManager.backend } returns backend
|
||||
every { appBackupManager.snapshotCreator } returns snapshotCreator
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -63,6 +69,8 @@ internal class ApkBackupManagerTest : TransportTest() {
|
|||
every { nm.onAppsNotBackedUp() } just Runs
|
||||
every { packageService.notBackedUpPackages } returns listOf(packageInfo)
|
||||
every { settingsManager.isBackupEnabled(packageInfo.packageName) } returns true
|
||||
every { snapshotManager.latestSnapshot } returns snapshot
|
||||
every { snapshotCreator.onNoDataInCurrentRun(snapshot, packageName, true) } just Runs
|
||||
|
||||
expectUploadIcons()
|
||||
|
||||
|
@ -87,6 +95,8 @@ internal class ApkBackupManagerTest : TransportTest() {
|
|||
every { nm.onAppsNotBackedUp() } just Runs
|
||||
every { packageService.notBackedUpPackages } returns listOf(packageInfo)
|
||||
every { settingsManager.isBackupEnabled(packageInfo.packageName) } returns true
|
||||
every { snapshotManager.latestSnapshot } returns snapshot
|
||||
every { snapshotCreator.onNoDataInCurrentRun(snapshot, packageName, true) } just Runs
|
||||
|
||||
expectUploadIcons()
|
||||
|
||||
|
@ -117,6 +127,8 @@ internal class ApkBackupManagerTest : TransportTest() {
|
|||
every { nm.onAppsNotBackedUp() } just Runs
|
||||
every { packageService.notBackedUpPackages } returns listOf(packageInfo)
|
||||
every { settingsManager.isBackupEnabled(packageInfo.packageName) } returns true
|
||||
every { snapshotManager.latestSnapshot } returns snapshot
|
||||
every { snapshotCreator.onNoDataInCurrentRun(snapshot, packageName, true) } just Runs
|
||||
|
||||
expectUploadIcons()
|
||||
|
||||
|
@ -141,6 +153,8 @@ internal class ApkBackupManagerTest : TransportTest() {
|
|||
every { nm.onAppsNotBackedUp() } just Runs
|
||||
every { packageService.notBackedUpPackages } returns listOf(packageInfo)
|
||||
every { settingsManager.isBackupEnabled(packageInfo.packageName) } returns true
|
||||
every { snapshotManager.latestSnapshot } returns snapshot
|
||||
every { snapshotCreator.onNoDataInCurrentRun(snapshot, packageName, true) } just Runs
|
||||
|
||||
expectUploadIcons()
|
||||
|
||||
|
@ -223,6 +237,8 @@ internal class ApkBackupManagerTest : TransportTest() {
|
|||
every { nm.onAppsNotBackedUp() } just Runs
|
||||
every { packageService.notBackedUpPackages } returns listOf(packageInfo)
|
||||
every { settingsManager.isBackupEnabled(packageInfo.packageName) } returns true
|
||||
every { snapshotManager.latestSnapshot } returns snapshot
|
||||
every { snapshotCreator.onNoDataInCurrentRun(snapshot, packageName, true) } just Runs
|
||||
|
||||
expectUploadIcons()
|
||||
|
||||
|
|
Loading…
Reference in a new issue