Commit d4ae3339 authored by Daniel Ng's avatar Daniel Ng Committed by Commit Bot

borealis: Add base for the borealis starter flow

This cl introduces the basic architecture to support the borealis
starter flow. It introduces the borealis context manager, which is
intended to manage the start up flow of the Borealis VM and be the
source of truth for run-time information about the Borealis VM. See
go/borealis-starter-flow for additional context.

Tests: Wrote and ran tests
Bug: b:168425531
Change-Id: I88bece091d5d9a5af4a121ff498c2ebdc504cff1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2434020
Commit-Queue: Daniel Ng <danielng@google.com>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarNic Hollingum <hollingum@google.com>
Cr-Commit-Position: refs/heads/master@{#814512}
parent f050b8de
...@@ -831,6 +831,12 @@ source_set("chromeos") { ...@@ -831,6 +831,12 @@ source_set("chromeos") {
"bluetooth/debug_logs_manager_factory.h", "bluetooth/debug_logs_manager_factory.h",
"boot_times_recorder.cc", "boot_times_recorder.cc",
"boot_times_recorder.h", "boot_times_recorder.h",
"borealis/borealis_context.h",
"borealis/borealis_context_manager.h",
"borealis/borealis_context_manager_factory.cc",
"borealis/borealis_context_manager_factory.h",
"borealis/borealis_context_manager_impl.cc",
"borealis/borealis_context_manager_impl.h",
"borealis/borealis_features.cc", "borealis/borealis_features.cc",
"borealis/borealis_features.h", "borealis/borealis_features.h",
"borealis/borealis_features_factory.cc", "borealis/borealis_features_factory.cc",
...@@ -843,6 +849,7 @@ source_set("chromeos") { ...@@ -843,6 +849,7 @@ source_set("chromeos") {
"borealis/borealis_installer_impl.h", "borealis/borealis_installer_impl.h",
"borealis/borealis_prefs.cc", "borealis/borealis_prefs.cc",
"borealis/borealis_prefs.h", "borealis/borealis_prefs.h",
"borealis/borealis_task.h",
"borealis/borealis_util.cc", "borealis/borealis_util.cc",
"borealis/borealis_util.h", "borealis/borealis_util.h",
"browser_context_keyed_service_factories.cc", "browser_context_keyed_service_factories.cc",
...@@ -3210,6 +3217,7 @@ source_set("unit_tests") { ...@@ -3210,6 +3217,7 @@ source_set("unit_tests") {
"authpolicy/authpolicy_helper.unittest.cc", "authpolicy/authpolicy_helper.unittest.cc",
"base/file_flusher_unittest.cc", "base/file_flusher_unittest.cc",
"bluetooth/debug_logs_manager_unittest.cc", "bluetooth/debug_logs_manager_unittest.cc",
"borealis/borealis_context_manager_unittest.cc",
"borealis/borealis_features_unittest.cc", "borealis/borealis_features_unittest.cc",
"borealis/borealis_installer_unittest.cc", "borealis/borealis_installer_unittest.cc",
"cert_provisioning/cert_provisioning_invalidator_unittest.cc", "cert_provisioning/cert_provisioning_invalidator_unittest.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_CONTEXT_H_
#define CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_CONTEXT_H_
#include "chrome/browser/profiles/profile.h"
namespace borealis {
// An object to track information about the state of the Borealis VM.
// BorealisContext objects should only be created by the Borealis Context
// Manager, which is why the constructor is private.
class BorealisContext {
public:
BorealisContext(const BorealisContext&) = delete;
BorealisContext& operator=(const BorealisContext&) = delete;
~BorealisContext() = default;
static BorealisContext* CreateBorealisContextForTesting() {
return new BorealisContext();
}
Profile* profile() { return profile_; }
void set_profile(Profile* profile) { profile_ = profile; }
bool borealis_running() const { return borealis_running_; }
void set_borealis_running(bool success) { borealis_running_ = success; }
private:
friend class BorealisContextManagerImpl;
BorealisContext() = default;
explicit BorealisContext(Profile* profile) : profile_(profile) {}
Profile* profile_ = nullptr;
bool borealis_running_ = false;
};
} // namespace borealis
#endif // CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_CONTEXT_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_CONTEXT_MANAGER_H_
#define CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_CONTEXT_MANAGER_H_
#include "base/callback.h"
#include "components/keyed_service/core/keyed_service.h"
namespace borealis {
class BorealisContext;
class BorealisTask;
using BorealisContextCallback =
base::OnceCallback<void(const BorealisContext&)>;
class BorealisContextManager : public KeyedService {
public:
BorealisContextManager() = default;
BorealisContextManager(const BorealisContextManager&) = delete;
BorealisContextManager& operator=(const BorealisContextManager&) = delete;
~BorealisContextManager() override = default;
// Starts the Borealis VM and/or runs the callback when it is running.
virtual void StartBorealis(BorealisContextCallback callback) = 0;
virtual void AddTaskForTesting(std::unique_ptr<BorealisTask> task) = 0;
};
} // namespace borealis
#endif // CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_CONTEXT_MANAGER_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/borealis/borealis_context_manager_factory.h"
#include "chrome/browser/chromeos/borealis/borealis_context_manager_impl.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/profiles/incognito_helpers.h"
#include "chrome/browser/profiles/profile.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
namespace borealis {
// static
BorealisContextManager* BorealisContextManagerFactory::GetForProfile(
Profile* profile) {
return static_cast<BorealisContextManager*>(
GetInstance()->GetServiceForBrowserContext(profile, true));
}
// static
BorealisContextManagerFactory* BorealisContextManagerFactory::GetInstance() {
static base::NoDestructor<BorealisContextManagerFactory> factory;
return factory.get();
}
BorealisContextManagerFactory::BorealisContextManagerFactory()
: BrowserContextKeyedServiceFactory(
"BorealisContextManager",
BrowserContextDependencyManager::GetInstance()) {}
BorealisContextManagerFactory::~BorealisContextManagerFactory() = default;
KeyedService* BorealisContextManagerFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
Profile* profile = Profile::FromBrowserContext(context);
if (chromeos::ProfileHelper::IsPrimaryProfile(profile))
return new BorealisContextManagerImpl(profile);
return nullptr;
}
content::BrowserContext* BorealisContextManagerFactory::GetBrowserContextToUse(
content::BrowserContext* context) const {
return chrome::GetBrowserContextRedirectedInIncognito(context);
}
} // namespace borealis
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_CONTEXT_MANAGER_FACTORY_H_
#define CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_CONTEXT_MANAGER_FACTORY_H_
#include "base/macros.h"
#include "base/no_destructor.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
namespace content {
class BrowserContext;
} // namespace content
class Profile;
namespace borealis {
class BorealisContextManager;
class BorealisContextManagerFactory : public BrowserContextKeyedServiceFactory {
public:
static BorealisContextManager* GetForProfile(Profile* profile);
static BorealisContextManagerFactory* GetInstance();
private:
friend base::NoDestructor<BorealisContextManagerFactory>;
BorealisContextManagerFactory();
BorealisContextManagerFactory(const BorealisContextManagerFactory&) = delete;
BorealisContextManagerFactory& operator=(
const BorealisContextManagerFactory&) = delete;
~BorealisContextManagerFactory() override;
// BrowserContextKeyedServiceFactory implementation.
KeyedService* BuildServiceInstanceFor(
content::BrowserContext* context) const override;
content::BrowserContext* GetBrowserContextToUse(
content::BrowserContext* context) const override;
};
} // namespace borealis
#endif // CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_CONTEXT_MANAGER_FACTORY_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/borealis/borealis_context_manager_impl.h"
#include "base/logging.h"
namespace borealis {
BorealisContextManagerImpl::BorealisContextManagerImpl(Profile* profile)
: profile_(profile), context_(profile_) {}
BorealisContextManagerImpl::~BorealisContextManagerImpl() = default;
void BorealisContextManagerImpl::StartBorealis(
BorealisContextCallback callback) {
if (is_borealis_running_) {
std::move(callback).Run(context_);
return;
}
AddCallback(std::move(callback));
if (!is_borealis_starting_) {
is_borealis_starting_ = true;
// TODO(b/168425531): add actual startup tasks here.
NextTask(/*should_continue=*/true);
}
}
void BorealisContextManagerImpl::AddTaskForTesting(
std::unique_ptr<BorealisTask> task) {
AddTask(std::move(task));
}
void BorealisContextManagerImpl::AddTask(std::unique_ptr<BorealisTask> task) {
task_queue_.push(std::move(task));
}
void BorealisContextManagerImpl::AddCallback(BorealisContextCallback callback) {
callback_queue_.push(std::move(callback));
}
void BorealisContextManagerImpl::NextTask(bool should_continue) {
if (!should_continue) {
// TODO(b/168425531): Error handling should be expanded to give more
// information about which task failed, why it failed and what should happen
// as a result.
LOG(ERROR) << "A task failed when trying to start Borealis.";
OnQueueComplete();
return;
}
if (task_queue_.empty()) {
context_.set_borealis_running(true);
is_borealis_running_ = true;
OnQueueComplete();
return;
}
std::unique_ptr<BorealisTask> next_task = std::move(task_queue_.front());
task_queue_.pop();
next_task->Run(&context_,
base::BindOnce(&BorealisContextManagerImpl::NextTask,
weak_factory_.GetWeakPtr()));
}
void BorealisContextManagerImpl::OnQueueComplete() {
is_borealis_starting_ = false;
while (!callback_queue_.empty()) {
BorealisContextCallback callback = std::move(callback_queue_.front());
callback_queue_.pop();
std::move(callback).Run(context_);
}
}
} // namespace borealis
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_CONTEXT_MANAGER_IMPL_H_
#define CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_CONTEXT_MANAGER_IMPL_H_
#include "base/containers/queue.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/chromeos/borealis/borealis_context.h"
#include "chrome/browser/chromeos/borealis/borealis_context_manager.h"
#include "chrome/browser/chromeos/borealis/borealis_task.h"
#include "chrome/browser/profiles/profile.h"
namespace borealis {
using BorealisContextCallback =
base::OnceCallback<void(const BorealisContext&)>;
// The Borealis Context Manager is a keyed service responsible for managing
// the Borealis VM startup flow and guaranteeing its state to other processes.
class BorealisContextManagerImpl : public BorealisContextManager {
public:
explicit BorealisContextManagerImpl(Profile* profile);
BorealisContextManagerImpl(const BorealisContextManagerImpl&) = delete;
BorealisContextManagerImpl& operator=(const BorealisContextManagerImpl&) =
delete;
~BorealisContextManagerImpl() override;
// BorealisContextManager:
void StartBorealis(BorealisContextCallback callback) override;
void AddTaskForTesting(std::unique_ptr<BorealisTask> task) override;
private:
void AddTask(std::unique_ptr<BorealisTask> task);
void AddCallback(BorealisContextCallback callback);
void NextTask(bool should_continue);
void OnQueueComplete();
bool is_borealis_running_ = false;
bool is_borealis_starting_ = false;
Profile* profile_ = nullptr;
BorealisContext context_;
base::queue<BorealisContextCallback> callback_queue_;
base::queue<std::unique_ptr<BorealisTask>> task_queue_;
base::WeakPtrFactory<BorealisContextManagerImpl> weak_factory_{this};
};
} // namespace borealis
#endif // CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_CONTEXT_MANAGER_IMPL_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/borealis/borealis_context_manager_impl.h"
#include <memory>
#include "chrome/browser/chromeos/borealis/borealis_context_manager_factory.h"
#include "chrome/browser/chromeos/borealis/borealis_task.h"
#include "chrome/browser/chromeos/login/users/mock_user_manager.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "components/user_manager/scoped_user_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace borealis {
namespace {
class MockTask : public BorealisTask {
public:
explicit MockTask(bool success) : success_(success) {}
void Run(BorealisContext* context,
base::OnceCallback<void(bool)> callback) override {
std::move(callback).Run(/*should_continue=*/success_);
}
bool success_ = true;
};
void CallbackForTesting(bool expected_success,
bool* callback_ran,
const BorealisContext& context) {
EXPECT_EQ(context.borealis_running(), expected_success);
*callback_ran = true;
}
class BorealisContextManagerTest : public testing::Test {
public:
BorealisContextManagerTest() = default;
BorealisContextManagerTest(const BorealisContextManagerTest&) = delete;
BorealisContextManagerTest& operator=(const BorealisContextManagerTest&) =
delete;
~BorealisContextManagerTest() override = default;
protected:
void SetUp() override {
auto mock_user_manager =
std::make_unique<testing::NiceMock<chromeos::MockUserManager>>();
mock_user_manager->AddUser(
AccountId::FromUserEmailGaiaId(profile_.GetProfileUserName(), "id"));
scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
std::move(mock_user_manager));
chromeos::DBusThreadManager::Initialize();
context_manager_ = BorealisContextManagerFactory::GetForProfile(&profile_);
callback_ran_ = false;
}
void TearDown() override { chromeos::DBusThreadManager::Shutdown(); }
content::BrowserTaskEnvironment task_environment_;
TestingProfile profile_;
std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
BorealisContextManager* context_manager_;
bool callback_ran_ = false;
};
TEST_F(BorealisContextManagerTest, StartupSucceedsForSuccessfulTask) {
context_manager_->AddTaskForTesting(
std::make_unique<MockTask>(/*success=*/true));
base::OnceCallback<void(const BorealisContext&)> callback = base::BindOnce(
CallbackForTesting, /*expected_success=*/true, &callback_ran_);
context_manager_->StartBorealis(std::move(callback));
task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_ran_);
}
TEST_F(BorealisContextManagerTest, StartupSucceedsForSuccessfulGroupOfTasks) {
context_manager_->AddTaskForTesting(
std::make_unique<MockTask>(/*success=*/true));
context_manager_->AddTaskForTesting(
std::make_unique<MockTask>(/*success=*/true));
context_manager_->AddTaskForTesting(
std::make_unique<MockTask>(/*success=*/true));
base::OnceCallback<void(const BorealisContext&)> callback = base::BindOnce(
CallbackForTesting, /*expected_success=*/true, &callback_ran_);
context_manager_->StartBorealis(std::move(callback));
task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_ran_);
}
TEST_F(BorealisContextManagerTest, StartupFailsForUnsuccessfulTask) {
context_manager_->AddTaskForTesting(
std::make_unique<MockTask>(/*success=*/false));
base::OnceCallback<void(const BorealisContext&)> callback = base::BindOnce(
CallbackForTesting, /*expected_success=*/false, &callback_ran_);
context_manager_->StartBorealis(std::move(callback));
task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_ran_);
}
TEST_F(BorealisContextManagerTest, StartupFailsForUnsuccessfulGroupOfTasks) {
context_manager_->AddTaskForTesting(
std::make_unique<MockTask>(/*success=*/true));
context_manager_->AddTaskForTesting(
std::make_unique<MockTask>(/*success=*/false));
context_manager_->AddTaskForTesting(
std::make_unique<MockTask>(/*success=*/true));
base::OnceCallback<void(const BorealisContext&)> callback = base::BindOnce(
CallbackForTesting, /*expected_success=*/false, &callback_ran_);
context_manager_->StartBorealis(std::move(callback));
task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_ran_);
}
TEST_F(BorealisContextManagerTest, MultipleSuccessfulStartupsAllCallbacksRan) {
context_manager_->AddTaskForTesting(
std::make_unique<MockTask>(/*success=*/true));
bool callback_one_ran = false;
bool callback_two_ran = false;
base::OnceCallback<void(const BorealisContext&)> callback_one =
base::BindOnce(CallbackForTesting, /*expected_success=*/true,
&callback_one_ran);
base::OnceCallback<void(const BorealisContext&)> callback_two =
base::BindOnce(CallbackForTesting, /*expected_success=*/true,
&callback_two_ran);
context_manager_->StartBorealis(std::move(callback_one));
context_manager_->StartBorealis(std::move(callback_two));
task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_one_ran);
EXPECT_TRUE(callback_two_ran);
}
TEST_F(BorealisContextManagerTest,
MultipleUnsuccessfulStartupsAllCallbacksRan) {
context_manager_->AddTaskForTesting(
std::make_unique<MockTask>(/*success=*/false));
context_manager_->AddTaskForTesting(
std::make_unique<MockTask>(/*success=*/false));
bool callback_one_ran = false;
bool callback_two_ran = false;
base::OnceCallback<void(const BorealisContext&)> callback_one =
base::BindOnce(CallbackForTesting, /*expected_success=*/false,
&callback_one_ran);
base::OnceCallback<void(const BorealisContext&)> callback_two =
base::BindOnce(CallbackForTesting, /*expected_success=*/false,
&callback_two_ran);
context_manager_->StartBorealis(std::move(callback_one));
context_manager_->StartBorealis(std::move(callback_two));
task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_one_ran);
EXPECT_TRUE(callback_two_ran);
}
} // namespace
} // namespace borealis
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_TASK_H_
#define CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_TASK_H_
namespace borealis {
class BorealisContext;
// BorealisTasks are collections of operations that are run by the
// Borealis Context Manager.
class BorealisTask {
public:
// Callback to be run when the task completes. The |bool| should reflect
// if the task succeeded (true) or failed (false).
using CompletionStatusCallback = base::OnceCallback<void(bool)>;
virtual void Run(BorealisContext* context,
CompletionStatusCallback callback) = 0;
virtual ~BorealisTask() = default;
};
} // namespace borealis
#endif // CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_TASK_H_
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