1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_INSTALLER_H_
6 #define CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_INSTALLER_H_
7 
8 #include "base/callback_forward.h"
9 #include "base/macros.h"
10 #include "base/scoped_observer.h"
11 #include "chrome/browser/chromeos/crostini/ansible/ansible_management_service.h"
12 #include "chrome/browser/chromeos/crostini/crostini_installer_ui_delegate.h"
13 #include "chrome/browser/chromeos/crostini/crostini_manager.h"
14 #include "chrome/browser/chromeos/crostini/crostini_types.mojom-forward.h"
15 #include "components/keyed_service/core/keyed_service.h"
16 
17 class Profile;
18 
19 namespace base {
20 class RepeatingTimer;
21 }  // namespace base
22 
23 namespace crostini {
24 
25 class CrostiniInstaller : public KeyedService,
26                           public CrostiniManager::RestartObserver,
27                           public CrostiniInstallerUIDelegate,
28                           public AnsibleManagementService::Observer {
29  public:
30   // These values are persisted to logs. Entries should not be renumbered and
31   // numeric values should never be reused.
32   // When you add entries to this enum don't forget to update enums.xml and the
33   // plx scripts in
34   // https://plx.corp.google.com/home2/home/collections/c16e3c1474497b821
35   enum class SetupResult {
36     kNotStarted = 0,
37     // kUserCancelled = 1,
38     kSuccess = 2,
39     kErrorLoadingTermina = 3,
40     // kErrorStartingConcierge = 4,
41     kErrorCreatingDiskImage = 5,
42     kErrorStartingTermina = 6,
43     kErrorStartingContainer = 7,
44     kErrorOffline = 8,
45     kErrorFetchingSshKeys = 9,
46     kErrorMountingContainer = 10,
47     kErrorSettingUpContainer = 11,
48 
49     kUserCancelledStart = 12,
50     kUserCancelledInstallImageLoader = 13,
51     // kUserCancelledStartConcierge = 14,
52     kUserCancelledCreateDiskImage = 15,
53     kUserCancelledStartTerminaVm = 16,
54     kUserCancelledCreateContainer = 17,
55     kUserCancelledStartContainer = 18,
56     kUserCancelledSetupContainer = 19,
57     kUserCancelledFetchSshKeys = 20,
58     kUserCancelledMountContainer = 21,
59 
60     kErrorInsufficientDiskSpace = 22,
61 
62     kErrorConfiguringContainer = 23,
63     kUserCancelledConfiguringContainer = 24,
64 
65     kErrorCreateContainer = 25,
66     kErrorUnknown = 26,
67 
68     kMaxValue = kErrorUnknown,
69     // When adding a new value, check you've followed the steps in the comment
70     // at the top of this enum.
71   };
72 
73   static CrostiniInstaller* GetForProfile(Profile* profile);
74 
75   explicit CrostiniInstaller(Profile* profile);
76   ~CrostiniInstaller() override;
77   void Shutdown() override;
78 
79   void ShowDialog(CrostiniUISurface ui_surface);
80 
81   // CrostiniInstallerUIDelegate:
82   void Install(CrostiniManager::RestartOptions options,
83                ProgressCallback progress_callback,
84                ResultCallback result_callback) override;
85   void Cancel(base::OnceClosure callback) override;
86   void CancelBeforeStart() override;
87 
88   // CrostiniManager::RestartObserver:
89   void OnStageStarted(crostini::mojom::InstallerState stage) override;
90   void OnComponentLoaded(crostini::CrostiniResult result) override;
91   void OnDiskImageCreated(bool success,
92                           vm_tools::concierge::DiskImageStatus status,
93                           int64_t disk_size_available) override;
94   void OnVmStarted(bool success) override;
95   void OnContainerDownloading(int32_t download_percent) override;
96   void OnContainerCreated(crostini::CrostiniResult result) override;
97   void OnContainerSetup(bool success) override;
98   void OnContainerStarted(crostini::CrostiniResult result) override;
99   void OnSshKeysFetched(bool success) override;
100   void OnContainerMounted(bool success) override;
101 
102   // AnsibleManagementService::Observer:
103   void OnAnsibleSoftwareConfigurationStarted() override;
104   void OnAnsibleSoftwareConfigurationFinished(bool success) override;
105 
106   // Return true if internal state allows starting installation.
107   bool CanInstall();
108 
set_require_cleanup_for_testing(bool require_cleanup)109   void set_require_cleanup_for_testing(bool require_cleanup) {
110     require_cleanup_ = require_cleanup;
111   }
set_skip_launching_terminal_for_testing()112   void set_skip_launching_terminal_for_testing() {
113     skip_launching_terminal_for_testing_ = true;
114   }
115 
116  private:
117   enum class State {
118     IDLE,
119     INSTALLING,
120     ERROR,                    // Something unexpected happened.
121     CANCEL_CLEANUP,           // Deleting a partial installation.
122     CANCEL_ABORT_CHECK_DISK,  // Don't proceed after checking disk.
123   };
124 
125   void RunProgressCallback();
126   void UpdateState(State new_state);
127   void UpdateInstallingState(
128       crostini::mojom::InstallerState new_installing_state,
129       bool run_callback = true);
130   void HandleError(crostini::mojom::InstallerError error);
131   void FinishCleanup(crostini::CrostiniResult result);
132   void RecordSetupResult(SetupResult result);
133 
134   void OnCrostiniRestartFinished(crostini::CrostiniResult result);
135   void OnAvailableDiskSpace(int64_t bytes);
136 
137   void OnCrostiniRemovedAfterConfigurationFailed(
138       crostini::CrostiniResult result);
139 
140   Profile* profile_;
141 
142   State state_ = State::IDLE;
143   crostini::mojom::InstallerState installing_state_;
144   base::TimeTicks install_start_time_;
145   base::Time installing_state_start_time_;
146   base::RepeatingTimer state_progress_timer_;
147   bool require_cleanup_;
148   int64_t free_disk_space_;
149   int32_t container_download_percent_;
150   crostini::CrostiniManager::RestartId restart_id_ =
151       crostini::CrostiniManager::kUninitializedRestartId;
152   CrostiniManager::RestartOptions restart_options_;
153 
154   bool skip_launching_terminal_for_testing_ = false;
155 
156   ProgressCallback progress_callback_;
157   ResultCallback result_callback_;
158   base::OnceClosure cancel_callback_;
159 
160   ScopedObserver<AnsibleManagementService, AnsibleManagementService::Observer>
161       ansible_management_service_observer_{this};
162 
163   base::WeakPtrFactory<CrostiniInstaller> weak_ptr_factory_{this};
164 
165   DISALLOW_COPY_AND_ASSIGN(CrostiniInstaller);
166 };
167 
168 }  // namespace crostini
169 
170 #endif  // CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_INSTALLER_H_
171