recoverPendingInstall method
Recovers a pending native install marker from the app-owned store.
Implementation
Future<void> recoverPendingInstall() async {
final UpdateInstallRecoveryMarker? marker;
try {
marker = await recoveryStore.readPendingInstall(channel: channel);
} on Object catch (error) {
_diagnosticsRecorder.record(
stage: UpdateDiagnosticStage.install,
level: UpdateDiagnosticLevel.warning,
message: "Recovery marker read failed.",
error: error,
);
notifyListeners();
return;
}
if (marker == null) {
return;
}
_diagnosticsRecorder
..clear()
..record(
stage: UpdateDiagnosticStage.install,
level: UpdateDiagnosticLevel.warning,
message: "Pending install marker found for "
"${marker.updateVersion ?? "unknown update"}.",
);
_diagnosticsRecorder.record(
stage: UpdateDiagnosticStage.install,
level: UpdateDiagnosticLevel.info,
message: "Recovery started for pending install.",
);
final transactionId = marker.transactionId;
if (transactionId != null && transactionId.isNotEmpty) {
final NativeInstallTransactionStatus? nativeStatus;
try {
final recovery = DesktopUpdaterPlatform.instance.nativeInstallRecovery;
if (_isWindows) {
if (recovery is! AtomicAfterExitNativeInstallRecovery) {
throw StateError(
"Windows native recovery must be atomic after caller exit.",
);
}
nativeStatus = await recovery
.resolvePendingInstallTransactionAfterExit(transactionId);
} else {
if (recovery is! QueryAndRecoverNativeInstallRecovery) {
throw StateError(
"This platform must expose query/recover native recovery.",
);
}
var status = await recovery.queryInstallTransaction(transactionId);
if (status?.requiresRecovery ?? false) {
const maximumRecoveryAttempts = 6;
var attempts = 0;
do {
status = await recovery
.recoverPendingInstallTransaction(transactionId);
attempts += 1;
} while ((status?.awaitsCallerExit ?? false) &&
attempts < maximumRecoveryAttempts);
}
nativeStatus = status;
}
} on Object catch (error) {
_failRecoveredInstall(
marker,
StateError("Could not verify native install transaction status."),
message: "Could not verify native install transaction status.",
appVersion: marker.appVersion,
error: error,
);
return;
}
// A custom released platform implementation has no native recovery
// lookup surface, so preserve its existing version-only behavior.
if (nativeStatus?.awaitsCallerExit ?? false) {
_state = const UpdateInstalling();
notifyListeners();
return;
}
if (nativeStatus != null && !nativeStatus.isTerminalSuccess) {
if (nativeStatus.isTerminalFailure) {
final cleaned = await _cleanupRecoveredOwnedStage(marker);
if (cleaned) {
await _clearPendingRecoveryMarker();
}
}
_failRecoveredInstall(
marker,
StateError("Native helper did not confirm a completed install."),
message: "Native helper did not confirm a completed install.",
appVersion: marker.appVersion,
);
return;
}
}
final DesktopVersionInfo? currentVersion;
try {
currentVersion = await currentVersionInfo();
} on Object catch (error) {
_failRecoveredInstall(
marker,
StateError("Could not verify completed install after relaunch."),
message: "Could not verify completed install after relaunch.",
appVersion: marker.appVersion,
error: error,
);
return;
}
if (currentVersion == null) {
_failRecoveredInstall(
marker,
StateError("Could not verify completed install after relaunch."),
message: "Could not verify completed install after relaunch.",
appVersion: marker.appVersion,
);
return;
}
final currentAppVersion = _formatVersionInfo(currentVersion);
_currentAppVersion = currentAppVersion ?? marker.appVersion;
if (_matchesRecoveredTarget(currentVersion, marker)) {
if (!await _cleanupRecoveredOwnedStage(marker)) {
_failRecoveredInstall(
marker,
StateError("Completed install stage cleanup failed."),
message: "Completed install stage cleanup failed.",
appVersion: _currentAppVersion,
);
return;
}
await _clearPendingRecoveryMarker();
_state = const UpdateIdle();
notifyListeners();
return;
}
_failRecoveredInstall(
marker,
StateError("Pending install did not complete after relaunch."),
message: "Pending install did not complete after relaunch.",
appVersion: _currentAppVersion,
);
}