Commit 1ffee348 authored by Sam McNally's avatar Sam McNally Committed by Commit Bot

Add a host on the Chrome side to mount and communicate with DriveFS.

Bug: 823607
Change-Id: I763d48015011dc4cb59d1e5d15cc78b9cf13f2b7
Reviewed-on: https://chromium-review.googlesource.com/1032271Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Reviewed-by: default avatarSteven Bennetts <stevenjb@chromium.org>
Commit-Queue: Sam McNally <sammc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#556364}
parent 03f0979c
...@@ -16,7 +16,9 @@ test("chromeos_components_unittests") { ...@@ -16,7 +16,9 @@ test("chromeos_components_unittests") {
deps = [ deps = [
"//base", "//base",
"//base/test:test_support", "//base/test:test_support",
"//chromeos/components/drivefs:unit_tests",
"//chromeos/components/proximity_auth:unit_tests", "//chromeos/components/proximity_auth:unit_tests",
"//chromeos/components/tether:unit_tests", "//chromeos/components/tether:unit_tests",
"//mojo/edk",
] ]
} }
# Copyright 2018 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.
assert(is_chromeos, "Non-ChromeOS builds cannot depend on //chromeos")
component("drivefs") {
sources = [
"drivefs_host.cc",
"drivefs_host.h",
"pending_connection_manager.cc",
"pending_connection_manager.h",
]
deps = [
"//base",
"//chromeos",
"//chromeos/components/drivefs/mojom",
"//components/account_id",
"//mojo/edk",
"//mojo/public/cpp/bindings",
]
defines = [ "IS_DRIVEFS_IMPL" ]
}
source_set("unit_tests") {
testonly = true
sources = [
"drivefs_host_unittest.cc",
"pending_connection_manager_unittest.cc",
]
deps = [
":drivefs",
"//base",
"//base/test:test_support",
"//chromeos:test_support",
"//chromeos/components/drivefs/mojom",
"//mojo/public/cpp/bindings",
"//testing/gmock",
"//testing/gtest",
]
}
include_rules = [
"+mojo/edk/embedder",
"+mojo/public",
]
dats@chromium.org
sammc@chromium.org
slangley@chromium.org
// Copyright 2018 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 "chromeos/components/drivefs/drivefs_host.h"
#include <utility>
#include "base/strings/strcat.h"
#include "base/unguessable_token.h"
#include "chromeos/components/drivefs/pending_connection_manager.h"
#include "mojo/edk/embedder/connection_params.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/edk/embedder/outgoing_broker_client_invitation.h"
#include "mojo/edk/embedder/scoped_platform_handle.h"
#include "mojo/edk/embedder/transport_protocol.h"
#include "mojo/public/cpp/bindings/binding.h"
namespace drivefs {
namespace {
constexpr char kMountScheme[] = "drivefs://";
constexpr char kDataPath[] = "GCache/v2";
class MojoConnectionDelegateImpl : public DriveFsHost::MojoConnectionDelegate {
public:
MojoConnectionDelegateImpl() = default;
static std::unique_ptr<DriveFsHost::MojoConnectionDelegate> Create() {
return std::make_unique<MojoConnectionDelegateImpl>();
}
mojom::DriveFsBootstrapPtrInfo InitializeMojoConnection() override {
return mojom::DriveFsBootstrapPtrInfo(
invitation_.AttachMessagePipe("drivefs-bootstrap"),
mojom::DriveFsBootstrap::Version_);
}
void AcceptMojoConnection(base::ScopedFD handle) override {
invitation_.Send(base::kNullProcessHandle,
{mojo::edk::TransportProtocol::kLegacy,
mojo::edk::ScopedPlatformHandle(
mojo::edk::PlatformHandle(handle.release()))});
}
private:
// The underlying mojo connection.
mojo::edk::OutgoingBrokerClientInvitation invitation_;
DISALLOW_COPY_AND_ASSIGN(MojoConnectionDelegateImpl);
};
base::RepeatingCallback<std::unique_ptr<DriveFsHost::MojoConnectionDelegate>()>
GetMojoConnectionDelegateFactoryOrDefault(
base::RepeatingCallback<
std::unique_ptr<DriveFsHost::MojoConnectionDelegate>()> factory) {
return factory ? factory
: base::BindRepeating(&MojoConnectionDelegateImpl::Create);
}
} // namespace
// A container of state tied to a particular mounting of DriveFS. None of this
// should be shared between mounts.
class DriveFsHost::MountState {
public:
explicit MountState(DriveFsHost* host)
: host_(host),
mojo_connection_delegate_(
host_->mojo_connection_delegate_factory_.Run()),
pending_token_(base::UnguessableToken::Create()),
binding_(host_) {
source_path_ = base::StrCat({kMountScheme, pending_token_.ToString(), "@",
host_->profile_path_.Append(kDataPath)
.Append(host_->account_id_.GetGaiaId())
.value()});
// TODO(sammc): Switch the mount type once a more appropriate mount type
// exists.
chromeos::disks::DiskMountManager::GetInstance()->MountPath(
source_path_, "", "", chromeos::MOUNT_TYPE_ARCHIVE,
chromeos::MOUNT_ACCESS_MODE_READ_WRITE);
auto bootstrap =
mojo::MakeProxy(mojo_connection_delegate_->InitializeMojoConnection());
mojom::DriveFsDelegatePtr delegate;
binding_.Bind(mojo::MakeRequest(&delegate));
bootstrap->Init({base::in_place, host_->account_id_.GetUserEmail()},
mojo::MakeRequest(&drivefs_), std::move(delegate));
// If unconsumed, the registration is cleaned up when |this| is destructed.
PendingConnectionManager::Get().ExpectOpenIpcChannel(
pending_token_,
base::BindOnce(&DriveFsHost::MountState::AcceptMojoConnection,
base::Unretained(this)));
}
~MountState() {
if (pending_token_) {
PendingConnectionManager::Get().CancelExpectedOpenIpcChannel(
pending_token_);
}
if (!mount_path_.empty()) {
chromeos::disks::DiskMountManager::GetInstance()->UnmountPath(
mount_path_, chromeos::UNMOUNT_OPTIONS_NONE, {});
}
}
// Accepts the mojo connection over |handle|, delegating to
// |mojo_connection_delegate_|.
void AcceptMojoConnection(base::ScopedFD handle) {
DCHECK(pending_token_);
pending_token_ = {};
mojo_connection_delegate_->AcceptMojoConnection(std::move(handle));
}
// Returns false if there was an error for |source_path_| and thus if |this|
// should be deleted.
bool OnMountEvent(
chromeos::disks::DiskMountManager::MountEvent event,
chromeos::MountError error_code,
const chromeos::disks::DiskMountManager::MountPointInfo& mount_info) {
if (mount_info.source_path != source_path_ ||
event != chromeos::disks::DiskMountManager::MOUNTING) {
return true;
}
if (error_code != chromeos::MOUNT_ERROR_NONE) {
return false;
}
mount_path_ = mount_info.mount_path;
return true;
}
private:
// Owns this.
DriveFsHost* const host_;
const std::unique_ptr<DriveFsHost::MojoConnectionDelegate>
mojo_connection_delegate_;
// The token passed to DriveFS as part of |source_path_| used to match it to
// this DriveFsHost instance.
base::UnguessableToken pending_token_;
// The path passed to cros-disks to mount.
std::string source_path_;
// The path where DriveFS is mounted.
std::string mount_path_;
// Mojo connections to the DriveFS process.
mojom::DriveFsPtr drivefs_;
mojo::Binding<mojom::DriveFsDelegate> binding_;
private:
DISALLOW_COPY_AND_ASSIGN(MountState);
};
DriveFsHost::DriveFsHost(
const base::FilePath& profile_path,
const AccountId& account_id,
base::RepeatingCallback<
std::unique_ptr<DriveFsHost::MojoConnectionDelegate>()>
mojo_connection_delegate_factory)
: profile_path_(profile_path),
account_id_(account_id),
mojo_connection_delegate_factory_(
GetMojoConnectionDelegateFactoryOrDefault(
std::move(mojo_connection_delegate_factory))) {
chromeos::disks::DiskMountManager::GetInstance()->AddObserver(this);
}
DriveFsHost::~DriveFsHost() {
chromeos::disks::DiskMountManager::GetInstance()->RemoveObserver(this);
}
bool DriveFsHost::Mount() {
if (mount_state_ || account_id_.GetAccountType() != AccountType::GOOGLE) {
return false;
}
mount_state_ = std::make_unique<MountState>(this);
return true;
}
void DriveFsHost::Unmount() {
mount_state_.reset();
}
void DriveFsHost::GetAccessToken(const std::string& client_id,
const std::string& app_id,
const std::vector<std::string>& scopes,
GetAccessTokenCallback callback) {
// TODO(crbug.com/823956): Implement this.
std::move(callback).Run(mojom::AccessTokenStatus::kAuthError, "");
}
void DriveFsHost::OnMountEvent(
chromeos::disks::DiskMountManager::MountEvent event,
chromeos::MountError error_code,
const chromeos::disks::DiskMountManager::MountPointInfo& mount_info) {
if (!mount_state_) {
return;
}
if (!mount_state_->OnMountEvent(event, error_code, mount_info)) {
Unmount();
}
}
} // namespace drivefs
// Copyright 2018 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 CHROMEOS_COMPONENTS_DRIVEFS_DRIVEFS_HOST_H_
#define CHROMEOS_COMPONENTS_DRIVEFS_DRIVEFS_HOST_H_
#include <memory>
#include <string>
#include <vector>
#include "base/component_export.h"
#include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/macros.h"
#include "chromeos/components/drivefs/mojom/drivefs.mojom.h"
#include "chromeos/disks/disk_mount_manager.h"
#include "components/account_id/account_id.h"
namespace drivefs {
// A host for a DriveFS process. In addition to managing its lifetime via
// mounting and unmounting, it also bridges between the DriveFS process and the
// file manager.
class COMPONENT_EXPORT(DRIVEFS) DriveFsHost
: public mojom::DriveFsDelegate,
public chromeos::disks::DiskMountManager::Observer {
public:
class MojoConnectionDelegate {
public:
virtual ~MojoConnectionDelegate() = default;
// Prepare the mojo connection to be used to communicate with the DriveFS
// process. Returns the mojo handle to use for bootstrapping.
virtual mojom::DriveFsBootstrapPtrInfo InitializeMojoConnection() = 0;
// Accepts the mojo connection over |handle|.
virtual void AcceptMojoConnection(base::ScopedFD handle) = 0;
};
DriveFsHost(const base::FilePath& profile_path,
const AccountId& account,
base::RepeatingCallback<std::unique_ptr<MojoConnectionDelegate>()>
mojo_connection_delegate_factory = {});
~DriveFsHost() override;
// Mount DriveFS.
bool Mount();
// Unmount DriveFS.
void Unmount();
private:
class MountState;
// mojom::DriveFsDelegate:
void GetAccessToken(const std::string& client_id,
const std::string& app_id,
const std::vector<std::string>& scopes,
GetAccessTokenCallback callback) override;
// DiskMountManager::Observer:
void OnMountEvent(chromeos::disks::DiskMountManager::MountEvent event,
chromeos::MountError error_code,
const chromeos::disks::DiskMountManager::MountPointInfo&
mount_info) override;
// The path to the user's profile.
const base::FilePath profile_path_;
// The user whose DriveFS instance |this| is host to.
const AccountId account_id_;
// A callback used to create mojo connection delegates.
const base::RepeatingCallback<std::unique_ptr<MojoConnectionDelegate>()>
mojo_connection_delegate_factory_;
// State specific to the current mount, or null if not mounted.
std::unique_ptr<MountState> mount_state_;
DISALLOW_COPY_AND_ASSIGN(DriveFsHost);
};
} // namespace drivefs
#endif // CHROMEOS_COMPONENTS_DRIVEFS_DRIVEFS_HOST_H_
// Copyright 2018 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 "chromeos/components/drivefs/drivefs_host.h"
#include <utility>
#include "base/logging.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_split.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_task_environment.h"
#include "chromeos/components/drivefs/pending_connection_manager.h"
#include "chromeos/disks/mock_disk_mount_manager.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace drivefs {
namespace {
using testing::_;
class TestingMojoConnectionDelegate
: public DriveFsHost::MojoConnectionDelegate {
public:
TestingMojoConnectionDelegate(
mojom::DriveFsBootstrapPtrInfo pending_bootstrap)
: pending_bootstrap_(std::move(pending_bootstrap)) {}
mojom::DriveFsBootstrapPtrInfo InitializeMojoConnection() override {
return std::move(pending_bootstrap_);
}
void AcceptMojoConnection(base::ScopedFD handle) override {}
private:
mojom::DriveFsBootstrapPtrInfo pending_bootstrap_;
DISALLOW_COPY_AND_ASSIGN(TestingMojoConnectionDelegate);
};
class DriveFsHostTest : public ::testing::Test,
public mojom::DriveFsBootstrap,
public mojom::DriveFs {
public:
DriveFsHostTest() : bootstrap_binding_(this), binding_(this) {}
protected:
void SetUp() override {
testing::Test::SetUp();
profile_path_ = base::FilePath(FILE_PATH_LITERAL("/path/to/profile"));
account_id_ = AccountId::FromUserEmailGaiaId("test@example.com", "ID");
disk_manager_ = new chromeos::disks::MockDiskMountManager;
// Takes ownership of |disk_manager_|.
chromeos::disks::DiskMountManager::InitializeForTesting(disk_manager_);
host_ = std::make_unique<DriveFsHost>(
profile_path_, account_id_,
base::BindRepeating(&DriveFsHostTest::CreateMojoConnectionDelegate,
base::Unretained(this)));
}
void TearDown() override {
host_.reset();
disk_manager_ = nullptr;
chromeos::disks::DiskMountManager::Shutdown();
}
std::unique_ptr<DriveFsHost::MojoConnectionDelegate>
CreateMojoConnectionDelegate() {
DCHECK(pending_bootstrap_);
return std::make_unique<TestingMojoConnectionDelegate>(
std::move(pending_bootstrap_));
}
void DispatchMountEvent(
chromeos::disks::DiskMountManager::MountEvent event,
chromeos::MountError error_code,
const chromeos::disks::DiskMountManager::MountPointInfo& mount_info) {
static_cast<chromeos::disks::DiskMountManager::Observer&>(*host_)
.OnMountEvent(event, error_code, mount_info);
}
std::string StartMount() {
std::string source;
EXPECT_CALL(
*disk_manager_,
MountPath(
testing::AllOf(testing::StartsWith("drivefs://"),
testing::EndsWith("@/path/to/profile/GCache/v2/ID")),
"", "", _, chromeos::MOUNT_ACCESS_MODE_READ_WRITE))
.WillOnce(testing::SaveArg<0>(&source));
mojom::DriveFsBootstrapPtrInfo bootstrap;
bootstrap_binding_.Bind(mojo::MakeRequest(&bootstrap));
pending_bootstrap_ = std::move(bootstrap);
EXPECT_TRUE(host_->Mount());
testing::Mock::VerifyAndClear(&disk_manager_);
return base::SplitStringPiece(
base::StringPiece(source).substr(strlen("drivefs://")), "@",
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)[0]
.as_string();
}
void DispatchMountSuccessEvent(const std::string& token) {
DispatchMountEvent(
chromeos::disks::DiskMountManager::MOUNTING, chromeos::MOUNT_ERROR_NONE,
{base::StrCat({"drivefs://", token, "@/path/to/profile/GCache/v2/ID"}),
"/media/drivefsroot/ID",
chromeos::MOUNT_TYPE_ARCHIVE,
{}});
}
void DoMount() {
auto token = StartMount();
DispatchMountSuccessEvent(token);
base::RunLoop run_loop;
bootstrap_binding_.set_connection_error_handler(run_loop.QuitClosure());
ASSERT_TRUE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
run_loop.Run();
}
void Init(mojom::DriveFsConfigurationPtr config,
mojom::DriveFsRequest drive_fs,
mojom::DriveFsDelegatePtr delegate) override {
EXPECT_EQ("test@example.com", config->user_email);
binding_.Bind(std::move(drive_fs));
delegate_ptr_ = std::move(delegate);
}
base::FilePath profile_path_;
base::test::ScopedTaskEnvironment task_environment_;
AccountId account_id_;
chromeos::disks::MockDiskMountManager* disk_manager_;
std::unique_ptr<DriveFsHost> host_;
mojo::Binding<mojom::DriveFsBootstrap> bootstrap_binding_;
mojo::Binding<mojom::DriveFs> binding_;
mojom::DriveFsDelegatePtr delegate_ptr_;
mojom::DriveFsBootstrapPtrInfo pending_bootstrap_;
private:
DISALLOW_COPY_AND_ASSIGN(DriveFsHostTest);
};
TEST_F(DriveFsHostTest, Basic) {
ASSERT_NO_FATAL_FAILURE(DoMount());
}
TEST_F(DriveFsHostTest, UnmountAfterMountComplete) {
ASSERT_NO_FATAL_FAILURE(DoMount());
EXPECT_CALL(*disk_manager_, UnmountPath("/media/drivefsroot/ID",
chromeos::UNMOUNT_OPTIONS_NONE, _));
base::RunLoop run_loop;
binding_.set_connection_error_handler(run_loop.QuitClosure());
host_->Unmount();
run_loop.Run();
}
TEST_F(DriveFsHostTest, UnmountBeforeMountEvent) {
auto token = StartMount();
host_->Unmount();
EXPECT_FALSE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
}
TEST_F(DriveFsHostTest, UnmountBeforeMojoConnection) {
auto token = StartMount();
DispatchMountSuccessEvent(token);
EXPECT_CALL(*disk_manager_, UnmountPath("/media/drivefsroot/ID",
chromeos::UNMOUNT_OPTIONS_NONE, _));
host_->Unmount();
EXPECT_FALSE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
}
TEST_F(DriveFsHostTest, DestroyBeforeMountEvent) {
auto token = StartMount();
EXPECT_CALL(*disk_manager_, UnmountPath(_, _, _)).Times(0);
host_.reset();
EXPECT_FALSE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
}
TEST_F(DriveFsHostTest, DestroyBeforeMojoConnection) {
auto token = StartMount();
DispatchMountSuccessEvent(token);
EXPECT_CALL(*disk_manager_, UnmountPath("/media/drivefsroot/ID",
chromeos::UNMOUNT_OPTIONS_NONE, _));
host_.reset();
EXPECT_FALSE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
}
TEST_F(DriveFsHostTest, ObserveOtherMount) {
auto token = StartMount();
EXPECT_CALL(*disk_manager_, UnmountPath(_, _, _)).Times(0);
DispatchMountEvent(chromeos::disks::DiskMountManager::MOUNTING,
chromeos::MOUNT_ERROR_DIRECTORY_CREATION_FAILED,
{"some/other/mount/event",
"/some/other/mount/point",
chromeos::MOUNT_TYPE_DEVICE,
{}});
DispatchMountEvent(
chromeos::disks::DiskMountManager::UNMOUNTING, chromeos::MOUNT_ERROR_NONE,
{base::StrCat({"drivefs://", token, "@/path/to/profile/GCache/v2/ID"}),
"/media/drivefsroot/ID",
chromeos::MOUNT_TYPE_ARCHIVE,
{}});
host_->Unmount();
}
TEST_F(DriveFsHostTest, MountError) {
auto token = StartMount();
EXPECT_CALL(*disk_manager_, UnmountPath(_, _, _)).Times(0);
DispatchMountEvent(
chromeos::disks::DiskMountManager::MOUNTING,
chromeos::MOUNT_ERROR_DIRECTORY_CREATION_FAILED,
{base::StrCat({"drivefs://", token, "@/path/to/profile/GCache/v2/ID"}),
"/media/drivefsroot/ID",
chromeos::MOUNT_TYPE_ARCHIVE,
{}});
EXPECT_FALSE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
}
TEST_F(DriveFsHostTest, MountWhileAlreadyMounted) {
DoMount();
EXPECT_FALSE(host_->Mount());
}
TEST_F(DriveFsHostTest, NonGaiaAccount) {
EXPECT_CALL(*disk_manager_, MountPath(_, _, _, _, _)).Times(0);
AccountId non_gaia_account = AccountId::FromUserEmail("test2@example.com");
host_ = std::make_unique<DriveFsHost>(profile_path_, non_gaia_account);
EXPECT_FALSE(host_->Mount());
}
TEST_F(DriveFsHostTest, GetAccessToken) {
ASSERT_NO_FATAL_FAILURE(DoMount());
base::RunLoop run_loop;
auto quit_closure = run_loop.QuitClosure();
delegate_ptr_->GetAccessToken(
"client ID", "app ID", {"scope1", "scope2"},
base::BindLambdaForTesting(
[&](mojom::AccessTokenStatus status, const std::string& token) {
EXPECT_EQ(mojom::AccessTokenStatus::kAuthError, status);
EXPECT_TRUE(token.empty());
std::move(quit_closure).Run();
}));
run_loop.Run();
}
} // namespace
} // namespace drivefs
# Copyright 2018 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.
import("//mojo/public/tools/bindings/mojom.gni")
mojom_component("mojom") {
sources = [
"drivefs.mojom",
]
public_deps = [
"//mojo/public/mojom/base",
]
output_prefix = "drivefs_mojom"
macro_prefix = "DRIVEFS_MOJOM"
}
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
// Copyright 2018 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.
module drivefs.mojom;
// This file tracks platform/drivefs/mojom/drivefs.mojom. Changes should be made
// there first and then replicated here.
// Implemented by DriveFS, used from Chrome.
interface DriveFsBootstrap {
// Initialize a DriveFS instance with its configuration and mojo connections
// to the browser.
Init(DriveFsConfiguration config, DriveFs& drive_fs,
DriveFsDelegate delegate);
};
// Implemented by DriveFS, used from Chrome.
interface DriveFs {
};
// Implemented by Chrome, used from DriveFS.
interface DriveFsDelegate {
// Get an access token for |client_id| and |app_id| with access to |scopes|.
// |access_token| is only valid if |status| is kSuccess.
GetAccessToken(string client_id, string app_id, array<string> scopes) => (
AccessTokenStatus status, string access_token);
};
struct DriveFsConfiguration {
string user_email;
};
enum AccessTokenStatus {
// Getting an access token succeeded.
kSuccess,
// Getting an access token failed due to a transient error (e.g. network
// access is unavailable).
kTransientError,
// Getting an access token failed due to an auth error.
kAuthError,
};
// Copyright 2018 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 "chromeos/components/drivefs/pending_connection_manager.h"
#include <utility>
#include "base/logging.h"
namespace drivefs {
// static
PendingConnectionManager& PendingConnectionManager::Get() {
static base::NoDestructor<PendingConnectionManager> connection_manager;
return *connection_manager;
}
bool PendingConnectionManager::OpenIpcChannel(const std::string& identity,
base::ScopedFD ipc_channel) {
auto it = open_ipc_channel_callbacks_.find(identity);
if (it == open_ipc_channel_callbacks_.end()) {
return false;
}
OpenIpcChannelCallback callback = std::move(it->second);
open_ipc_channel_callbacks_.erase(it);
std::move(callback).Run(std::move(ipc_channel));
return true;
}
void PendingConnectionManager::ExpectOpenIpcChannel(
base::UnguessableToken token,
OpenIpcChannelCallback handler) {
DCHECK(token);
open_ipc_channel_callbacks_.emplace(token.ToString(), std::move(handler));
}
void PendingConnectionManager::CancelExpectedOpenIpcChannel(
base::UnguessableToken token) {
bool erased = open_ipc_channel_callbacks_.erase(token.ToString());
DCHECK_EQ(1u, erased);
}
PendingConnectionManager::PendingConnectionManager() = default;
PendingConnectionManager::~PendingConnectionManager() = default;
} // namespace drivefs
// Copyright 2018 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 CHROMEOS_COMPONENTS_DRIVEFS_PENDING_CONNECTION_MANAGER_H_
#define CHROMEOS_COMPONENTS_DRIVEFS_PENDING_CONNECTION_MANAGER_H_
#include <string>
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/component_export.h"
#include "base/containers/flat_map.h"
#include "base/files/scoped_file.h"
#include "base/macros.h"
#include "base/no_destructor.h"
#include "base/unguessable_token.h"
#include "chromeos/chromeos_export.h"
namespace drivefs {
class PendingConnectionManagerTest;
class COMPONENT_EXPORT(DRIVEFS) PendingConnectionManager {
public:
using OpenIpcChannelCallback = base::OnceCallback<void(base::ScopedFD)>;
static PendingConnectionManager& Get();
bool OpenIpcChannel(const std::string& identity, base::ScopedFD ipc_channel);
void ExpectOpenIpcChannel(base::UnguessableToken token,
OpenIpcChannelCallback handler);
void CancelExpectedOpenIpcChannel(base::UnguessableToken token);
private:
friend class base::NoDestructor<PendingConnectionManager>;
friend class PendingConnectionManagerTest;
PendingConnectionManager();
~PendingConnectionManager();
base::flat_map<std::string, OpenIpcChannelCallback>
open_ipc_channel_callbacks_;
DISALLOW_COPY_AND_ASSIGN(PendingConnectionManager);
};
} // namespace drivefs
#endif // CHROMEOS_COMPONENTS_DRIVEFS_PENDING_CONNECTION_MANAGER_H_
// Copyright 2018 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 "chromeos/components/drivefs/pending_connection_manager.h"
#include "base/test/bind_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace drivefs {
class PendingConnectionManagerTest : public testing::Test {
protected:
PendingConnectionManager connection_manager_;
};
namespace {
TEST_F(PendingConnectionManagerTest, Basic) {
auto token = base::UnguessableToken::Create();
int callback_calls = 0;
connection_manager_.ExpectOpenIpcChannel(
token,
base::BindLambdaForTesting([&](base::ScopedFD fd) { callback_calls++; }));
EXPECT_TRUE(connection_manager_.OpenIpcChannel(token.ToString(), {}));
EXPECT_FALSE(connection_manager_.OpenIpcChannel(token.ToString(), {}));
EXPECT_EQ(1, callback_calls);
}
TEST_F(PendingConnectionManagerTest, Cancel) {
auto token = base::UnguessableToken::Create();
connection_manager_.ExpectOpenIpcChannel(
token, base::BindLambdaForTesting(
[&](base::ScopedFD fd) { FAIL() << "Unexpected call"; }));
connection_manager_.CancelExpectedOpenIpcChannel(token);
EXPECT_FALSE(connection_manager_.OpenIpcChannel(token.ToString(), {}));
}
TEST_F(PendingConnectionManagerTest, UnexpectedConnection) {
EXPECT_FALSE(connection_manager_.OpenIpcChannel("invalid", {}));
}
} // namespace
} // namespace drivefs
...@@ -5,8 +5,12 @@ ...@@ -5,8 +5,12 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/test/launcher/unit_test_launcher.h" #include "base/test/launcher/unit_test_launcher.h"
#include "base/test/test_suite.h" #include "base/test/test_suite.h"
#include "mojo/edk/embedder/embedder.h"
int main(int argc, char** argv) { int main(int argc, char** argv) {
// Some unit tests make Mojo calls.
mojo::edk::Init();
base::TestSuite test_suite(argc, argv); base::TestSuite test_suite(argc, argv);
return base::LaunchUnitTests( return base::LaunchUnitTests(
argc, argv, argc, argv,
......
...@@ -250,32 +250,29 @@ class CHROMEOS_EXPORT DiskMountManager { ...@@ -250,32 +250,29 @@ class CHROMEOS_EXPORT DiskMountManager {
typedef base::Callback<void(bool success)> EnsureMountInfoRefreshedCallback; typedef base::Callback<void(bool success)> EnsureMountInfoRefreshedCallback;
// Implement this interface to be notified about disk/mount related events. // Implement this interface to be notified about disk/mount related events.
// TODO(agawronska): Make observer methods non-pure virtual, because
// subclasses only use small subset of them.
class Observer { class Observer {
public: public:
virtual ~Observer() {} virtual ~Observer() {}
// Called when auto-mountable disk mount status is changed. // Called when auto-mountable disk mount status is changed.
virtual void OnAutoMountableDiskEvent(DiskEvent event, virtual void OnAutoMountableDiskEvent(DiskEvent event, const Disk& disk) {}
const Disk& disk) = 0;
// Called when fixed storage disk status is changed. // Called when fixed storage disk status is changed.
virtual void OnBootDeviceDiskEvent(DiskEvent event, const Disk& disk) = 0; virtual void OnBootDeviceDiskEvent(DiskEvent event, const Disk& disk) {}
// Called when device status is changed. // Called when device status is changed.
virtual void OnDeviceEvent(DeviceEvent event, virtual void OnDeviceEvent(DeviceEvent event,
const std::string& device_path) = 0; const std::string& device_path) {}
// Called after a mount point has been mounted or unmounted. // Called after a mount point has been mounted or unmounted.
virtual void OnMountEvent(MountEvent event, virtual void OnMountEvent(MountEvent event,
MountError error_code, MountError error_code,
const MountPointInfo& mount_info) = 0; const MountPointInfo& mount_info) {}
// Called on format process events. // Called on format process events.
virtual void OnFormatEvent(FormatEvent event, virtual void OnFormatEvent(FormatEvent event,
FormatError error_code, FormatError error_code,
const std::string& device_path) = 0; const std::string& device_path) {}
// Called on rename process events. // Called on rename process events.
virtual void OnRenameEvent(RenameEvent event, virtual void OnRenameEvent(RenameEvent event,
RenameError error_code, RenameError error_code,
const std::string& device_path) = 0; const std::string& device_path) {}
}; };
virtual ~DiskMountManager() {} virtual ~DiskMountManager() {}
......
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