// Copyright 2018 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_MANAGER_H_
#define CHROME_BROWSER_ASH_CROSTINI_CROSTINI_MANAGER_H_
#include <map>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "base/callback_list.h"
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/scoped_observation.h"
#include "base/scoped_observation_traits.h"
#include "base/time/time.h"
#include "base/unguessable_token.h"
#include "chrome/browser/ash/crostini/crostini_low_disk_notification.h"
#include "chrome/browser/ash/crostini/crostini_simple_types.h"
#include "chrome/browser/ash/crostini/crostini_types.mojom-forward.h"
#include "chrome/browser/ash/crostini/crostini_util.h"
#include "chrome/browser/ash/crostini/termina_installer.h"
#include "chrome/browser/ash/guest_os/guest_id.h"
#include "chrome/browser/ash/guest_os/guest_os_launcher.h"
#include "chrome/browser/ash/guest_os/guest_os_remover.h"
#include "chrome/browser/ash/guest_os/guest_os_session_tracker.h"
#include "chrome/browser/ash/guest_os/public/guest_os_mount_provider_registry.h"
#include "chrome/browser/ash/guest_os/public/guest_os_terminal_provider_registry.h"
#include "chrome/browser/ash/guest_os/vm_shutdown_observer.h"
#include "chrome/browser/ash/guest_os/vm_starting_observer.h"
#include "chromeos/ash/components/dbus/anomaly_detector/anomaly_detector.pb.h"
#include "chromeos/ash/components/dbus/anomaly_detector/anomaly_detector_client.h"
#include "chromeos/ash/components/dbus/cicerone/cicerone_client.h"
#include "chromeos/ash/components/dbus/cicerone/cicerone_service.pb.h"
#include "chromeos/ash/components/dbus/concierge/concierge_client.h"
#include "chromeos/ash/components/dbus/vm_concierge/concierge_service.pb.h"
#include "chromeos/ash/components/network/network_state_handler_observer.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "components/keyed_service/core/keyed_service.h"
class Profile;
namespace ash {
class NetworkState;
class NetworkStateHandler;
} // namespace ash
namespace guest_os {
class GuestOsStabilityMonitor;
} // namespace guest_os
namespace crostini {
extern const char kCrostiniStabilityHistogram[];
class CrostiniUpgradeAvailableNotification;
class CrostiniSshfs;
class LinuxPackageOperationProgressObserver {
public:
// A successfully started package install will continually fire progress
// events until it returns a status of SUCCEEDED or FAILED. The
// |progress_percent| field is given as a percentage of the given step,
// DOWNLOADING or INSTALLING. If |status| is FAILED, the |error_message|
// will contain output of the failing installation command.
virtual void OnInstallLinuxPackageProgress(
const guest_os::GuestId& container_id,
InstallLinuxPackageProgressStatus status,
int progress_percent,
const std::string& error_message) = 0;
// A successfully started package uninstall will continually fire progress
// events until it returns a status of SUCCEEDED or FAILED.
virtual void OnUninstallPackageProgress(const guest_os::GuestId& container_id,
UninstallPackageProgressStatus status,
int progress_percent) = 0;
};
class PendingAppListUpdatesObserver : public base::CheckedObserver {
public:
// Called whenever the kPendingAppListUpdatesMethod signal is sent.
virtual void OnPendingAppListUpdates(const guest_os::GuestId& container_id,
int count) = 0;
};
class ExportContainerProgressObserver {
public:
// A successfully started container export will continually fire progress
// events until the original callback from ExportLxdContainer is invoked with
// a status of SUCCESS or CONTAINER_EXPORT_FAILED.
virtual void OnExportContainerProgress(const guest_os::GuestId& container_id,
const StreamingExportStatus&) = 0;
};
class ImportContainerProgressObserver {
public:
// A successfully started container import will continually fire progress
// events until the original callback from ImportLxdContainer is invoked with
// a status of SUCCESS or CONTAINER_IMPORT_FAILED[_*].
virtual void OnImportContainerProgress(
const guest_os::GuestId& container_id,
ImportContainerProgressStatus status,
int progress_percent,
uint64_t progress_speed,
const std::string& architecture_device,
const std::string& architecture_container,
uint64_t available_space,
uint64_t minimum_required_space) = 0;
};
class UpgradeContainerProgressObserver {
public:
virtual void OnUpgradeContainerProgress(
const guest_os::GuestId& container_id,
UpgradeContainerProgressStatus status,
const std::vector<std::string>& messages) = 0;
};
class CrostiniDialogStatusObserver : public base::CheckedObserver {
public:
// Called when a Crostini dialog (installer, upgrader, etc.) opens or
// closes.
virtual void OnCrostiniDialogStatusChanged(DialogType dialog_type,
bool open) = 0;
};
class CrostiniContainerPropertiesObserver : public base::CheckedObserver {
public:
// Called when a container's OS release version changes.
virtual void OnContainerOsReleaseChanged(
const guest_os::GuestId& container_id,
bool can_upgrade) = 0;
};
class ContainerShutdownObserver : public base::CheckedObserver {
public:
// Called when the container has shutdown.
virtual void OnContainerShutdown(const guest_os::GuestId& container_id) = 0;
};
// CrostiniManager is a singleton which is used to check arguments for
// ConciergeClient and CiceroneClient. ConciergeClient is dedicated to
// communication with the Concierge service, CiceroneClient is dedicated to
// communication with the Cicerone service and both should remain as thin as
// possible. The existence of Cicerone is abstracted behind this class and
// only the Concierge name is exposed outside of here.
class CrostiniManager : public KeyedService,
public ash::AnomalyDetectorClient::Observer,
public ash::ConciergeClient::VmObserver,
public ash::CiceroneClient::Observer,
public ash::NetworkStateHandlerObserver,
public chromeos::PowerManagerClient::Observer {
public:
using CrostiniResultCallback =
base::OnceCallback<void(CrostiniResult result)>;
using ExportLxdContainerResultCallback =
base::OnceCallback<void(CrostiniResult result,
uint64_t container_size,
uint64_t compressed_size)>;
// Callback indicating success or failure
using BoolCallback = base::OnceCallback<void(bool success)>;
using RestartId = int;
static const RestartId kUninitializedRestartId = -1;
// Observer class for the Crostini restart flow.
class RestartObserver {
public:
virtual ~RestartObserver() = default;
virtual void OnStageStarted(mojom::InstallerState stage) {}
virtual void OnDiskImageCreated(bool success,
CrostiniResult result,
int64_t disk_size_bytes) {}
virtual void OnContainerDownloading(int32_t download_percent) {}
};
struct RestartOptions {
RestartSource restart_source = RestartSource::kOther;
bool start_vm_only = false;
bool stop_after_lxd_available = false;
// Paths to share with VM on startup.
std::vector<base::FilePath> share_paths;
// These five options only affect new containers.
std::optional<std::string> container_username;
std::optional<int64_t> disk_size_bytes;
std::optional<std::string> image_server_url;
std::optional<std::string> image_alias;
std::optional<base::FilePath> ansible_playbook;
RestartOptions();
~RestartOptions();
// Add copy version if necessary.
RestartOptions(RestartOptions&&);
RestartOptions& operator=(RestartOptions&&);
};
static CrostiniManager* GetForProfile(Profile* profile);
explicit CrostiniManager(Profile* profile);
CrostiniManager(const CrostiniManager&) = delete;
CrostiniManager& operator=(const CrostiniManager&) = delete;
~CrostiniManager() override;
base::WeakPtr<CrostiniManager> GetWeakPtr();
// Returns true if the /dev/kvm directory is present.
static bool IsDevKvmPresent();
// Returns true if concierge allows termina VM to be launched.
static bool IsVmLaunchAllowed();
// Upgrades cros-termina component if the current version is not compatible.
// This is a no-op if `ash::features::kCrostiniUseDlc` is enabled.
void MaybeUpdateCrostini();
// Installs termina using the DLC service.
void InstallTermina(CrostiniResultCallback callback);
// Try to cancel a previous InstallTermina call. This is done on a best-effort
// basis. The callback passed to InstallTermina is still run upon completion.
void CancelInstallTermina();
// Unloads and removes termina.
void UninstallTermina(BoolCallback callback);
// Checks the arguments for creating a new Termina VM disk image. Creates a
// disk image for a Termina VM via ConciergeClient::CreateDiskImage.
// |callback| is called if the arguments are bad, or after the method call
// finishes.
using CreateDiskImageCallback =
base::OnceCallback<void(CrostiniResult result,
const base::FilePath& disk_path)>;
void CreateDiskImage(
// The path to the disk image, including the name of
// the image itself. The image name should match the
// name of the VM that it will be used for.
const std::string& vm_name,
// The storage location for the disk image
vm_tools::concierge::StorageLocation storage_location,
// The logical size of the disk image, in bytes
int64_t disk_size_bytes,
CreateDiskImageCallback callback);
// Checks the arguments for starting a Termina VM. Starts a Termina VM via
// ConciergeClient::StartTerminaVm. |callback| is called if the arguments
// are bad, or after the method call finishes.
void StartTerminaVm(
// The human-readable name to be assigned to this VM.
std::string name,
// Path to the disk image on the host.
const base::FilePath& disk_path,
// The number of logical CPU cores that are currently disabled.
size_t num_cores_disabled,
// A callback to invoke with the result of the launch request.
BoolCallback callback);
// Checks the arguments for stopping a Termina VM. Stops the Termina VM via
// ConciergeClient::StopVm. |callback| is called if the arguments are bad,
// or after the method call finishes.
void StopVm(std::string name, CrostiniResultCallback callback);
// Calls |StopVm| for each member of |running_vms_| not already in state
// |STOPPING|.
void StopRunningVms(CrostiniResultCallback callback);
// Asynchronously retrieve the Termina VM kernel version using concierge's
// GetVmEnterpriseReportingInfo method and store it in prefs.
void UpdateTerminaVmKernelVersion();
// Wrapper for CiceroneClient::StartLxd with some extra parameter validation.
// |callback| is called immediately if the arguments are bad, or after LXD has
// been started.
void StartLxd(std::string vm_name, CrostiniResultCallback callback);
// Checks the arguments for creating an Lxd container via
// CiceroneClient::CreateLxdContainer. |callback| is called immediately if the
// arguments are bad, or once the container has been created.
void CreateLxdContainer(guest_os::GuestId container_id,
std::optional<std::string> opt_image_server_url,
std::optional<std::string> opt_image_alias,
CrostiniResultCallback callback);
// Checks the arguments for deleting an Lxd container via
// CiceroneClient::DeleteLxdContainer. |callback| is called immediately if the
// arguments are bad, or once the container has been deleted.
void DeleteLxdContainer(guest_os::GuestId container_id,
BoolCallback callback);
// Checks the arguments for starting an Lxd container via
// CiceroneClient::StartLxdContainer. |callback| is called immediately if the
// arguments are bad, or once the container has been created.
void StartLxdContainer(guest_os::GuestId container_id,
CrostiniResultCallback callback);
// Checks the arguments for stopping an Lxd container via
// CiceroneClient::StopLxdContainer. |callback| is called immediately if the
// arguments are bad, or once the container has been stopped.
void StopLxdContainer(guest_os::GuestId container_id,
CrostiniResultCallback callback);
// Checks the arguments for setting up an Lxd container user via
// CiceroneClient::SetUpLxdContainerUser. |callback| is called immediately if
// the arguments are bad, or once garcon has been started.
void SetUpLxdContainerUser(guest_os::GuestId container_id,
std::string container_username,
BoolCallback callback);
// Checks the arguments for exporting an Lxd container via
// CiceroneClient::ExportLxdContainer. |callback| is called immediately if the
// arguments are bad, or after the method call finishes.
void ExportLxdContainer(guest_os::GuestId container_id,
base::FilePath export_path,
ExportLxdContainerResultCallback callback);
// Checks the arguments for importing an Lxd container via
// CiceroneClient::ImportLxdContainer. |callback| is called immediately if the
// arguments are bad, or after the method call finishes.
void ImportLxdContainer(guest_os::GuestId container_id,
base::FilePath import_path,
CrostiniResultCallback callback);
// Checks the arguments for cancelling a Lxd container export via
// CiceroneClient::CancelExportLxdContainer .
void CancelExportLxdContainer(guest_os::GuestId key);
// Checks the arguments for cancelling a Lxd container import via
// CiceroneClient::CancelImportLxdContainer.
void CancelImportLxdContainer(guest_os::GuestId key);
// Checks the arguments for upgrading an existing container via
// CiceroneClient::UpgradeContainer. An UpgradeProgressObserver should be used
// to monitor further results.
void UpgradeContainer(const guest_os::GuestId& key,
ContainerVersion target_version,
CrostiniResultCallback callback);
// Checks the arguments for canceling the upgrade of an existing container via
// CiceroneClient::CancelUpgradeContainer.
void CancelUpgradeContainer(const guest_os::GuestId& key,
CrostiniResultCallback callback);
// Asynchronously gets app icons as specified by their desktop file ids.
// |callback| is called after the method call finishes.
using GetContainerAppIconsCallback =
base::OnceCallback<void(bool success, const std::vector<Icon>& icons)>;
void GetContainerAppIcons(const guest_os::GuestId& container_id,
std::vector<std::string> desktop_file_ids,
int icon_size,
int scale,
GetContainerAppIconsCallback callback);
// Asynchronously retrieve information about a Linux Package (.deb) inside the
// container.
using GetLinuxPackageInfoCallback =
base::OnceCallback<void(const LinuxPackageInfo&)>;
void GetLinuxPackageInfo(const guest_os::GuestId& container_id,
std::string package_path,
GetLinuxPackageInfoCallback callback);
// Begin installation of a Linux Package inside the container. If the
// installation is successfully started, further updates will be sent to
// added LinuxPackageOperationProgressObservers.
using InstallLinuxPackageCallback = CrostiniResultCallback;
void InstallLinuxPackage(const guest_os::GuestId& container_id,
std::string package_path,
InstallLinuxPackageCallback callback);
// Begin installation of a Linux Package inside the container. If the
// installation is successfully started, further updates will be sent to
// added LinuxPackageOperationProgressObservers. Uses a package_id, given
// by "package_name;version;arch;data", to identify the package to install
// from the APT repository.
void InstallLinuxPackageFromApt(const guest_os::GuestId& container_id,
std::string package_id,
InstallLinuxPackageCallback callback);
// Begin uninstallation of a Linux Package inside the container. The package
// is identified by its associated .desktop file's ID; we don't use package_id
// to avoid problems with stale package_ids (such as after upgrades). If the
// uninstallation is successfully started, further updates will be sent to
// added LinuxPackageOperationProgressObservers.
void UninstallPackageOwningFile(const guest_os::GuestId& container_id,
std::string desktop_file_id,
CrostiniResultCallback callback);
// Runs all the steps required to restart the given crostini vm and container.
// The optional |observer| tracks progress. If provided, it must be alive
// until the restart completes (i.e. when |callback| is called) or the request
// is cancelled via |CancelRestartCrostini|.
RestartId RestartCrostini(guest_os::GuestId container_id,
CrostiniResultCallback callback,
RestartObserver* observer = nullptr);
RestartId RestartCrostiniWithOptions(guest_os::GuestId container_id,
RestartOptions options,
CrostiniResultCallback callback,
RestartObserver* observer = nullptr);
// CreateOption operations.
// Registers the CreateOptions to create a container with specified
// RestartOptions. For containers that existed before this feature, this will
// be generic restart options, for newly created containers, this will store
// the initial starting information. Returns false if there is already an
// CreateOption registered.
bool RegisterCreateOptions(const guest_os::GuestId& container_id,
const RestartOptions& options);
// Fetches the CreateOptions as RestartOptions. Returns True if this
// configuration has been started with before.
bool FetchCreateOptions(const guest_os::GuestId& container_id,
RestartOptions* restart_options);
// Returns true if the container is currently pending creation.
bool IsPendingCreation(const guest_os::GuestId& container_id);
// Sets an CreateOptions as booted, so it becomes a historical record and has
// no effect on future starts.
void SetCreateOptionsUsed(const guest_os::GuestId& container_id);
// Cancel a restart request. The associated result callback will be fired
// immediately and the observer will be removed. If there were multiple
// restart requests for the same container id, the restart may actually keep
// going.
void CancelRestartCrostini(RestartId restart_id);
// Returns true if the Restart corresponding to |restart_id| is not yet
// complete.
bool IsRestartPending(RestartId restart_id);
// Returns whether there is an active restarter for a given GuestId. Even
// after cancelling all requests or aborting a restarter, this will continue
// to return true until the current operation is finished and the restarter
// is destroyed.
bool HasRestarterForTesting(const guest_os::GuestId& guest_id);
// Adds a callback to receive notification of container shutdown.
void AddShutdownContainerCallback(guest_os::GuestId container_id,
base::OnceClosure shutdown_callback);
// Adds a callback to receive uninstall notification.
using RemoveCrostiniCallback = CrostiniResultCallback;
void AddRemoveCrostiniCallback(RemoveCrostiniCallback remove_callback);
// Add/remove observers for package install and uninstall progress.
void AddLinuxPackageOperationProgressObserver(
LinuxPackageOperationProgressObserver* observer);
void RemoveLinuxPackageOperationProgressObserver(
LinuxPackageOperationProgressObserver* observer);
// Add/remove observers for pending app list updates.
void AddPendingAppListUpdatesObserver(
PendingAppListUpdatesObserver* observer);
void RemovePendingAppListUpdatesObserver(
PendingAppListUpdatesObserver* observer);
// Add/remove observers for container export/import.
void AddExportContainerProgressObserver(
ExportContainerProgressObserver* observer);
void RemoveExportContainerProgressObserver(
ExportContainerProgressObserver* observer);
void AddImportContainerProgressObserver(
ImportContainerProgressObserver* observer);
void RemoveImportContainerProgressObserver(
ImportContainerProgressObserver* observer);
// Add/remove observers for container upgrade
void AddUpgradeContainerProgressObserver(
UpgradeContainerProgressObserver* observer);
void RemoveUpgradeContainerProgressObserver(
UpgradeContainerProgressObserver* observer);
// Add/remove vm shutdown observers.
void AddVmShutdownObserver(ash::VmShutdownObserver* observer);
void RemoveVmShutdownObserver(ash::VmShutdownObserver* observer);
// Add/remove vm starting observers.
void AddVmStartingObserver(ash::VmStartingObserver* observer);
void RemoveVmStartingObserver(ash::VmStartingObserver* observer);
// AnomalyDetectorClient::Observer:
void OnGuestFileCorruption(
const anomaly_detector::GuestFileCorruptionSignal& signal) override;
// ConciergeClient::VmObserver:
void OnVmStarted(const vm_tools::concierge::VmStartedSignal& signal) override;
void OnVmStopped(const vm_tools::concierge::VmStoppedSignal& signal) override;
void OnVmStopping(
const vm_tools::concierge::VmStoppingSignal& signal) override;
// CiceroneClient::Observer:
void OnContainerStarted(
const vm_tools::cicerone::ContainerStartedSignal& signal) override;
void OnContainerShutdown(
const vm_tools::cicerone::ContainerShutdownSignal& signal) override;
void OnInstallLinuxPackageProgress(
const vm_tools::cicerone::InstallLinuxPackageProgressSignal& signal)
override;
void OnUninstallPackageProgress(
const vm_tools::cicerone::UninstallPackageProgressSignal& signal)
override;
void OnLxdContainerCreated(
const vm_tools::cicerone::LxdContainerCreatedSignal& signal) override;
void OnLxdContainerDeleted(
const vm_tools::cicerone::LxdContainerDeletedSignal& signal) override;
void OnLxdContainerDownloading(
const vm_tools::cicerone::LxdContainerDownloadingSignal& signal) override;
void OnTremplinStarted(
const vm_tools::cicerone::TremplinStartedSignal& signal) override;
void OnLxdContainerStarting(
const vm_tools::cicerone::LxdContainerStartingSignal& signal) override;
void OnExportLxdContainerProgress(
const vm_tools::cicerone::ExportLxdContainerProgressSignal& signal)
override;
void OnImportLxdContainerProgress(
const vm_tools::cicerone::ImportLxdContainerProgressSignal& signal)
override;
void OnPendingAppListUpdates(
const vm_tools::cicerone::PendingAppListUpdatesSignal& signal) override;
void OnApplyAnsiblePlaybookProgress(
const vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal& signal)
override;
void OnUpgradeContainerProgress(
const vm_tools::cicerone::UpgradeContainerProgressSignal& signal)
override;
void OnStartLxdProgress(
const vm_tools::cicerone::StartLxdProgressSignal& signal) override;
// ash::NetworkStateHandlerObserver overrides:
void ActiveNetworksChanged(
const std::vector<const ash::NetworkState*>& active_networks) override;
void OnShuttingDown() override;
// chromeos::PowerManagerClient::Observer overrides:
void SuspendImminent(power_manager::SuspendImminent::Reason reason) override;
void SuspendDone(base::TimeDelta sleep_duration) override;
// Callback for |RemoveSshfsCrostiniVolume| called from |SuspendImminent| when
// the device is allowed to suspend. Removes metadata associated with the
// crostini sshfs mount and unblocks a pending suspend.
void OnRemoveSshfsCrostiniVolume(
base::UnguessableToken power_manager_suspend_token,
bool result);
void RemoveCrostini(std::string vm_name, RemoveCrostiniCallback callback);
void UpdateVmState(std::string vm_name, VmState vm_state);
bool IsVmRunning(std::string vm_name);
// Returns std::nullopt if VM is not running.
std::optional<VmInfo> GetVmInfo(std::string vm_name);
void AddRunningVmForTesting(std::string vm_name, uint32_t cid = 0);
void AddStoppingVmForTesting(std::string vm_name);
void SetContainerOsRelease(const guest_os::GuestId& container_id,
const vm_tools::cicerone::OsRelease& os_release);
const vm_tools::cicerone::OsRelease* GetContainerOsRelease(
const guest_os::GuestId& container_id) const;
void AddRunningContainerForTesting(std::string vm_name,
ContainerInfo info,
bool notify = false);
// If the Crostini reporting policy is set, save the last app launch
// time window and the Termina version in prefs for asynchronous reporting.
void UpdateLaunchMetricsForEnterpriseReporting();
// Clear the lists of running VMs and containers.
// Can be called for testing to skip restart.
void set_skip_restart_for_testing() { skip_restart_for_testing_ = true; }
bool skip_restart_for_testing() { return skip_restart_for_testing_; }
void SetCrostiniDialogStatus(DialogType dialog_type, bool open);
// Returns true if the dialog is open.
bool GetCrostiniDialogStatus(DialogType dialog_type) const;
void AddCrostiniDialogStatusObserver(CrostiniDialogStatusObserver* observer);
void RemoveCrostiniDialogStatusObserver(
CrostiniDialogStatusObserver* observer);
void AddCrostiniContainerPropertiesObserver(
CrostiniContainerPropertiesObserver* observer);
void RemoveCrostiniContainerPropertiesObserver(
CrostiniContainerPropertiesObserver* observer);
void AddContainerShutdownObserver(ContainerShutdownObserver* observer);
void RemoveContainerShutdownObserver(ContainerShutdownObserver* observer);
bool IsContainerUpgradeable(const guest_os::GuestId& container_id) const;
bool ShouldPromptContainerUpgrade(
const guest_os::GuestId& container_id) const;
void UpgradePromptShown(const guest_os::GuestId& container_id);
bool IsUncleanStartup() const;
void SetUncleanStartupForTesting(bool is_unclean_startup);
void RemoveUncleanSshfsMounts();
void DeallocateForwardedPortsCallback(const guest_os::GuestId& container_id);
void CallRestarterStartLxdContainerFinishedForTesting(
CrostiniManager::RestartId id,
CrostiniResult result);
void SetInstallTerminaNeverCompletesForTesting(bool never_completes) {
install_termina_never_completes_for_testing_ = never_completes;
}
// Mounts the user's Crostini home directory so it's accessible from the host.
// Must be called from the UI thread, no-op if the home directory is already
// mounted. If this is something running in the background set background to
// true, if failures are user-visible set it to false. If you're setting
// base::DoNothing as the callback then background should be true.
void MountCrostiniFiles(guest_os::GuestId container_id,
CrostiniResultCallback callback,
bool background);
void GetInstallLocation(base::OnceCallback<void(base::FilePath)> callback);
private:
class CrostiniRestarter;
void RemoveDBusObservers();
// Callback for ConciergeClient::CreateDiskImage. Called after the Concierge
// service method finishes.
void OnCreateDiskImage(
CreateDiskImageCallback callback,
std::optional<vm_tools::concierge::CreateDiskImageResponse> response);
// Callback for ConciergeClient::StartVm. Called after the Concierge
// service method finishes. Updates running containers list then calls the
// |callback| if the container has already been started, otherwise passes the
// callback to OnStartTremplin.
void OnStartTerminaVm(
std::string vm_name,
BoolCallback callback,
std::optional<vm_tools::concierge::StartVmResponse> response);
// Callback for ConciergeClient::TremplinStartedSignal. Called after the
// Tremplin service starts. Updates running containers list and then calls the
// |callback| with true, indicating success.
void OnStartTremplin(std::string vm_name,
uint32_t seneschal_server_handle,
BoolCallback callback);
// Callback for ConciergeClient::StopVm. Called after the Concierge
// service method finishes.
void OnStopVm(std::string vm_name,
CrostiniResultCallback callback,
std::optional<vm_tools::concierge::StopVmResponse> response);
// Callback for ConciergeClient::GetVmEnterpriseReportingInfo.
// Currently used to report the Termina kernel version for enterprise
// reporting.
void OnGetTerminaVmKernelVersion(
std::optional<vm_tools::concierge::GetVmEnterpriseReportingInfoResponse>
response);
// Callback for CiceroneClient::StartLxd. May indicate that LXD is still being
// started in which case we will wait for OnStartLxdProgress events.
void OnStartLxd(std::string vm_name,
CrostiniResultCallback callback,
std::optional<vm_tools::cicerone::StartLxdResponse> response);
// Callback for CiceroneClient::CreateLxdContainer. May indicate the container
// is still being created, in which case we will wait for an
// OnLxdContainerCreated event.
void OnCreateLxdContainer(
const guest_os::GuestId& container_id,
CrostiniResultCallback callback,
std::optional<vm_tools::cicerone::CreateLxdContainerResponse> response);
// Callback for CiceroneClient::DeleteLxdContainer.
void OnDeleteLxdContainer(
const guest_os::GuestId& container_id,
BoolCallback callback,
std::optional<vm_tools::cicerone::DeleteLxdContainerResponse> response);
// Callback for CiceroneClient::StartLxdContainer.
void OnStartLxdContainer(
const guest_os::GuestId& container_id,
CrostiniResultCallback callback,
std::optional<vm_tools::cicerone::StartLxdContainerResponse> response);
// Callback for CiceroneClient::StopLxdContainer.
void OnStopLxdContainer(
const guest_os::GuestId& container_id,
CrostiniResultCallback callback,
std::optional<vm_tools::cicerone::StopLxdContainerResponse> response);
// Callback for CiceroneClient::SetUpLxdContainerUser.
void OnSetUpLxdContainerUser(
const guest_os::GuestId& container_id,
BoolCallback callback,
std::optional<vm_tools::cicerone::SetUpLxdContainerUserResponse>
response);
// Callback for CiceroneClient::ExportLxdContainer.
void OnExportLxdContainer(
const guest_os::GuestId& container_id,
std::optional<vm_tools::cicerone::ExportLxdContainerResponse> response);
// Callback for CiceroneClient::ImportLxdContainer.
void OnImportLxdContainer(
const guest_os::GuestId& container_id,
std::optional<vm_tools::cicerone::ImportLxdContainerResponse> response);
// Callback for CiceroneClient::CancelExportLxdContainer.
void OnCancelExportLxdContainer(
const guest_os::GuestId& key,
std::optional<vm_tools::cicerone::CancelExportLxdContainerResponse>
response);
// Callback for CiceroneClient::CancelImportLxdContainer.
void OnCancelImportLxdContainer(
const guest_os::GuestId& key,
std::optional<vm_tools::cicerone::CancelImportLxdContainerResponse>
response);
// Callback for CiceroneClient::UpgradeContainer.
void OnUpgradeContainer(
CrostiniResultCallback callback,
std::optional<vm_tools::cicerone::UpgradeContainerResponse> response);
// Callback for CiceroneClient::CancelUpgradeContainer.
void OnCancelUpgradeContainer(
CrostiniResultCallback callback,
std::optional<vm_tools::cicerone::CancelUpgradeContainerResponse>
response);
// Callback for CrostiniManager::LaunchContainerApplication.
void OnLaunchContainerApplication(
guest_os::launcher::SuccessCallback callback,
std::optional<vm_tools::cicerone::LaunchContainerApplicationResponse>
response);
// Callback for CrostiniManager::GetContainerAppIcons. Called after the
// Concierge service finishes.
void OnGetContainerAppIcons(
GetContainerAppIconsCallback callback,
std::optional<vm_tools::cicerone::ContainerAppIconResponse> response);
// Callback for CrostiniManager::GetLinuxPackageInfo.
void OnGetLinuxPackageInfo(
GetLinuxPackageInfoCallback callback,
std::optional<vm_tools::cicerone::LinuxPackageInfoResponse> response);
// Callback for CrostiniManager::InstallLinuxPackage.
void OnInstallLinuxPackage(
InstallLinuxPackageCallback callback,
std::optional<vm_tools::cicerone::InstallLinuxPackageResponse> response);
// Callback for CrostiniManager::UninstallPackageOwningFile.
void OnUninstallPackageOwningFile(
CrostiniResultCallback callback,
std::optional<vm_tools::cicerone::UninstallPackageOwningFileResponse>
response);
// Helper for CrostiniManager::MaybeUpdateCrostini. Makes blocking calls to
// check for /dev/kvm.
static void CheckPaths();
// Helper for CrostiniManager::MaybeUpdateCrostini. Checks that concierge is
// available.
void CheckConciergeAvailable();
// Helper for CrostiniManager::MaybeUpdateCrostini. Checks that concierge will
// allow the termina VM to be launched.
void CheckVmLaunchAllowed(bool service_is_available);
void OnCheckVmLaunchAllowed(
std::optional<vm_tools::concierge::GetVmLaunchAllowedResponse> response);
// Helper for CrostiniManager::MaybeUpdateCrostini. Separated because the
// checking component registration code may block.
void MaybeUpdateCrostiniAfterChecks();
// Called by CrostiniRestarter once it's done with a specific restart request.
void RemoveRestartId(RestartId restart_id);
// Called by CrostiniRestarter once it's finished. |closure| encapsulates any
// outstanding callbacks passed to RestartCrostini*().
void RestartCompleted(CrostiniRestarter* restarter,
base::OnceClosure closure);
// Callback for CrostiniManager::RemoveCrostini.
void OnRemoveCrostini(guest_os::GuestOsRemover::Result result);
void OnRemoveTermina(bool success);
void FinishUninstall(CrostiniResult result);
void OnVmStoppedCleanup(const std::string& vm_name);
// Configure the container so that it can sideload apps into Arc++.
void ConfigureForArcSideload();
// Tries to query Concierge for the type of disk the named VM has then emits a
// metric logging the type. Mostly happens async and best-effort.
void EmitVmDiskTypeMetric(const std::string vm_name);
// Runs things that should happened whenever a container shutdowns e.g.
// triggering observers.
void HandleContainerShutdown(const guest_os::GuestId& container_id);
// Registers a container with the GuestOsService's terminal provider registry.
void RegisterContainerTerminal(const guest_os::GuestId& container_id);
// Registers a container with GuestOsService's registries. No-op if it's
// already registered.
void RegisterContainer(const guest_os::GuestId& container_id);
// Unregisters a container from GuestOsService's registries. No-op if it's
// not registered.
void UnregisterContainer(const guest_os::GuestId& container_id);
// Unregisters all container from GuestOsService's registries.
void UnregisterAllContainers();
// Best-effort attempt to premount the user's files.
void MountCrostiniFilesBackground(guest_os::GuestInfo info);
bool ShouldWarnAboutExpiredVersion(const guest_os::GuestId& container_id);
raw_ptr<Profile> profile_;
std::string owner_id_;
bool skip_restart_for_testing_ = false;
static bool is_dev_kvm_present_;
static bool is_vm_launch_allowed_;
// |is_unclean_startup_| is true when we detect Concierge still running at
// session startup time, and the last session ended in a crash.
bool is_unclean_startup_ = false;
// Callbacks that are waiting on a signal
std::multimap<guest_os::GuestId, CrostiniResultCallback>
start_container_callbacks_;
std::multimap<guest_os::GuestId, base::OnceClosure>
shutdown_container_callbacks_;
std::multimap<guest_os::GuestId, CrostiniResultCallback>
create_lxd_container_callbacks_;
std::multimap<guest_os::GuestId, BoolCallback>
delete_lxd_container_callbacks_;
std::map<guest_os::GuestId, ExportLxdContainerResultCallback>
export_lxd_container_callbacks_;
std::map<guest_os::GuestId, CrostiniResultCallback>
import_lxd_container_callbacks_;
// Callbacks to run after Tremplin is started, keyed by vm_name. These are
// used if StartTerminaVm completes but we need to wait from Tremplin to
// start.
std::multimap<std::string, base::OnceClosure> tremplin_started_callbacks_;
// Callbacks to run after LXD is started, keyed by vm_name. Used if StartLxd
// completes but we need to wait for LXD to start.
std::multimap<std::string, CrostiniResultCallback> start_lxd_callbacks_;
std::map<std::string, VmInfo> running_vms_;
// OsRelease protos keyed by guest_os::GuestId. We populate this map even if a
// container fails to start normally.
std::map<guest_os::GuestId, vm_tools::cicerone::OsRelease>
container_os_releases_;
std::set<guest_os::GuestId> container_upgrade_prompt_shown_;
std::vector<RemoveCrostiniCallback> remove_crostini_callbacks_;
base::ObserverList<LinuxPackageOperationProgressObserver>::
UncheckedAndDanglingUntriaged linux_package_operation_progress_observers_;
base::ObserverList<PendingAppListUpdatesObserver>
pending_app_list_updates_observers_;
base::ObserverList<ExportContainerProgressObserver>::
UncheckedAndDanglingUntriaged export_container_progress_observers_;
base::ObserverList<ImportContainerProgressObserver>::
UncheckedAndDanglingUntriaged import_container_progress_observers_;
base::ObserverList<UpgradeContainerProgressObserver>::
UncheckedAndDanglingUntriaged upgrade_container_progress_observers_;
base::ObserverList<ash::VmShutdownObserver> vm_shutdown_observers_;
base::ObserverList<ash::VmStartingObserver> vm_starting_observers_;
// RestartIds present in |restarters_by_id_| will always have a restarter in
// |restarters_by_container_| for the corresponding guest_os::GuestId.
std::map<CrostiniManager::RestartId, guest_os::GuestId> restarters_by_id_;
std::map<guest_os::GuestId, std::unique_ptr<CrostiniRestarter>>
restarters_by_container_;
static RestartId next_restart_id_;
base::ObserverList<CrostiniDialogStatusObserver>
crostini_dialog_status_observers_;
base::ObserverList<CrostiniContainerPropertiesObserver>
crostini_container_properties_observers_;
base::ObserverList<ContainerShutdownObserver> container_shutdown_observers_;
// Contains the types of crostini dialogs currently open. It is generally
// invalid to show more than one. e.g. uninstalling and installing are
// mutually exclusive.
base::flat_set<DialogType> open_crostini_dialogs_;
bool dbus_observers_removed_ = false;
base::Time time_of_last_disk_type_metric_;
std::unique_ptr<guest_os::GuestOsStabilityMonitor>
guest_os_stability_monitor_;
std::unique_ptr<CrostiniLowDiskNotification> low_disk_notifier_;
std::unique_ptr<CrostiniUpgradeAvailableNotification>
upgrade_available_notification_;
TerminaInstaller termina_installer_{};
bool install_termina_never_completes_for_testing_ = false;
std::unique_ptr<CrostiniSshfs> crostini_sshfs_;
base::flat_map<guest_os::GuestId,
guest_os::GuestOsTerminalProviderRegistry::Id>
terminal_provider_ids_;
base::ScopedObservation<ash::NetworkStateHandler,
ash::NetworkStateHandlerObserver>
network_state_handler_observer_{this};
base::flat_map<guest_os::GuestId, guest_os::GuestOsMountProviderRegistry::Id>
mount_provider_ids_;
base::CallbackListSubscription primary_counter_mount_subscription_;
bool already_warned_expired_version_ = false;
// Note: This should remain the last member so it'll be destroyed and
// invalidate its weak pointers before any other members are destroyed.
base::WeakPtrFactory<CrostiniManager> weak_ptr_factory_{this};
};
} // namespace crostini
namespace base {
template <>
struct ScopedObservationTraits<crostini::CrostiniManager,
crostini::ContainerShutdownObserver> {
static void AddObserver(crostini::CrostiniManager* source,
crostini::ContainerShutdownObserver* observer) {
source->AddContainerShutdownObserver(observer);
}
static void RemoveObserver(crostini::CrostiniManager* source,
crostini::ContainerShutdownObserver* observer) {
source->RemoveContainerShutdownObserver(observer);
}
};
template <>
struct ScopedObservationTraits<crostini::CrostiniManager,
ash::VmShutdownObserver> {
static void AddObserver(crostini::CrostiniManager* source,
ash::VmShutdownObserver* observer) {
source->AddVmShutdownObserver(observer);
}
static void RemoveObserver(crostini::CrostiniManager* source,
ash::VmShutdownObserver* observer) {
source->RemoveVmShutdownObserver(observer);
}
};
} // namespace base
#endif // CHROME_BROWSER_ASH_CROSTINI_CROSTINI_MANAGER_H_