private void ThreadExecution(CancellationToken cancellationToken) { try { _state.Value = PatcherState.None; DebugLogger.Log("Patcher thread started."); ThreadLoadPatcherData(); EnsureSingleInstance(); ThreadLoadPatcherConfiguration(); UnityDispatcher.Invoke(() => _app = new App(_data.Value.AppDataPath, _data.Value.AppSecret, _data.Value.OverrideLatestVersionId)).WaitOne(); while (true) { cancellationToken.ThrowIfCancellationRequested(); ThreadWaitForUserDecision(cancellationToken); cancellationToken.ThrowIfCancellationRequested(); ThreadExecuteUserDecision(cancellationToken); } } catch (OperationCanceledException) { DebugLogger.Log("Patcher thread finished: thread has been cancelled."); } catch (ThreadInterruptedException) { DebugLogger.Log("Patcher thread finished: thread has been interrupted."); } catch (ThreadAbortException) { DebugLogger.Log("Patcher thread finished: thread has been aborted."); } catch (MultipleInstancesException exception) { DebugLogger.LogException(exception); Quit(); } catch (Exception exception) { DebugLogger.LogError("Patcher thread failed: an exception has occured."); DebugLogger.LogException(exception); } finally { _state.Value = PatcherState.None; if (_app != null) { _app.Dispose(); _app = null; } } }
private void ThreadDisplayError(PatcherError error, CancellationToken cancellationToken) { try { DebugLogger.Log(string.Format("Displaying patcher error {0}...", error)); ErrorDialog.Display(error, cancellationToken); DebugLogger.Log(string.Format("Patcher error {0} displayed.", error)); } catch (OperationCanceledException) { DebugLogger.Log(string.Format("Displaying patcher error {0} cancelled.", _userDecision)); } catch (ThreadInterruptedException) { DebugLogger.Log(string.Format("Displaying patcher error {0} interrupted: thread has been interrupted. Rethrowing exception.", error)); throw; } catch (ThreadAbortException) { DebugLogger.Log(string.Format("Displaying patcher error {0} aborted: thread has been aborted. Rethrowing exception.", error)); throw; } catch (Exception) { DebugLogger.LogWarning(string.Format("Error while displaying patcher error {0}: an exception has occured. Rethrowing exception.", error)); throw; } }
private void ThreadUpdateApp(CancellationToken cancellationToken) { _state.Value = PatcherState.UpdatingApp; _remoteVersionId.Value = _app.GetLatestVersionId(); if (_app.IsInstalled()) { _localVersionId.Value = _app.GetInstalledVersionId(); } _updateAppCancellationTokenSource = new CancellationTokenSource(); using (cancellationToken.Register(() => _updateAppCancellationTokenSource.Cancel())) { var appUpdater = new AppUpdater.AppUpdater(new AppUpdaterContext(_app, _configuration.AppUpdaterConfiguration)); appUpdater.Context.StatusMonitor.OverallStatusChanged += OnUpdateAppStatusChanged; try { appUpdater.Update(_updateAppCancellationTokenSource.Token); } finally { appUpdater.Context.StatusMonitor.OverallStatusChanged -= OnUpdateAppStatusChanged; _updateAppCancellationTokenSource = null; } } }
private void ThreadDisplayError(PatcherErrorMessage error, CancellationToken cancellationToken) { PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.PatcherFailed); try { _state.Value = PatcherState.DisplayingError; DebugLogger.Log(string.Format("Displaying patcher error {0}...", error.Message)); ErrorDialog.Display(error, cancellationToken); DebugLogger.Log(string.Format("Patcher error {0} displayed.", error.Message)); } catch (OperationCanceledException) { DebugLogger.Log(string.Format("Displaying patcher error {0} cancelled.", _userDecision)); } catch (ThreadInterruptedException) { DebugLogger.Log(string.Format("Displaying patcher error {0} interrupted: thread has been interrupted. Rethrowing exception.", error.Message)); throw; } catch (ThreadAbortException) { DebugLogger.Log(string.Format("Displaying patcher error {0} aborted: thread has been aborted. Rethrowing exception.", error.Message)); throw; } catch (Exception) { DebugLogger.LogWarning(string.Format("Error while displaying patcher error {}: an exception has occured. Rethrowing exception.", error.Message)); throw; } }
// returns true if data is valid (was valid from the start or successfull repair was performed) public bool Perform(PatchKit.Unity.Patcher.Cancellation.CancellationToken cancellationToken) { _lowestVersionWithContentId = Context.App.GetLowestVersionWithContentId(cancellationToken); for (int attempt = 1; attempt <= RepeatCount; ++attempt) { DebugLogger.Log("Running integrity check, attempt " + attempt + " of " + RepeatCount); if (PerformInternal(cancellationToken)) { return(true); } } // retry count reached, let's check for the last time if data is ok, but without repairing int installedVersionId = Context.App.GetInstalledVersionId(); VersionIntegrity results = CheckIntegrity(cancellationToken, installedVersionId); var filesNeedFixing = FilesNeedFixing(results); if (filesNeedFixing.Count() == 0) { DebugLogger.Log("No missing or invalid size files."); return(true); } DebugLogger.LogError("Still have corrupted files after all fixing attempts"); return(false); }
// returns true if there was no integrity errors, false if there was and repair was performed private bool PerformInternal(PatchKit.Unity.Patcher.Cancellation.CancellationToken cancellationToken) { int installedVersionId = Context.App.GetInstalledVersionId(); VersionIntegrity results = CheckIntegrity(cancellationToken, installedVersionId); var filesNeedFixing = FilesNeedFixing(results); if (filesNeedFixing.Count() == 0) { DebugLogger.Log("No missing or invalid size files."); return(true); } // need to collect some data about the application to calculate the repair cost and make decisions int latestVersionId = Context.App.GetLatestVersionId(true, cancellationToken); AppContentSummary installedVersionContentSummary = Context.App.RemoteMetaData.GetContentSummary(installedVersionId, cancellationToken); AppContentSummary latestVersionContentSummary = Context.App.RemoteMetaData.GetContentSummary(latestVersionId, cancellationToken); bool isNewVersionAvailable = installedVersionId < latestVersionId; long contentSize = isNewVersionAvailable ? latestVersionContentSummary.Files.Sum(f => f.Size) : installedVersionContentSummary.Files.Sum(f => f.Size); double repairCost = CalculateRepairCost(installedVersionContentSummary, filesNeedFixing); // increasing repair costs that reinstallation will be done for 1/3 of the content size repairCost *= IncreaseRepairCost; if (_lowestVersionWithContentId > installedVersionId) { DebugLogger.Log( "Repair is impossible because lowest version with content id is " + _lowestVersionWithContentId + " and currently installed version id is " + installedVersionId + ". Reinstalling."); ReinstallContent(cancellationToken); } else if (repairCost < contentSize) { DebugLogger.Log(string.Format("Repair cost {0} is smaller than content cost {1}, repairing...", repairCost, contentSize)); IAppUpdaterStrategy repairStrategy = _strategyResolver.Create(StrategyType.Repair, Context); repairStrategy.Update(cancellationToken); } else { DebugLogger.Log(string.Format("Content cost {0} is smaller than repair {1}. Reinstalling.", contentSize, repairCost)); ReinstallContent(cancellationToken); } return(false); }
/// <summary> /// Like Thread.Sleep() but checks if cancelation occured meanwhile /// </summary> /// <param name="duration">Miliseconds, time to sleep</param> /// <param name="cancellationToken">token to check cancellation exception</param> public static void CancelableSleep(int duration, PatchKit.Unity.Patcher.Cancellation.CancellationToken cancellationToken) { // FIX: Bug #692 int singleSleep = 100; for (int i = 0; i < duration / singleSleep; ++i) { cancellationToken.ThrowIfCancellationRequested(); Thread.Sleep(singleSleep); } }
private void ReinstallContent(PatchKit.Unity.Patcher.Cancellation.CancellationToken cancellationToken) { IUninstallCommand uninstall = _commandFactory.CreateUninstallCommand(Context); uninstall.Prepare(_status, cancellationToken); uninstall.Execute(cancellationToken); // not catching any exceptions here, because exception during content installation in this place should be fatal var contentStrategy = new AppUpdaterContentStrategy(Context, _status); contentStrategy.RepairOnError = false; // do not attempt to repair content to not cause a loop contentStrategy.Update(cancellationToken); }
private bool TryHandleFallback(PatchKit.Unity.Patcher.Cancellation.CancellationToken cancellationToken) { var fallbackType = _strategyResolver.GetFallbackStrategy(_strategy.GetStrategyType()); if (fallbackType == StrategyType.None) { return(false); } _strategy = _strategyResolver.Create(fallbackType, Context); _strategy.Update(cancellationToken); return(true); }
public override void Prepare(UpdaterStatus status, CancellationToken cancellationToken) { base.Prepare(status, cancellationToken); if (status == null) { throw new ArgumentNullException("status"); } DebugLogger.Log("Preparing package download."); _status = new DownloadStatus { Weight = { Value = StatusWeightHelper.GetResourceDownloadWeight(_resource) }, Description = { Value = "Downloading package..." } }; status.RegisterOperation(_status); }
private VersionIntegrity CheckIntegrity( PatchKit.Unity.Patcher.Cancellation.CancellationToken cancellationToken, int installedVersionId ) { ICheckVersionIntegrityCommand checkIntegrity = _commandFactory .CreateCheckVersionIntegrityCommand( versionId: installedVersionId, context: Context, isCheckingHash: CheckHashes, isCheckingSize: true, cancellationToken: cancellationToken); checkIntegrity.Prepare(_status, cancellationToken); checkIntegrity.Execute(cancellationToken); return(checkIntegrity.Results); }
private void ThreadUpdateApp(bool automatically, CancellationToken cancellationToken) { _state.Value = PatcherState.Connecting; _updateAppCancellationTokenSource = new PatchKit.Unity.Patcher.Cancellation.CancellationTokenSource(); using (cancellationToken.Register(() => _updateAppCancellationTokenSource.Cancel())) { _appInfo.Value = _app.RemoteMetaData.GetAppInfo(!automatically, _updateAppCancellationTokenSource.Token); _remoteVersionId.Value = _app.GetLatestVersionId(!automatically, _updateAppCancellationTokenSource.Token); if (_app.IsFullyInstalled()) { _localVersionId.Value = _app.GetInstalledVersionId(); } var appUpdater = new AppUpdater.AppUpdater(new AppUpdaterContext(_app, _configuration.AppUpdaterConfiguration)); try { _updaterStatus.Value = appUpdater.Status; using (_updaterStatus.Take(1).Subscribe((status) => _state.Value = PatcherState.UpdatingApp)) { appUpdater.Update(_updateAppCancellationTokenSource.Token); _wasUpdateSuccessfulOrNotNecessary = true; } } catch (OperationCanceledException) { PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.PatcherCanceled); throw; } finally { _state.Value = PatcherState.None; _updaterStatus.Value = null; _updateAppCancellationTokenSource = null; } } }
public void Update(PatchKit.Unity.Patcher.Cancellation.CancellationToken cancellationToken) { Assert.MethodCalledOnlyOnce(ref _updateHasBeenCalled, "Update"); if (Context.App.IsInstallationBroken() || Context.App.IsFullyInstalled()) { PreUpdate(cancellationToken); } DebugLogger.Log("Updating."); StrategyType type = _strategyResolver.Resolve(Context, cancellationToken); _strategy = _strategyResolver.Create(type, Context); try { _strategy.Update(cancellationToken); } catch (Exception ex) { if (ex is OperationCanceledException || ex is UnauthorizedAccessException || ex is NotEnoughtDiskSpaceException || ex is ThreadInterruptedException || ex is ThreadAbortException) { DebugLogger.LogWarning("Strategy caused exception, to be handled further"); throw; } else { DebugLogger.LogWarningFormat("Strategy caused exception, being handled by fallback: {0}, Trace: {1}", ex, ex.StackTrace); if (!TryHandleFallback(cancellationToken)) { throw; } } } }
protected void Display(PatchKit.Unity.Patcher.Cancellation.CancellationToken cancellationToken) { Assert.IsFalse(_unityThread == Thread.CurrentThread, "Display dialog can be only used on separate thread."); try { _isDisplaying = true; _dialogDisplayed.Reset(); using (cancellationToken.Register(() => _dialogDisplayed.Set())) { cancellationToken.ThrowIfCancellationRequested(); _dialogDisplayed.WaitOne(); } } finally { _isDisplaying = false; } }
private void ThreadUpdateApp(CancellationToken cancellationToken) { _state.Value = PatcherState.UpdatingApp; _updateAppCancellationTokenSource = new CancellationTokenSource(); using (cancellationToken.Register(() => _updateAppCancellationTokenSource.Cancel())) { var appUpdater = new AppUpdater.AppUpdater(_app, _configuration.AppUpdaterConfiguration); appUpdater.Context.StatusMonitor.OverallStatusChanged += OnUpdateAppStatusChanged; try { appUpdater.Update(_updateAppCancellationTokenSource.Token); } finally { appUpdater.Context.StatusMonitor.OverallStatusChanged -= OnUpdateAppStatusChanged; _updateAppCancellationTokenSource = null; } } }
private void ThreadExecuteUserDecision(CancellationToken cancellationToken) { bool displayWarningInsteadOfError = false; try { _warning.Value = string.Empty; DebugLogger.Log(string.Format("Executing user decision {0}...", _userDecision)); switch (_userDecision) { case UserDecision.None: break; case UserDecision.RepairApp: break; case UserDecision.StartAppAutomatically: case UserDecision.StartApp: ThreadStartApp(); break; case UserDecision.InstallAppAutomatically: displayWarningInsteadOfError = _app.IsFullyInstalled(); ThreadUpdateApp(true, cancellationToken); break; case UserDecision.InstallApp: ThreadUpdateApp(false, cancellationToken); break; case UserDecision.CheckForAppUpdatesAutomatically: displayWarningInsteadOfError = _app.IsFullyInstalled(); ThreadUpdateApp(true, cancellationToken); break; case UserDecision.CheckForAppUpdates: ThreadUpdateApp(false, cancellationToken); break; } DebugLogger.Log(string.Format("User decision {0} execution done.", _userDecision)); } catch (OperationCanceledException) { DebugLogger.Log(string.Format("User decision {0} execution cancelled.", _userDecision)); } catch (UnauthorizedAccessException e) { DebugLogger.Log(string.Format("User decision {0} execution issue: permissions failure.", _userDecision)); DebugLogger.LogException(e); if (ThreadTryRestartWithRequestForPermissions()) { UnityDispatcher.Invoke(Quit); } else { ThreadDisplayError(PatcherErrorMessage.NoPermissions(), cancellationToken); } } catch (ApiConnectionException e) { DebugLogger.LogException(e); if (displayWarningInsteadOfError) { _warning.Value = "Unable to check for updates. Please check your internet connection."; } else { ThreadDisplayError(PatcherErrorMessage.NoInternetConnection(), cancellationToken); } } catch (NotEnoughtDiskSpaceException e) { DebugLogger.LogException(e); ThreadDisplayError(PatcherErrorMessage.NotEnoughDiskSpace(e.RequiredSpace - e.AvailableSpace), cancellationToken); } catch (ThreadInterruptedException) { DebugLogger.Log(string.Format( "User decision {0} execution interrupted: thread has been interrupted. Rethrowing exception.", _userDecision)); throw; } catch (ThreadAbortException) { DebugLogger.Log(string.Format( "User decision {0} execution aborted: thread has been aborted. Rethrowing exception.", _userDecision)); throw; } catch (Exception exception) { DebugLogger.LogWarning(string.Format( "Error while executing user decision {0}: an exception has occured.", _userDecision)); DebugLogger.LogException(exception); if (displayWarningInsteadOfError) { _warning.Value = "Unable to check for updates. Please check your internet connection."; } else { ThreadDisplayError(PatcherErrorMessage.Other(), cancellationToken); } } }
private void ThreadWaitForUserDecision(CancellationToken cancellationToken) { try { DebugLogger.Log("Waiting for user decision..."); _state.Value = PatcherState.WaitingForUserDecision; bool isInstalled = _app.IsFullyInstalled(); DebugLogger.LogVariable(isInstalled, "isInstalled"); bool canRepairApp = false; // not implemented bool canInstallApp = !isInstalled; bool canCheckForAppUpdates = isInstalled; bool canStartApp = isInstalled; _isAppInstalled.Value = isInstalled; _canRepairApp.Value = false; _canInstallApp.Value = false; _canCheckForAppUpdates.Value = false; _canStartApp.Value = false; if (canInstallApp && _configuration.AutomaticallyInstallApp && !_hasAutomaticallyInstalledApp) { DebugLogger.Log("Automatically deciding to install app."); _hasAutomaticallyInstalledApp = true; _hasAutomaticallyCheckedForAppUpdate = true; _userDecision = UserDecision.InstallAppAutomatically; return; } if (canCheckForAppUpdates && _configuration.AutomaticallyCheckForAppUpdates && !_hasAutomaticallyCheckedForAppUpdate) { DebugLogger.Log("Automatically deciding to check for app updates."); _hasAutomaticallyInstalledApp = true; _hasAutomaticallyCheckedForAppUpdate = true; _userDecision = UserDecision.CheckForAppUpdatesAutomatically; return; } var updatesOnly = Environment.GetCommandLineArgs().Any(arg => arg.Equals("--updateOnly", StringComparison.OrdinalIgnoreCase)); if (canStartApp && updatesOnly) { _canStartApp.Value = true; _userDecision = UserDecision.AppUpdatesOnly; return; } else { if (canStartApp && _configuration.AutomaticallyStartApp && !_hasAutomaticallyStartedApp) { DebugLogger.Log("Automatically deciding to start app."); _hasAutomaticallyStartedApp = true; _userDecision = UserDecision.StartAppAutomatically; return; } _canRepairApp.Value = canRepairApp; _canInstallApp.Value = canInstallApp; _canCheckForAppUpdates.Value = canCheckForAppUpdates; _canStartApp.Value = canStartApp; } _userDecisionSetEvent.Reset(); using (cancellationToken.Register(() => _userDecisionSetEvent.Set())) { cancellationToken.ThrowIfCancellationRequested(); _userDecisionSetEvent.WaitOne(); } _canRepairApp.Value = false; _canInstallApp.Value = false; _canCheckForAppUpdates.Value = false; _canStartApp.Value = false; cancellationToken.ThrowIfCancellationRequested(); DebugLogger.Log(string.Format("Waiting for user decision result: {0}.", _userDecision)); } catch (OperationCanceledException) { DebugLogger.Log("Waiting for user decision cancelled."); } catch (ThreadInterruptedException) { DebugLogger.Log("Waiting for user decision interrupted: thread has been interrupted. Rethrowing exception."); throw; } catch (ThreadAbortException) { DebugLogger.Log("Waiting for user decision aborted: thread has been aborted. Rethrowing exception."); throw; } catch (Exception) { DebugLogger.LogWarning("Error while waiting for user decision: an exception has occured. Rethrowing exception."); throw; } }
private void ThreadExecution(CancellationToken cancellationToken) { try { _state.Value = PatcherState.None; DebugLogger.Log("Patcher thread started."); try { ThreadLoadPatcherData(); } catch (NonLauncherExecutionException) { try { LauncherUtilities.ExecuteLauncher(); return; } catch (ApplicationException) { ThreadDisplayError(PatcherErrorMessage.NonLauncherExecution(), cancellationToken); return; } finally { Quit(); } } EnsureSingleInstance(); ThreadLoadPatcherConfiguration(); UnityDispatcher.Invoke(() => _app = new App(_data.Value.AppDataPath, _data.Value.AppSecret, _data.Value.OverrideLatestVersionId, _requestTimeoutCalculator)).WaitOne(); PatcherStatistics.TryDispatchSendEvent(PatcherStatistics.Event.PatcherStarted); while (true) { cancellationToken.ThrowIfCancellationRequested(); ThreadWaitForUserDecision(cancellationToken); cancellationToken.ThrowIfCancellationRequested(); ThreadExecuteUserDecision(cancellationToken); } } catch (OperationCanceledException) { DebugLogger.Log("Patcher thread finished: thread has been cancelled."); } catch (ThreadInterruptedException) { DebugLogger.Log("Patcher thread finished: thread has been interrupted."); } catch (ThreadAbortException) { DebugLogger.Log("Patcher thread finished: thread has been aborted."); } catch (MultipleInstancesException exception) { DebugLogger.LogException(exception); Quit(); } catch (Exception exception) { DebugLogger.LogError("Patcher thread failed: an exception has occured."); DebugLogger.LogException(exception); } finally { _state.Value = PatcherState.None; } }
public static void TryExecute(Action action, IRequestRetryStrategy retryStrategy, PatchKit.Unity.Patcher.Cancellation.CancellationToken cancellationToken) { do { try { action(); return; } catch (IOException e) { retryStrategy.OnRequestFailure(); if (!retryStrategy.ShouldRetry) { DebugLogger.LogError(string.Format("An IO Exception has occured: {0}. rethrowing.", e)); throw; } DebugLogger.LogWarning(string.Format("An IO Exception has occured: {0}. retrying...", e)); Threading.CancelableSleep(retryStrategy.DelayBeforeNextTry, cancellationToken); } } while (retryStrategy.ShouldRetry); }
public static void TryExecute(Action action, PatchKit.Unity.Patcher.Cancellation.CancellationToken cancellationToken) { TryExecute(action, new RetryStrategy(), cancellationToken); }
private void ThreadWaitForUserDecision(CancellationToken cancellationToken) { try { DebugLogger.Log("Waiting for user decision..."); _state.Value = PatcherState.WaitingForUserDecision; bool isInstalled = _app.IsInstalled(); DebugLogger.LogVariable(isInstalled, "isInstalled"); _canRepairApp.Value = false; // not implemented _canInstallApp.Value = !isInstalled; _canCheckForAppUpdates.Value = isInstalled; _canStartApp.Value = isInstalled; if (_canInstallApp.Value && _configuration.AutomaticallyInstallApp && !_hasAutomaticallyInstalledApp) { DebugLogger.Log("Automatically deciding to install app."); _hasAutomaticallyInstalledApp = true; _userDecision = UserDecision.InstallAppAutomatically; return; } if (_canCheckForAppUpdates.Value && _configuration.AutomaticallyCheckForAppUpdates && !_hasAutomaticallyCheckedForAppUpdate) { DebugLogger.Log("Automatically deciding to check for app updates."); _hasAutomaticallyCheckedForAppUpdate = true; _userDecision = UserDecision.CheckForAppUpdatesAutomatically; return; } if (_canStartApp.Value && _configuration.AutomaticallyStartApp && !_hasAutomaticallyStartedApp) { DebugLogger.Log("Automatically deciding to start app."); _hasAutomaticallyStartedApp = true; _userDecision = UserDecision.StartAppAutomatically; return; } _userDecisionSetEvent.Reset(); using (cancellationToken.Register(() => _userDecisionSetEvent.Set())) { cancellationToken.ThrowIfCancellationRequested(); _userDecisionSetEvent.WaitOne(); } cancellationToken.ThrowIfCancellationRequested(); DebugLogger.Log(string.Format("Waiting for user decision result: {0}.", _userDecision)); } catch (OperationCanceledException) { DebugLogger.Log("Waiting for user decision cancelled."); } catch (ThreadInterruptedException) { DebugLogger.Log("Waiting for user decision interrupted: thread has been interrupted. Rethrowing exception."); throw; } catch (ThreadAbortException) { DebugLogger.Log("Waiting for user decision aborted: thread has been aborted. Rethrowing exception."); throw; } catch (Exception) { DebugLogger.LogWarning("Error while waiting for user decision: an exception has occured. Rethrowing exception."); throw; } }
private void PreUpdate(PatchKit.Unity.Patcher.Cancellation.CancellationToken cancellationToken) { var appRepairer = new AppRepairer(Context, _status); appRepairer.Perform(cancellationToken); }
private void PreUpdate(PatchKit.Unity.Patcher.Cancellation.CancellationToken cancellationToken) { DebugLogger.Log("Pre update integrity check"); var commandFactory = new AppUpdaterCommandFactory(); int installedVersionId = Context.App.GetInstalledVersionId(); int latestVersionId = Context.App.GetLatestVersionId(); int lowestVersionWithContentId = Context.App.GetLowestVersionWithContentId(); if (lowestVersionWithContentId > installedVersionId) { DebugLogger.Log( "Repair is impossible because lowest version with content id is " + lowestVersionWithContentId + " and currently installed version id is " + installedVersionId + ". Uninstalling to prepare for content strategy."); IUninstallCommand uninstall = commandFactory.CreateUninstallCommand(Context); uninstall.Prepare(_status); uninstall.Execute(cancellationToken); return; } AppContentSummary installedVersionContentSummary = Context.App.RemoteMetaData.GetContentSummary(installedVersionId); AppContentSummary latestVersionContentSummary = Context.App.RemoteMetaData.GetContentSummary(latestVersionId); bool isNewVersionAvailable = installedVersionId < latestVersionId; long contentSize = isNewVersionAvailable ? latestVersionContentSummary.Size : installedVersionContentSummary.Size; ICheckVersionIntegrityCommand checkIntegrity = commandFactory .CreateCheckVersionIntegrityCommand( versionId: installedVersionId, context: Context, isCheckingHash: false, isCheckingSize: true); checkIntegrity.Prepare(_status); checkIntegrity.Execute(cancellationToken); var missingFiles = checkIntegrity.Results.Files .Where(f => f.Status == FileIntegrityStatus.MissingData); int missingFilesCount = missingFiles.Count(); var invalidSizeFiles = checkIntegrity.Results.Files .Where(f => f.Status == FileIntegrityStatus.InvalidSize); int invalidSizeFilesCount = invalidSizeFiles.Count(); if (missingFilesCount + invalidSizeFilesCount == 0) { DebugLogger.Log("No missing or invalid size files."); return; } double repairCost = CalculateRepairCost(installedVersionContentSummary, missingFiles.Concat(invalidSizeFiles)); if (repairCost < contentSize) { DebugLogger.Log(string.Format("Repair cost {0} is smaller than content cost {1}, repairing...", repairCost, contentSize)); IAppUpdaterStrategy repairStrategy = _strategyResolver.Create(StrategyType.Repair, Context); repairStrategy.Update(cancellationToken); } else { DebugLogger.Log("Content cost is smaller than repair. Uninstalling to prepare for content strategy."); IUninstallCommand uninstall = commandFactory.CreateUninstallCommand(Context); uninstall.Prepare(_status); uninstall.Execute(cancellationToken); } }