chromium/chrome/browser/ash/crostini/crostini_installer.h

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_ASH_CROSTINI_CROSTINI_INSTALLER_H_
#define CHROME_BROWSER_ASH_CROSTINI_CROSTINI_INSTALLER_H_

#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/ash/crostini/ansible/ansible_management_service.h"
#include "chrome/browser/ash/crostini/crostini_installer_ui_delegate.h"
#include "chrome/browser/ash/crostini/crostini_manager.h"
#include "chrome/browser/ash/crostini/crostini_types.mojom-forward.h"
#include "components/keyed_service/core/keyed_service.h"

class Profile;

namespace crostini {

class CrostiniInstaller : public KeyedService,
                          public CrostiniManager::RestartObserver,
                          public CrostiniInstallerUIDelegate {
 public:
  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  // When you add entries to this enum don't forget to update enums.xml and the
  // plx scripts in
  // https://plx.corp.google.com/home2/home/collections/c16e3c1474497b821
  enum class SetupResult {
    kNotStarted = 0,
    // kUserCancelled = 1,
    kSuccess = 2,
    kErrorLoadingTermina = 3,
    // kErrorStartingConcierge = 4,
    kErrorCreatingDiskImage = 5,
    kErrorStartingTermina = 6,
    kErrorStartingContainer = 7,
    kErrorOffline = 8,
    // kErrorFetchingSshKeys = 9,
    // kErrorMountingContainer = 10,
    kErrorSettingUpContainer = 11,

    kUserCancelledStart = 12,
    kUserCancelledInstallImageLoader = 13,
    // kUserCancelledStartConcierge = 14,
    kUserCancelledCreateDiskImage = 15,
    kUserCancelledStartTerminaVm = 16,
    kUserCancelledCreateContainer = 17,
    kUserCancelledStartContainer = 18,
    kUserCancelledSetupContainer = 19,
    // kUserCancelledFetchSshKeys = 20,
    // kUserCancelledMountContainer = 21,

    kErrorInsufficientDiskSpace = 22,

    kErrorConfiguringContainer = 23,
    kUserCancelledConfiguringContainer = 24,

    kErrorCreateContainer = 25,
    kErrorUnknown = 26,

    kUserCancelledStartLxd = 27,
    kErrorStartingLxd = 28,

    kNeedUpdate = 29,

    kMaxValue = kNeedUpdate,
    // When adding a new value, check you've followed the steps in the comment
    // at the top of this enum.
  };

  static CrostiniInstaller* GetForProfile(Profile* profile);

  explicit CrostiniInstaller(Profile* profile);

  CrostiniInstaller(const CrostiniInstaller&) = delete;
  CrostiniInstaller& operator=(const CrostiniInstaller&) = delete;

  ~CrostiniInstaller() override;
  void Shutdown() override;

  void ShowDialog(CrostiniUISurface ui_surface);

  // CrostiniInstallerUIDelegate:
  void Install(CrostiniManager::RestartOptions options,
               ProgressCallback progress_callback,
               ResultCallback result_callback) override;
  void Cancel(base::OnceClosure callback) override;
  void CancelBeforeStart() override;

  // CrostiniManager::RestartObserver:
  void OnStageStarted(crostini::mojom::InstallerState stage) override;
  void OnDiskImageCreated(bool success,
                          CrostiniResult result,
                          int64_t disk_size_available) override;
  void OnContainerDownloading(int32_t download_percent) override;

  // Return true if internal state allows starting installation.
  bool CanInstall();

  void set_require_cleanup_for_testing(bool require_cleanup) {
    require_cleanup_ = require_cleanup;
  }
  void set_skip_launching_terminal_for_testing() {
    skip_launching_terminal_for_testing_ = true;
  }

  static void EnsureFactoryBuilt();

 private:
  enum class State {
    IDLE,
    INSTALLING,
    ERROR,                    // Something unexpected happened.
    CANCEL_CLEANUP,           // Deleting a partial installation.
    CANCEL_ABORT_CHECK_DISK,  // Don't proceed after checking disk.
  };

  void RunProgressCallback();
  void UpdateState(State new_state);
  void UpdateInstallingState(
      crostini::mojom::InstallerState new_installing_state,
      bool run_callback = true);
  void HandleError(crostini::mojom::InstallerError error);
  void FinishCleanup(crostini::CrostiniResult result);
  void RecordSetupResult(SetupResult result);

  void OnCrostiniRestartFinished(crostini::CrostiniResult result);
  void OnAvailableDiskSpace(std::optional<int64_t> bytes);

  void OnCrostiniRemovedAfterConfigurationFailed(
      crostini::CrostiniResult result);

  raw_ptr<Profile> profile_;

  State state_ = State::IDLE;
  crostini::mojom::InstallerState installing_state_;
  base::TimeTicks install_start_time_;
  base::Time installing_state_start_time_;
  base::RepeatingTimer state_progress_timer_;
  bool require_cleanup_;
  int64_t free_disk_space_;
  int32_t container_download_percent_;
  crostini::CrostiniManager::RestartId restart_id_ =
      crostini::CrostiniManager::kUninitializedRestartId;
  CrostiniManager::RestartOptions restart_options_;

  bool skip_launching_terminal_for_testing_ = false;

  ProgressCallback progress_callback_;
  ResultCallback result_callback_;
  base::OnceClosure cancel_callback_;

  base::WeakPtrFactory<CrostiniInstaller> weak_ptr_factory_{this};
};

}  // namespace crostini

#endif  // CHROME_BROWSER_ASH_CROSTINI_CROSTINI_INSTALLER_H_