Catch error when loading snapshots for app checking

This commit is contained in:
Torsten Grote 2024-10-28 15:32:39 -03:00
parent 83974b4121
commit 06191a1fc3
No known key found for this signature in database
GPG key ID: 3E5F77D92CF891FF
4 changed files with 49 additions and 26 deletions

View file

@ -49,20 +49,25 @@ internal class Checker(
private set private set
@WorkerThread @WorkerThread
suspend fun getBackupSize(): Long { suspend fun getBackupSize(): Long? {
// get all snapshots // get all snapshots
val folder = TopLevelFolder(crypto.repoId) val folder = TopLevelFolder(crypto.repoId)
val handles = mutableListOf<AppBackupFileType.Snapshot>() val handles = mutableListOf<AppBackupFileType.Snapshot>()
backendManager.backend.list(folder, AppBackupFileType.Snapshot::class) { fileInfo -> try {
handles.add(fileInfo.fileHandle as AppBackupFileType.Snapshot) backendManager.backend.list(folder, AppBackupFileType.Snapshot::class) { fileInfo ->
handles.add(fileInfo.fileHandle as AppBackupFileType.Snapshot)
}
val snapshots = snapshotManager.onSnapshotsLoaded(handles)
this.snapshots = snapshots // remember loaded snapshots
this.handleSize = handles.size // remember number of snapshot handles we had
} catch (e: Exception) {
log.error(e) { "Error loading snapshots: " }
// we swallow this exception, because an error will be shown in the next step
return null
} }
val snapshots = snapshotManager.onSnapshotsLoaded(handles)
this.snapshots = snapshots // remember loaded snapshots
this.handleSize = handles.size // remember number of snapshot handles we had
// get total disk space used by snapshots // get total disk space used by snapshots
val sizeMap = mutableMapOf<String, Int>() val sizeMap = mutableMapOf<String, Int>()
snapshots.forEach { snapshot -> snapshots?.forEach { snapshot ->
// add sizes to a map first, so we don't double count // add sizes to a map first, so we don't double count
snapshot.blobsMap.forEach { (chunkId, blob) -> sizeMap[chunkId] = blob.length } snapshot.blobsMap.forEach { (chunkId, blob) -> sizeMap[chunkId] = blob.length }
} }
@ -73,7 +78,12 @@ internal class Checker(
suspend fun check(percent: Int) { suspend fun check(percent: Int) {
check(percent in 0..100) { "Percent $percent out of bounds." } check(percent in 0..100) { "Percent $percent out of bounds." }
if (snapshots == null) getBackupSize() // just get size again to be sure we get snapshots if (snapshots == null) try {
getBackupSize() // just get size again to be sure we get snapshots
} catch (e: Exception) {
nm.onCheckFinishedWithError(0, 0)
checkerResult = CheckerResult.GeneralError(e)
}
val snapshots = snapshots ?: error("Snapshots still null") val snapshots = snapshots ?: error("Snapshots still null")
val handleSize = handleSize ?: error("Handle size still null") val handleSize = handleSize ?: error("Handle size still null")
check(handleSize >= snapshots.size) { check(handleSize >= snapshots.size) {

View file

@ -50,9 +50,11 @@ class AppCheckFragment : Fragment() {
} }
viewModel.backupSize.observe(viewLifecycleOwner) { viewModel.backupSize.observe(viewLifecycleOwner) {
slider.labelBehavior = LABEL_VISIBLE if (it != null) {
slider.invalidate() slider.labelBehavior = LABEL_VISIBLE
onSliderChanged(slider.value) slider.invalidate()
onSliderChanged(slider.value)
}
// we can stop observing as the loaded size won't change again // we can stop observing as the loaded size won't change again
viewModel.backupSize.removeObservers(viewLifecycleOwner) viewModel.backupSize.removeObservers(viewLifecycleOwner)
} }

View file

@ -56,9 +56,14 @@ class AppCheckResultActivity : BackupActivity() {
is CheckerResult.Success -> onSuccess(result) is CheckerResult.Success -> onSuccess(result)
is CheckerResult.Error -> onError(result) is CheckerResult.Error -> onError(result)
is CheckerResult.GeneralError, null -> { is CheckerResult.GeneralError, null -> {
// TODO if (result == null) {
if (result == null) log.error { "No more result" } val str = getString(R.string.backup_app_check_error_no_result)
else log.info((result as CheckerResult.GeneralError).e) { "Error: " } val e = NullPointerException(str)
val r = CheckerResult.GeneralError(e)
onGeneralError(r)
} else {
onGeneralError(result as CheckerResult.GeneralError)
}
} }
} }
checker.clear() checker.clear()
@ -98,14 +103,8 @@ class AppCheckResultActivity : BackupActivity() {
R.string.backup_app_check_error_only_broken_snapshots, R.string.backup_app_check_error_only_broken_snapshots,
result.existingSnapshots, result.existingSnapshots,
) )
} else if (result.existingSnapshots > result.snapshots.size) {
getString(
R.string.backup_app_check_error_some_snapshots,
result.existingSnapshots,
result.snapshots.size,
)
} else { } else {
getString(R.string.backup_app_check_error_read_all_snapshots, result.snapshots.size) getString(R.string.backup_app_check_error_has_snapshots, result.existingSnapshots)
} }
requireViewById<TextView>(R.id.introView).text = intro requireViewById<TextView>(R.id.introView).text = intro
@ -121,4 +120,16 @@ class AppCheckResultActivity : BackupActivity() {
) )
} }
private fun onGeneralError(result: CheckerResult.GeneralError) {
setContentView(R.layout.activity_check_result)
requireViewById<ImageView>(R.id.imageView).setImageResource(R.drawable.ic_cloud_error)
requireViewById<TextView>(R.id.titleView).setText(R.string.backup_app_check_error_title)
requireViewById<TextView>(R.id.introView).text =
getString(R.string.backup_app_check_error_no_snapshots)
requireViewById<TextView>(R.id.disclaimerView).text =
"${result.e.localizedMessage}\n\n${result.e}"
}
} }

View file

@ -196,12 +196,12 @@
<string name="notification_checking_action">Details</string> <string name="notification_checking_action">Details</string>
<string name="backup_app_check_success_intro">%1$d snapshots were found and %2$d%% of their data (%3$s) successfully verified:</string> <string name="backup_app_check_success_intro">%1$d snapshots were found and %2$d%% of their data (%3$s) successfully verified:</string>
<string name="backup_app_check_success_disclaimer">Note: We can not verify whether apps include all of their data in the backup.</string> <string name="backup_app_check_success_disclaimer">Note: we cannot verify whether individual apps include all of their user data in the backup.</string>
<string name="backup_app_check_error_title">@string/notification_checking_error_title</string> <string name="backup_app_check_error_title">@string/notification_checking_error_title</string>
<string name="backup_app_check_error_no_snapshots">We could not find any backup. Please run a successful backup first and then try checking again.</string> <string name="backup_app_check_error_no_snapshots">We could not find any backup. Please run a successful backup first and then try checking again.</string>
<string name="backup_app_check_error_only_broken_snapshots">We found %1$d backup snapshots. However, all of them were corrupted. Please run a successful backup and then try checking again.</string> <string name="backup_app_check_error_only_broken_snapshots">We found %1$d backup snapshots. However, all of them were corrupted. Please run a successful backup and then try checking again.</string>
<string name="backup_app_check_error_some_snapshots">We found %1$d backup snapshots. However, we could only read the following %2$d snapshots.</string> <string name="backup_app_check_error_has_snapshots">We found %1$d backup snapshots, some of them are corrupt or have errors. Below are the backups that could be restored.</string>
<string name="backup_app_check_error_read_all_snapshots">We found %1$d backup snapshots. However, some had errors and can not be fully restored.</string> <string name="backup_app_check_error_no_result">We lost the detailed results. Did a long time pass since running the check? Try running again.</string>
<!-- App Backup and Restore State --> <!-- App Backup and Restore State -->
@ -235,7 +235,7 @@
<string name="restore_restore_set_apps_no_size">Has user data for <xliff:g example="42" id="apps">%1$d</xliff:g> apps</string> <string name="restore_restore_set_apps_no_size">Has user data for <xliff:g example="42" id="apps">%1$d</xliff:g> apps</string>
<string name="restore_restore_set_apks">Contains <xliff:g example="42" id="apps">%1$d</xliff:g> apps (<xliff:g example="1 GB" id="size">%2$s</xliff:g>)</string> <string name="restore_restore_set_apks">Contains <xliff:g example="42" id="apps">%1$d</xliff:g> apps (<xliff:g example="1 GB" id="size">%2$s</xliff:g>)</string>
<string name="restore_restore_set_apks_no_size">Contains <xliff:g example="42" id="apps">%1$d</xliff:g> apps</string> <string name="restore_restore_set_apks_no_size">Contains <xliff:g example="42" id="apps">%1$d</xliff:g> apps</string>
<string name="restore_restore_set_can_not_get_restored">Can not be (fully) restored.</string> <string name="restore_restore_set_can_not_get_restored">This backup has some errors. You may be able to restore it partly.</string>
<string name="restore_skip">Don\'t restore</string> <string name="restore_skip">Don\'t restore</string>
<string name="restore_skip_apps">Skip restoring apps</string> <string name="restore_skip_apps">Skip restoring apps</string>
<string name="restore_invalid_location_title">No backups found</string> <string name="restore_invalid_location_title">No backups found</string>