Commit 55e6170f authored by Nicholas Verne's avatar Nicholas Verne Committed by Commit Bot

Crostini restarting flow.

Once crostini is already installed, it needs to be restarted after
Chrome shuts down. These restart steps are all very fast in the case that
the default vm and container are already running. If not, there can be
a several second delay. Launching crostini apps always goes through this
flow and testing shows the impact is not user-noticeable when the
vm and container are running.

A future CL will add an observer to the
CrostiniRestarter class for this case.

Bug: 832509
Change-Id: I359e5a25c30664fad6425c218853dda204c605d9
Reviewed-on: https://chromium-review.googlesource.com/1013804
Commit-Queue: Nicholas Verne <nverne@chromium.org>
Reviewed-by: default avatarTimothy Loh <timloh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#551593}
parent 7c1b43d1
...@@ -8,16 +8,22 @@ ...@@ -8,16 +8,22 @@
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/sys_info.h" #include "base/sys_info.h"
#include "base/task_scheduler/post_task.h" #include "base/task_scheduler/post_task.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/component_updater/cros_component_installer.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/app_list/crostini/crostini_util.h" #include "chrome/browser/ui/app_list/crostini/crostini_util.h"
#include "chrome/browser/ui/extensions/application_launch.h" #include "chrome/browser/ui/extensions/application_launch.h"
#include "chromeos/dbus/concierge_client.h" #include "chromeos/dbus/concierge_client.h"
#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/debug_daemon_client.h" #include "chromeos/dbus/debug_daemon_client.h"
#include "content/public/browser/browser_thread.h"
#include "dbus/message.h" #include "dbus/message.h"
#include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_registry.h"
#include "net/base/escape.h" #include "net/base/escape.h"
namespace crostini {
namespace { namespace {
constexpr int64_t kMinimumDiskSize = 1ll * 1024 * 1024 * 1024; // 1 GiB constexpr int64_t kMinimumDiskSize = 1ll * 1024 * 1024 * 1024; // 1 GiB
...@@ -28,9 +34,122 @@ chromeos::ConciergeClient* GetConciergeClient() { ...@@ -28,9 +34,122 @@ chromeos::ConciergeClient* GetConciergeClient() {
return chromeos::DBusThreadManager::Get()->GetConciergeClient(); return chromeos::DBusThreadManager::Get()->GetConciergeClient();
} }
} // namespace std::string ContainerUserNameForProfile(Profile* profile) {
// Get rid of the @domain.name in the profile user name (an email address).
std::string container_username = profile->GetProfileUserName();
return container_username.substr(0, container_username.find('@'));
}
namespace crostini { class CrostiniRestarter : public base::RefCountedThreadSafe<CrostiniRestarter> {
public:
CrostiniRestarter(std::string vm_name,
std::string cryptohome_id,
std::string container_name,
std::string container_username,
CrostiniManager::RestartCrostiniCallback callback)
: vm_name_(std::move(vm_name)),
cryptohome_id_(std::move(cryptohome_id)),
container_name_(std::move(container_name)),
container_username_(std::move(container_username)),
callback_(std::move(callback)) {}
void Restart() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
g_browser_process->platform_part()->cros_component_manager()->Load(
"cros-termina",
component_updater::CrOSComponentManager::MountPolicy::kMount,
base::BindOnce(&CrostiniRestarter::InstallImageLoaderFinished,
base::WrapRefCounted(this)));
}
private:
friend class base::RefCountedThreadSafe<CrostiniRestarter>;
~CrostiniRestarter() {
if (callback_) {
LOG(ERROR) << "Destroying without having called the callback.";
}
}
static void InstallImageLoaderFinished(
scoped_refptr<CrostiniRestarter> restarter,
component_updater::CrOSComponentManager::Error error,
const base::FilePath& result) {
DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::BindOnce(&CrostiniRestarter::InstallImageLoaderFinishedOnUIThread,
restarter, error, result));
}
void InstallImageLoaderFinishedOnUIThread(
component_updater::CrOSComponentManager::Error error,
const base::FilePath& result) {
if (error != component_updater::CrOSComponentManager::Error::NONE) {
LOG(ERROR)
<< "Failed to install the cros-termina component with error code: "
<< static_cast<int>(error);
std::move(callback_).Run(ConciergeClientResult::CONTAINER_START_FAILED);
return;
}
CrostiniManager::GetInstance()->StartVmConcierge(
base::BindOnce(&CrostiniRestarter::ConciergeStarted, this));
}
void ConciergeStarted(bool is_started) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!is_started) {
LOG(ERROR) << "Failed to start Concierge service.";
std::move(callback_).Run(ConciergeClientResult::CONTAINER_START_FAILED);
return;
}
CrostiniManager::GetInstance()->CreateDiskImage(
cryptohome_id_, base::FilePath(vm_name_),
vm_tools::concierge::StorageLocation::STORAGE_CRYPTOHOME_ROOT,
base::BindOnce(&CrostiniRestarter::CreateDiskImageFinished, this));
}
void CreateDiskImageFinished(ConciergeClientResult result,
const base::FilePath& result_path) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (result != ConciergeClientResult::SUCCESS) {
LOG(ERROR) << "Failed to create disk image.";
std::move(callback_).Run(result);
return;
}
CrostiniManager::GetInstance()->StartTerminaVm(
vm_name_, result_path,
base::BindOnce(&CrostiniRestarter::StartTerminaVmFinished, this));
}
void StartTerminaVmFinished(ConciergeClientResult result) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (result != ConciergeClientResult::SUCCESS) {
LOG(ERROR) << "Failed to Start Termina VM.";
std::move(callback_).Run(result);
return;
}
CrostiniManager::GetInstance()->StartContainer(
vm_name_, container_name_, container_username_,
base::BindOnce(&CrostiniRestarter::StartContainerFinished, this));
}
void StartContainerFinished(ConciergeClientResult result) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (result != ConciergeClientResult::SUCCESS) {
LOG(ERROR) << "Failed to start container.";
}
std::move(callback_).Run(result);
}
std::string vm_name_;
std::string cryptohome_id_;
std::string container_name_;
std::string container_username_;
CrostiniManager::RestartCrostiniCallback callback_;
};
} // namespace
// static // static
CrostiniManager* CrostiniManager::GetInstance() { CrostiniManager* CrostiniManager::GetInstance() {
...@@ -46,6 +165,14 @@ CrostiniManager::CrostiniManager() : weak_ptr_factory_(this) { ...@@ -46,6 +165,14 @@ CrostiniManager::CrostiniManager() : weak_ptr_factory_(this) {
CrostiniManager::~CrostiniManager() {} CrostiniManager::~CrostiniManager() {}
// static
bool CrostiniManager::IsCrosTerminaInstalled() {
return !g_browser_process->platform_part()
->cros_component_manager()
->GetCompatiblePath("cros-termina")
.empty();
}
void CrostiniManager::StartVmConcierge(StartVmConciergeCallback callback) { void CrostiniManager::StartVmConcierge(StartVmConciergeCallback callback) {
VLOG(1) << "Starting VmConcierge service"; VLOG(1) << "Starting VmConcierge service";
chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->StartVmConcierge( chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->StartVmConcierge(
...@@ -276,8 +403,8 @@ void CrostiniManager::LaunchContainerApplication( ...@@ -276,8 +403,8 @@ void CrostiniManager::LaunchContainerApplication(
void CrostiniManager::LaunchContainerTerminal( void CrostiniManager::LaunchContainerTerminal(
Profile* profile, Profile* profile,
const std::string& vm_name, const std::string& vm_name,
const std::string& container_name, const std::string& container_name) {
const std::string& container_username) { std::string container_username = ContainerUserNameForProfile(profile);
std::string vsh_crosh = base::StringPrintf( std::string vsh_crosh = base::StringPrintf(
"chrome-extension://%s/html/crosh.html?command=vmshell", "chrome-extension://%s/html/crosh.html?command=vmshell",
kCrostiniCroshBuiltinAppId); kCrostiniCroshBuiltinAppId);
...@@ -312,6 +439,22 @@ void CrostiniManager::LaunchContainerTerminal( ...@@ -312,6 +439,22 @@ void CrostiniManager::LaunchContainerTerminal(
OpenApplicationWindow(launch_params, vsh_in_crosh_url); OpenApplicationWindow(launch_params, vsh_in_crosh_url);
} }
void CrostiniManager::RestartCrostini(Profile* profile,
std::string vm_name,
std::string container_name,
RestartCrostiniCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
std::string cryptohome_id = chromeos::ProfileHelper::Get()
->GetUserByProfile(profile)
->username_hash();
std::string container_username = ContainerUserNameForProfile(profile);
auto crostini_restarter = base::MakeRefCounted<CrostiniRestarter>(
std::move(vm_name), std::move(cryptohome_id), std::move(container_name),
std::move(container_username), std::move(callback));
crostini_restarter->Restart();
}
void CrostiniManager::OnCreateDiskImage( void CrostiniManager::OnCreateDiskImage(
CreateDiskImageCallback callback, CreateDiskImageCallback callback,
base::Optional<vm_tools::concierge::CreateDiskImageResponse> reply) { base::Optional<vm_tools::concierge::CreateDiskImageResponse> reply) {
......
...@@ -61,6 +61,12 @@ class CrostiniManager : public chromeos::ConciergeClient::Observer { ...@@ -61,6 +61,12 @@ class CrostiniManager : public chromeos::ConciergeClient::Observer {
// The type of the callback for CrostiniManager::LaunchContainerApplication. // The type of the callback for CrostiniManager::LaunchContainerApplication.
using LaunchContainerApplicationCallback = using LaunchContainerApplicationCallback =
base::OnceCallback<void(ConciergeClientResult result)>; base::OnceCallback<void(ConciergeClientResult result)>;
// The type of the callback for CrostiniManager::RestartCrostini.
using RestartCrostiniCallback =
base::OnceCallback<void(ConciergeClientResult result)>;
// Checks if the cros-termina component is installed.
static bool IsCrosTerminaInstalled();
// Starts the Concierge service. |callback| is called after the method call // Starts the Concierge service. |callback| is called after the method call
// finishes. // finishes.
...@@ -119,8 +125,8 @@ class CrostiniManager : public chromeos::ConciergeClient::Observer { ...@@ -119,8 +125,8 @@ class CrostiniManager : public chromeos::ConciergeClient::Observer {
StartContainerCallback callback); StartContainerCallback callback);
// Asynchronously launches an app as specified by its desktop file id. // Asynchronously launches an app as specified by its desktop file id.
// |callback| is called with SUCCESS when the relevant process is started or // |callback| is called with SUCCESS when the relevant process is started
// LAUNCH_CONTAINER_APPLICATION_FAILED if there was an error somewhere. // or LAUNCH_CONTAINER_APPLICATION_FAILED if there was an error somewhere.
// //
// TODO(nverne): Start the VM and Container if not already running. // TODO(nverne): Start the VM and Container if not already running.
void LaunchContainerApplication(std::string vm_name, void LaunchContainerApplication(std::string vm_name,
...@@ -132,8 +138,12 @@ class CrostiniManager : public chromeos::ConciergeClient::Observer { ...@@ -132,8 +138,12 @@ class CrostiniManager : public chromeos::ConciergeClient::Observer {
// container on a VM. // container on a VM.
void LaunchContainerTerminal(Profile* profile, void LaunchContainerTerminal(Profile* profile,
const std::string& vm_name, const std::string& vm_name,
const std::string& container_name, const std::string& container_name);
const std::string& container_username);
void RestartCrostini(Profile* profile,
std::string vm_name,
std::string container_name,
RestartCrostiniCallback callback);
// ConciergeClient::Observer: // ConciergeClient::Observer:
void OnContainerStarted( void OnContainerStarted(
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "chrome/browser/ui/app_list/app_list_controller_delegate.h" #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
#include "chrome/browser/ui/app_list/crostini/crostini_app_context_menu.h" #include "chrome/browser/ui/app_list/crostini/crostini_app_context_menu.h"
#include "chrome/browser/ui/app_list/crostini/crostini_installer_view.h" #include "chrome/browser/ui/app_list/crostini/crostini_installer_view.h"
#include "chrome/browser/ui/app_list/crostini/crostini_util.h"
#include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia.h"
// static // static
...@@ -38,22 +39,51 @@ const char* CrostiniAppItem::GetItemType() const { ...@@ -38,22 +39,51 @@ const char* CrostiniAppItem::GetItemType() const {
return CrostiniAppItem::kItemType; return CrostiniAppItem::kItemType;
} }
void CrostiniAppItem::Activate(int event_flags) { namespace {
chromeos::CrostiniRegistryService* registry_service = void MaybeLaunchTerminal(Profile* profile,
chromeos::CrostiniRegistryServiceFactory::GetForProfile(profile()); crostini::ConciergeClientResult result) {
std::unique_ptr<chromeos::CrostiniRegistryService::Registration> if (result == crostini::ConciergeClientResult::SUCCESS) {
registration = registry_service->GetRegistration(id()); crostini::CrostiniManager::GetInstance()->LaunchContainerTerminal(
if (registration) { profile, kCrostiniDefaultVmName, kCrostiniDefaultContainerName);
}
}
void MaybeLaunchContainerAppplication(
std::unique_ptr<chromeos::CrostiniRegistryService::Registration>
registration,
crostini::ConciergeClientResult result) {
if (result == crostini::ConciergeClientResult::SUCCESS) {
// TODO(timloh): Do something if launching failed, as otherwise the app // TODO(timloh): Do something if launching failed, as otherwise the app
// launcher remains open and there's no feedback. // launcher remains open and there's no feedback.
crostini::CrostiniManager::GetInstance()->LaunchContainerApplication( crostini::CrostiniManager::GetInstance()->LaunchContainerApplication(
registration->vm_name, registration->container_name, registration->vm_name, registration->container_name,
registration->desktop_file_id, registration->desktop_file_id,
base::BindOnce([](crostini::ConciergeClientResult result) {})); base::BindOnce([](crostini::ConciergeClientResult result) {}));
return;
} }
}
} // namespace
CrostiniInstallerView::Show(this, profile()); void CrostiniAppItem::Activate(int event_flags) {
chromeos::CrostiniRegistryService* registry_service =
chromeos::CrostiniRegistryServiceFactory::GetForProfile(profile());
std::unique_ptr<chromeos::CrostiniRegistryService::Registration>
registration = registry_service->GetRegistration(id());
auto* crostini_manager = crostini::CrostiniManager::GetInstance();
if (registration) {
crostini_manager->RestartCrostini(
profile(), registration->vm_name, registration->container_name,
base::BindOnce(&MaybeLaunchContainerAppplication,
std::move(registration)));
return;
}
if (!crostini_manager->IsCrosTerminaInstalled()) {
CrostiniInstallerView::Show(this, profile());
} else {
crostini_manager->RestartCrostini(
profile(), kCrostiniDefaultVmName, kCrostiniDefaultContainerName,
base::BindOnce(&MaybeLaunchTerminal, base::Unretained(profile())));
}
} }
ui::MenuModel* CrostiniAppItem::GetContextMenuModel() { ui::MenuModel* CrostiniAppItem::GetContextMenuModel() {
......
...@@ -325,8 +325,7 @@ void CrostiniInstallerView::ShowLoginShell() { ...@@ -325,8 +325,7 @@ void CrostiniInstallerView::ShowLoginShell() {
state_ = State::SHOW_LOGIN_SHELL; state_ = State::SHOW_LOGIN_SHELL;
crostini::CrostiniManager::GetInstance()->LaunchContainerTerminal( crostini::CrostiniManager::GetInstance()->LaunchContainerTerminal(
profile_, kCrostiniDefaultVmName, kCrostiniDefaultContainerName, profile_, kCrostiniDefaultVmName, kCrostiniDefaultContainerName);
container_user_name_);
GetWidget()->SetSize(GetWidget()->non_client_view()->GetPreferredSize()); GetWidget()->SetSize(GetWidget()->non_client_view()->GetPreferredSize());
GetWidget()->UpdateWindowTitle(); GetWidget()->UpdateWindowTitle();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment