Commit 5f0fdfe5 authored by Kyle Horimoto's avatar Kyle Horimoto Committed by Commit Bot

[CrOS MultiDevice] Create HostBackendDelegate.

This class serves as the source of truth for which device is the host
for the logged-in account; additionally, it provides the ability to set
the host on the back-end.

If an attempt fails, it is retried every 5 minutes until it either
succeeds or until AttemptToSetMultiDeviceHostOnBackend() is called with
a new device.

Bug: 824568
Change-Id: I61239ac85b84d57a53b259bc05209aee751f7cee
Reviewed-on: https://chromium-review.googlesource.com/1112838
Commit-Queue: Kyle Horimoto <khorimoto@chromium.org>
Commit-Queue: Ryan Hansberry <hansberry@chromium.org>
Reviewed-by: default avatarRyan Hansberry <hansberry@chromium.org>
Cr-Commit-Position: refs/heads/master@{#570111}
parent 7f112aaa
...@@ -14,6 +14,10 @@ static_library("multidevice_setup") { ...@@ -14,6 +14,10 @@ static_library("multidevice_setup") {
"account_status_change_delegate_notifier.h", "account_status_change_delegate_notifier.h",
"account_status_change_delegate_notifier_impl.cc", "account_status_change_delegate_notifier_impl.cc",
"account_status_change_delegate_notifier_impl.h", "account_status_change_delegate_notifier_impl.h",
"host_backend_delegate.cc",
"host_backend_delegate.h",
"host_backend_delegate_impl.cc",
"host_backend_delegate_impl.h",
"multidevice_setup_base.cc", "multidevice_setup_base.cc",
"multidevice_setup_base.h", "multidevice_setup_base.h",
"multidevice_setup_impl.cc", "multidevice_setup_impl.cc",
...@@ -54,6 +58,8 @@ static_library("test_support") { ...@@ -54,6 +58,8 @@ static_library("test_support") {
"fake_account_status_change_delegate.cc", "fake_account_status_change_delegate.cc",
"fake_account_status_change_delegate.h", "fake_account_status_change_delegate.h",
"fake_account_status_change_delegate_notifier.h", "fake_account_status_change_delegate_notifier.h",
"fake_host_backend_delegate.cc",
"fake_host_backend_delegate.h",
"fake_multidevice_setup.cc", "fake_multidevice_setup.cc",
"fake_multidevice_setup.h", "fake_multidevice_setup.h",
"fake_setup_flow_completion_recorder.cc", "fake_setup_flow_completion_recorder.cc",
...@@ -64,6 +70,7 @@ static_library("test_support") { ...@@ -64,6 +70,7 @@ static_library("test_support") {
":multidevice_setup", ":multidevice_setup",
"//base", "//base",
"//chromeos/services/multidevice_setup/public/mojom", "//chromeos/services/multidevice_setup/public/mojom",
"//components/cryptauth",
"//testing/gmock", "//testing/gmock",
"//testing/gtest", "//testing/gtest",
] ]
...@@ -74,6 +81,7 @@ source_set("unit_tests") { ...@@ -74,6 +81,7 @@ source_set("unit_tests") {
sources = [ sources = [
"account_status_change_delegate_notifier_impl_unittest.cc", "account_status_change_delegate_notifier_impl_unittest.cc",
"host_backend_delegate_impl_unittest.cc",
"multidevice_setup_impl_unittest.cc", "multidevice_setup_impl_unittest.cc",
"multidevice_setup_service_unittest.cc", "multidevice_setup_service_unittest.cc",
"setup_flow_completion_recorder_impl_unittest.cc", "setup_flow_completion_recorder_impl_unittest.cc",
......
// 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/services/multidevice_setup/fake_host_backend_delegate.h"
#include "base/callback.h"
namespace chromeos {
namespace multidevice_setup {
FakeHostBackendDelegate::FakeHostBackendDelegate() : HostBackendDelegate() {}
FakeHostBackendDelegate::~FakeHostBackendDelegate() = default;
void FakeHostBackendDelegate::NotifyHostChangedOnBackend(
const base::Optional<cryptauth::RemoteDeviceRef>& host_device_on_backend) {
host_device_on_backend_ = host_device_on_backend;
if (pending_host_request_ && *pending_host_request_ == host_device_on_backend)
pending_host_request_.reset();
HostBackendDelegate::NotifyHostChangedOnBackend();
}
void FakeHostBackendDelegate::NotifyBackendRequestFailed() {
// A request must be active in order for a back-end request to fail.
DCHECK(pending_host_request_);
HostBackendDelegate::NotifyBackendRequestFailed();
}
void FakeHostBackendDelegate::AttemptToSetMultiDeviceHostOnBackend(
const base::Optional<cryptauth::RemoteDeviceRef>& host_device) {
if (host_device_on_backend_ == host_device) {
pending_host_request_.reset();
return;
}
*pending_host_request_ = host_device;
}
bool FakeHostBackendDelegate::HasPendingHostRequest() {
return pending_host_request_ != base::nullopt;
}
base::Optional<cryptauth::RemoteDeviceRef>
FakeHostBackendDelegate::GetPendingHostRequest() const {
return *pending_host_request_;
}
base::Optional<cryptauth::RemoteDeviceRef>
FakeHostBackendDelegate::GetMultiDeviceHostFromBackend() const {
return host_device_on_backend_;
}
FakeHostBackendDelegateObserver::FakeHostBackendDelegateObserver() = default;
FakeHostBackendDelegateObserver::~FakeHostBackendDelegateObserver() = default;
void FakeHostBackendDelegateObserver::OnHostChangedOnBackend() {
++num_changes_on_backend_;
}
void FakeHostBackendDelegateObserver::OnBackendRequestFailed() {
++num_failed_backend_requests_;
}
void FakeHostBackendDelegateObserver::OnPendingHostRequestChange() {
++num_pending_host_request_changes_;
}
} // namespace multidevice_setup
} // namespace chromeos
// 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_SERVICES_MULTIDEVICE_SETUP_FAKE_HOST_BACKEND_DELEGATE_H_
#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FAKE_HOST_BACKEND_DELEGATE_H_
#include <utility>
#include <vector>
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/optional.h"
#include "chromeos/services/multidevice_setup/host_backend_delegate.h"
#include "components/cryptauth/remote_device_ref.h"
namespace chromeos {
namespace multidevice_setup {
// Test HostBackendDelegate implementation.
class FakeHostBackendDelegate : public HostBackendDelegate {
public:
FakeHostBackendDelegate();
~FakeHostBackendDelegate() override;
// Changes the backend host to |host_device_on_backend| and notifies
// observers.
void NotifyHostChangedOnBackend(
const base::Optional<cryptauth::RemoteDeviceRef>& host_device_on_backend);
void NotifyBackendRequestFailed();
private:
// HostBackendDelegate:
void AttemptToSetMultiDeviceHostOnBackend(
const base::Optional<cryptauth::RemoteDeviceRef>& host_device) override;
bool HasPendingHostRequest() override;
base::Optional<cryptauth::RemoteDeviceRef> GetPendingHostRequest()
const override;
base::Optional<cryptauth::RemoteDeviceRef> GetMultiDeviceHostFromBackend()
const override;
base::Optional<base::Optional<cryptauth::RemoteDeviceRef>>
pending_host_request_;
base::Optional<cryptauth::RemoteDeviceRef> host_device_on_backend_;
DISALLOW_COPY_AND_ASSIGN(FakeHostBackendDelegate);
};
// Test HostBackendDelegate::Observer implementation.
class FakeHostBackendDelegateObserver : public HostBackendDelegate::Observer {
public:
FakeHostBackendDelegateObserver();
~FakeHostBackendDelegateObserver() override;
size_t num_changes_on_backend() const { return num_changes_on_backend_; }
size_t num_failed_backend_requests() const {
return num_failed_backend_requests_;
}
size_t num_pending_host_request_changes() const {
return num_pending_host_request_changes_;
}
private:
// HostBackendDelegate::Observer:
void OnHostChangedOnBackend() override;
void OnBackendRequestFailed() override;
void OnPendingHostRequestChange() override;
size_t num_changes_on_backend_ = 0u;
size_t num_failed_backend_requests_ = 0u;
size_t num_pending_host_request_changes_ = 0u;
DISALLOW_COPY_AND_ASSIGN(FakeHostBackendDelegateObserver);
};
} // namespace multidevice_setup
} // namespace chromeos
#endif // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FAKE_HOST_BACKEND_DELEGATE_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/services/multidevice_setup/host_backend_delegate.h"
namespace chromeos {
namespace multidevice_setup {
HostBackendDelegate::HostBackendDelegate() = default;
HostBackendDelegate::~HostBackendDelegate() = default;
void HostBackendDelegate::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
void HostBackendDelegate::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
void HostBackendDelegate::NotifyHostChangedOnBackend() {
for (auto& observer : observer_list_)
observer.OnHostChangedOnBackend();
}
void HostBackendDelegate::NotifyBackendRequestFailed() {
for (auto& observer : observer_list_)
observer.OnBackendRequestFailed();
}
void HostBackendDelegate::NotifyPendingHostRequestChange() {
for (auto& observer : observer_list_)
observer.OnPendingHostRequestChange();
}
} // namespace multidevice_setup
} // namespace chromeos
// 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_SERVICES_MULTIDEVICE_SETUP_HOST_BACKEND_DELEGATE_H_
#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_BACKEND_DELEGATE_H_
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/optional.h"
#include "components/cryptauth/remote_device_ref.h"
namespace chromeos {
namespace multidevice_setup {
// Delegate for setting and receiving the MultiDevice host from the back-end.
// This class is considered the source of truth for the most recent snapshot of
// what the server knows about.
class HostBackendDelegate {
public:
class Observer {
public:
virtual ~Observer() = default;
// Invoked when the host has changed. The new host can be retrieved via
// GetMultiDeviceHostFromBackend().
//
// Note that this function is invoked when the host changes from one device
// to another, from a device to no device at all, or from no device at all
// to a device. The function is not invoked when an individual property of
// the host device changes (i.e., this function is only invoked when the
// previous host's device ID is different fro the new host's device ID).
virtual void OnHostChangedOnBackend() {}
// Invoked when an attempt to set the MultiDevice host failed. The device
// whose attempt failed can be retrieved via GetPendingHostRequest().
virtual void OnBackendRequestFailed() {}
// Invoked when the pending host request has changed. Note that this
// callback is also fired when a HasPendingHostRequest() changes from true
// to false.
virtual void OnPendingHostRequestChange() {}
};
virtual ~HostBackendDelegate();
// Attempts to set |host_device| as the host on the back-end. If |host_device|
// is null, this function attempts to remove the active host device.
//
// If the request is successful, the OnHostChangedOnBackend() observer
// function is invoked.
//
// If a the request fails (e.g., the device is offline or the server is down),
// the OnBackendRequestFailed() observer function is invoked, but this
// object continues to attempt the request until the request succeeds or until
// AttemptToSetMultiDeviceHostOnBackend() is called with a new device.
virtual void AttemptToSetMultiDeviceHostOnBackend(
const base::Optional<cryptauth::RemoteDeviceRef>& host_device) = 0;
// Returns whether there is a pending request to set the host on the back-end
// which has not yet completed.
virtual bool HasPendingHostRequest() = 0;
// Returns the device which is pending to be set as the host device. If null
// is returned, this means that the current host is pending removal.
//
// This function invokes a crash if called when HasPendingHostRequest()
// returns false.
virtual base::Optional<cryptauth::RemoteDeviceRef> GetPendingHostRequest()
const = 0;
// Provides the host from the most recent device sync. If the return value is
// null, there is no host set on the back-end.
virtual base::Optional<cryptauth::RemoteDeviceRef>
GetMultiDeviceHostFromBackend() const = 0;
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
protected:
HostBackendDelegate();
void NotifyHostChangedOnBackend();
void NotifyBackendRequestFailed();
void NotifyPendingHostRequestChange();
private:
base::ObserverList<Observer> observer_list_;
DISALLOW_COPY_AND_ASSIGN(HostBackendDelegate);
};
} // namespace multidevice_setup
} // namespace chromeos
#endif // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_BACKEND_DELEGATE_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/services/multidevice_setup/host_backend_delegate_impl.h"
#include <algorithm>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "chromeos/components/proximity_auth/logging/logging.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
namespace chromeos {
namespace multidevice_setup {
namespace {
// Name of the pref which stores the device ID of the host which is pending
// being set on the back-end.
const char kPendingRequestHostIdPrefName[] =
"multidevice_setup.pending_request_host_id";
// String to use for the pending host ID preference entry when the pending
// request is to remove the current host.
const char kPendingRemovalOfCurrentHost[] = "pendingRemovalOfCurrentHost";
// String to use for the pending host ID when there is no pending request.
const char kNoPendingRequest[] = "";
// The number of minutes to wait before retrying a failed attempt.
const int kNumMinutesBetweenRetries = 5;
const char kNoHostForLogging[] = "[no host]";
} // namespace
// static
HostBackendDelegateImpl::Factory*
HostBackendDelegateImpl::Factory::test_factory_ = nullptr;
// static
HostBackendDelegateImpl::Factory* HostBackendDelegateImpl::Factory::Get() {
if (test_factory_)
return test_factory_;
static base::NoDestructor<Factory> factory;
return factory.get();
}
// static
void HostBackendDelegateImpl::Factory::SetFactoryForTesting(
Factory* test_factory) {
test_factory_ = test_factory;
}
HostBackendDelegateImpl::Factory::~Factory() = default;
std::unique_ptr<HostBackendDelegate>
HostBackendDelegateImpl::Factory::BuildInstance(
PrefService* pref_service,
device_sync::DeviceSyncClient* device_sync_client,
std::unique_ptr<base::OneShotTimer> timer) {
return base::WrapUnique(new HostBackendDelegateImpl(
pref_service, device_sync_client, std::move(timer)));
}
// static
void HostBackendDelegateImpl::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterStringPref(kPendingRequestHostIdPrefName,
kNoPendingRequest);
}
HostBackendDelegateImpl::HostBackendDelegateImpl(
PrefService* pref_service,
device_sync::DeviceSyncClient* device_sync_client,
std::unique_ptr<base::OneShotTimer> timer)
: HostBackendDelegate(),
pref_service_(pref_service),
device_sync_client_(device_sync_client),
timer_(std::move(timer)),
weak_ptr_factory_(this) {
device_sync_client_->AddObserver(this);
host_from_last_sync_ = GetHostFromDeviceSync();
if (HasPendingHostRequest())
AttemptNetworkRequest(false /* is_retry */);
}
HostBackendDelegateImpl::~HostBackendDelegateImpl() {
device_sync_client_->RemoveObserver(this);
}
void HostBackendDelegateImpl::AttemptToSetMultiDeviceHostOnBackend(
const base::Optional<cryptauth::RemoteDeviceRef>& host_device) {
// If the device on the back-end is already |host_device|, there no longer
// needs to be a pending request.
if (host_from_last_sync_ == host_device) {
SetPendingHostRequest(kNoPendingRequest);
return;
}
if (host_device)
SetPendingHostRequest(host_device->GetDeviceId());
else
SetPendingHostRequest(kPendingRemovalOfCurrentHost);
AttemptNetworkRequest(false /* is_retry */);
}
bool HostBackendDelegateImpl::HasPendingHostRequest() {
const std::string pending_host_id_from_prefs =
pref_service_->GetString(kPendingRequestHostIdPrefName);
if (pending_host_id_from_prefs == kNoPendingRequest)
return false;
if (pending_host_id_from_prefs == kPendingRemovalOfCurrentHost) {
// If the pending request is to remove the current host but there is no
// current host, the host was removed by another device while this device
// was offline.
if (!host_from_last_sync_) {
SetPendingHostRequest(kNoPendingRequest);
return false;
}
// Otherwise, there still is a pending request to remove the current host.
return true;
}
// By this point, |pending_host_id_from_prefs| refers to a real device ID and
// not one of the two sentinel values.
for (const auto& remote_device : device_sync_client_->GetSyncedDevices()) {
if (pending_host_id_from_prefs == remote_device.GetDeviceId())
return true;
}
// If a request was pending for a specific host device, but that device is no
// longer present on the user's account, there is no longer a pending request.
SetPendingHostRequest(kNoPendingRequest);
return false;
}
base::Optional<cryptauth::RemoteDeviceRef>
HostBackendDelegateImpl::GetPendingHostRequest() const {
const std::string pending_host_id_from_prefs =
pref_service_->GetString(kPendingRequestHostIdPrefName);
if (pending_host_id_from_prefs == kNoPendingRequest) {
PA_LOG(ERROR) << "HostBackendDelegateImpl::GetPendingHostRequest(): Tried "
<< "to get pending host request, but there was no pending "
<< "host request.";
NOTREACHED();
}
if (pending_host_id_from_prefs == kPendingRemovalOfCurrentHost)
return base::nullopt;
for (const auto& remote_device : device_sync_client_->GetSyncedDevices()) {
if (pending_host_id_from_prefs == remote_device.GetDeviceId())
return remote_device;
}
PA_LOG(ERROR) << "HostBackendDelegateImpl::GetPendingHostRequest(): Tried to "
<< "get pending host request, but the pending host ID was not "
<< "present.";
NOTREACHED();
return base::nullopt;
}
base::Optional<cryptauth::RemoteDeviceRef>
HostBackendDelegateImpl::GetMultiDeviceHostFromBackend() const {
return host_from_last_sync_;
}
void HostBackendDelegateImpl::SetPendingHostRequest(
const std::string& pending_host_id) {
const std::string host_id_from_prefs_before_call =
pref_service_->GetString(kPendingRequestHostIdPrefName);
if (pending_host_id == host_id_from_prefs_before_call)
return;
pref_service_->SetString(kPendingRequestHostIdPrefName, pending_host_id);
timer_->Stop();
NotifyPendingHostRequestChange();
}
void HostBackendDelegateImpl::AttemptNetworkRequest(bool is_retry) {
if (!HasPendingHostRequest()) {
PA_LOG(ERROR) << "HostBackendDelegateImpl::AttemptNetworkRequest(): Tried "
<< "to attempt a network request, but there was no pending "
<< "host request.";
NOTREACHED();
}
base::Optional<cryptauth::RemoteDeviceRef> pending_host_request =
GetPendingHostRequest();
// If |pending_host_request| is non-null, the request should be to set that
// device. If it is null, the pending request is to remove the current host.
cryptauth::RemoteDeviceRef device_to_set =
pending_host_request ? *pending_host_request : *host_from_last_sync_;
// Likewise, if |pending_host_request| is non-null, that device should be
// enabled, and if it is null, the old device should be disabled.
bool should_enable = pending_host_request != base::nullopt;
PA_LOG(INFO) << "HostBackendDelegateImpl::AttemptNetworkRequest(): "
<< (is_retry ? "Retrying attempt" : "Attempting") << " to "
<< (should_enable ? "enable" : "disable") << " the host with ID "
<< device_to_set.GetTruncatedDeviceIdForLogs() << ".";
device_sync_client_->SetSoftwareFeatureState(
device_to_set.public_key(),
cryptauth::SoftwareFeature::BETTER_TOGETHER_HOST,
should_enable /* enabled */, should_enable /* is_exclusive */,
base::BindOnce(&HostBackendDelegateImpl::OnSetSoftwareFeatureStateResult,
weak_ptr_factory_.GetWeakPtr(), device_to_set,
should_enable));
}
void HostBackendDelegateImpl::OnNewDevicesSynced() {
base::Optional<cryptauth::RemoteDeviceRef> host_from_sync =
GetHostFromDeviceSync();
if (host_from_last_sync_ == host_from_sync)
return;
std::string old_host_id =
host_from_last_sync_ ? host_from_last_sync_->GetTruncatedDeviceIdForLogs()
: kNoHostForLogging;
std::string new_host_id = host_from_sync
? host_from_sync->GetTruncatedDeviceIdForLogs()
: kNoHostForLogging;
host_from_last_sync_ = host_from_sync;
PA_LOG(INFO) << "HostBackendDelegateImpl::OnNewDevicesSynced(): New host "
<< "device has been set. Old host device ID: " << old_host_id
<< ", New host device ID: " << new_host_id;
// If there is a pending request and the new host fulfills that pending
// request, there is no longer a pending request.
if (HasPendingHostRequest() &&
host_from_last_sync_ == GetPendingHostRequest()) {
SetPendingHostRequest(kNoPendingRequest);
}
NotifyHostChangedOnBackend();
}
base::Optional<cryptauth::RemoteDeviceRef>
HostBackendDelegateImpl::GetHostFromDeviceSync() {
cryptauth::RemoteDeviceRefList synced_devices =
device_sync_client_->GetSyncedDevices();
auto it = std::find_if(
synced_devices.begin(), synced_devices.end(),
[](const auto& remote_device) {
cryptauth::SoftwareFeatureState host_state =
remote_device.GetSoftwareFeatureState(
cryptauth::SoftwareFeature::BETTER_TOGETHER_HOST);
return host_state == cryptauth::SoftwareFeatureState::kEnabled;
});
if (it == synced_devices.end())
return base::nullopt;
return *it;
}
void HostBackendDelegateImpl::OnSetSoftwareFeatureStateResult(
cryptauth::RemoteDeviceRef device_for_request,
bool attempted_to_enable,
const base::Optional<std::string>& error_code) {
if (!error_code)
return;
PA_LOG(WARNING) << "HostBackendDelegateImpl::"
<< "OnSetSoftwareFeatureStateResult(): Failure requesting "
<< "a host change. Device ID: "
<< device_for_request.GetTruncatedDeviceIdForLogs()
<< ", Attempted to enable: "
<< (attempted_to_enable ? "true" : "false")
<< ", Error code: " << *error_code;
if (!HasPendingHostRequest())
return;
base::Optional<cryptauth::RemoteDeviceRef> pending_host_request =
GetPendingHostRequest();
bool failed_request_was_to_set_pending_host =
attempted_to_enable && pending_host_request &&
*pending_host_request == device_for_request;
bool failed_request_was_to_remove_pending_host =
!attempted_to_enable && !pending_host_request &&
device_for_request == host_from_last_sync_;
// If the request which failed corresponds to the most recent call to
// AttemptToSetMultiDeviceHostOnBackend(), alert observers that this request
// failed and schedule a retry.
if (failed_request_was_to_set_pending_host ||
failed_request_was_to_remove_pending_host) {
NotifyBackendRequestFailed();
timer_->Start(FROM_HERE,
base::TimeDelta::FromMinutes(kNumMinutesBetweenRetries),
base::Bind(&HostBackendDelegateImpl::AttemptNetworkRequest,
base::Unretained(this), true /* is_retry */));
}
}
} // namespace multidevice_setup
} // namespace chromeos
// 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_SERVICES_MULTIDEVICE_SETUP_HOST_BACKEND_DELEGATE_IMPL_H_
#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_BACKEND_DELEGATE_IMPL_H_
#include "base/callback_forward.h"
#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
#include "chromeos/services/multidevice_setup/host_backend_delegate.h"
#include "components/cryptauth/remote_device_ref.h"
class PrefRegistrySimple;
class PrefService;
namespace chromeos {
namespace multidevice_setup {
// Concrete HostBackendDelegate implementation, which utilizes
// DeviceSyncClient to communicate with the back-end.
class HostBackendDelegateImpl : public HostBackendDelegate,
public device_sync::DeviceSyncClient::Observer {
public:
class Factory {
public:
static Factory* Get();
static void SetFactoryForTesting(Factory* test_factory);
virtual ~Factory();
virtual std::unique_ptr<HostBackendDelegate> BuildInstance(
PrefService* pref_service,
device_sync::DeviceSyncClient* device_sync_client,
std::unique_ptr<base::OneShotTimer> timer =
std::make_unique<base::OneShotTimer>());
private:
static Factory* test_factory_;
};
static void RegisterPrefs(PrefRegistrySimple* registry);
~HostBackendDelegateImpl() override;
private:
HostBackendDelegateImpl(PrefService* pref_service,
device_sync::DeviceSyncClient* device_sync_client,
std::unique_ptr<base::OneShotTimer> timer);
// HostBackendDelegate:
void AttemptToSetMultiDeviceHostOnBackend(
const base::Optional<cryptauth::RemoteDeviceRef>& host_device) override;
bool HasPendingHostRequest() override;
base::Optional<cryptauth::RemoteDeviceRef> GetPendingHostRequest()
const override;
base::Optional<cryptauth::RemoteDeviceRef> GetMultiDeviceHostFromBackend()
const override;
// DeviceSyncClient::Observer:
void OnNewDevicesSynced() override;
// Sets the pending host request. To signal that the request is to remove the
// current host, pass kPendingRemovalOfCurrentHost. To signal that there is no
// pending request, pass kNoPendingRequest.
void SetPendingHostRequest(const std::string& host_device_id);
void AttemptNetworkRequest(bool is_retry);
base::Optional<cryptauth::RemoteDeviceRef> GetHostFromDeviceSync();
void OnSetSoftwareFeatureStateResult(
cryptauth::RemoteDeviceRef device_for_request,
bool attempted_to_enable,
const base::Optional<std::string>& error_code);
PrefService* pref_service_;
device_sync::DeviceSyncClient* device_sync_client_;
std::unique_ptr<base::OneShotTimer> timer_;
// The most-recent snapshot of the host on the back-end.
base::Optional<cryptauth::RemoteDeviceRef> host_from_last_sync_;
base::WeakPtrFactory<HostBackendDelegateImpl> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(HostBackendDelegateImpl);
};
} // namespace multidevice_setup
} // namespace chromeos
#endif // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_BACKEND_DELEGATE_IMPL_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/services/multidevice_setup/host_backend_delegate_impl.h"
#include <memory>
#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/timer/mock_timer.h"
#include "base/unguessable_token.h"
#include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h"
#include "chromeos/services/multidevice_setup/fake_host_backend_delegate.h"
#include "components/cryptauth/remote_device_test_util.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace multidevice_setup {
namespace {
const char kPendingRequestHostIdPrefName[] =
"multidevice_setup.pending_request_host_id";
const char kPendingRemovalOfCurrentHost[] = "pendingRemovalOfCurrentHost";
const char kNoPendingRequest[] = "";
const size_t kNumTestDevices = 5;
} // namespace
class MultiDeviceSetupHostBackendDelegateImplTest : public testing::Test {
protected:
MultiDeviceSetupHostBackendDelegateImplTest()
: test_devices_(
cryptauth::CreateRemoteDeviceRefListForTest(kNumTestDevices)) {}
~MultiDeviceSetupHostBackendDelegateImplTest() override = default;
// testing::Test:
void SetUp() override {
test_pref_service_ =
std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
HostBackendDelegateImpl::RegisterPrefs(test_pref_service_->registry());
fake_device_sync_client_ =
std::make_unique<device_sync::FakeDeviceSyncClient>();
fake_device_sync_client_->set_synced_devices(test_devices_);
}
void TearDown() override { delegate_->RemoveObserver(observer_.get()); }
void CreateDelegate(
const base::Optional<cryptauth::RemoteDeviceRef>& initial_host,
const std::string& initial_pending_host_request = kNoPendingRequest) {
SetHostInDeviceSyncClient(initial_host);
test_pref_service_->SetString(kPendingRequestHostIdPrefName,
initial_pending_host_request);
auto mock_timer = std::make_unique<base::MockOneShotTimer>();
mock_timer_ = mock_timer.get();
delegate_ = HostBackendDelegateImpl::Factory::Get()->BuildInstance(
test_pref_service_.get(), fake_device_sync_client_.get(),
std::move(mock_timer));
EXPECT_EQ(initial_host, delegate_->GetMultiDeviceHostFromBackend());
observer_ = std::make_unique<FakeHostBackendDelegateObserver>();
delegate_->AddObserver(observer_.get());
}
void InvokePendingSetSoftwareFeatureStateCallback(
const base::Optional<std::string>& error_code,
bool expected_to_notify_observer_and_start_retry_timer) {
size_t num_failure_events_before_call =
observer_->num_failed_backend_requests();
fake_device_sync_client_->InvokePendingSetSoftwareFeatureStateCallback(
error_code);
if (expected_to_notify_observer_and_start_retry_timer) {
EXPECT_EQ(num_failure_events_before_call + 1u,
observer_->num_failed_backend_requests());
} else {
EXPECT_EQ(num_failure_events_before_call,
observer_->num_failed_backend_requests());
}
EXPECT_EQ(expected_to_notify_observer_and_start_retry_timer,
mock_timer_->IsRunning());
}
void SimulateNewHostDevicesSynced(
const base::Optional<cryptauth::RemoteDeviceRef>& host_device_after_sync,
bool expected_to_fulfill_pending_request) {
base::Optional<cryptauth::RemoteDeviceRef> host_device_before_call =
delegate_->GetMultiDeviceHostFromBackend();
bool host_changed = host_device_before_call != host_device_after_sync;
size_t num_host_change_events_before_call =
observer_->num_changes_on_backend();
size_t num_pending_host_request_change_events_before_call =
observer_->num_pending_host_request_changes();
SetHostInDeviceSyncClient(host_device_after_sync);
fake_device_sync_client_->NotifyNewDevicesSynced();
if (host_changed) {
EXPECT_EQ(num_host_change_events_before_call + 1u,
observer_->num_changes_on_backend());
} else {
EXPECT_EQ(num_host_change_events_before_call,
observer_->num_changes_on_backend());
}
if (expected_to_fulfill_pending_request) {
EXPECT_FALSE(delegate_->HasPendingHostRequest());
// Expected to change from a pending request to no request.
EXPECT_EQ(num_pending_host_request_change_events_before_call + 1u,
observer_->num_pending_host_request_changes());
} else {
EXPECT_EQ(num_pending_host_request_change_events_before_call,
observer_->num_pending_host_request_changes());
}
}
void AttemptToSetMultiDeviceHostOnBackend(
const base::Optional<cryptauth::RemoteDeviceRef>& host_device) {
bool attempting_to_set_host_which_already_exists =
host_device == delegate_->GetMultiDeviceHostFromBackend();
size_t num_pending_host_request_change_events_before_call =
observer_->num_pending_host_request_changes();
bool was_request_for_same_device_as_pending_request =
delegate_->HasPendingHostRequest() &&
delegate_->GetPendingHostRequest() == host_device;
delegate_->AttemptToSetMultiDeviceHostOnBackend(host_device);
// A new attempt means that any previous retry attempts should have been
// canceled.
EXPECT_FALSE(mock_timer_->IsRunning());
if (attempting_to_set_host_which_already_exists) {
EXPECT_FALSE(delegate_->HasPendingHostRequest());
return;
}
EXPECT_EQ(host_device, delegate_->GetPendingHostRequest());
if (was_request_for_same_device_as_pending_request) {
EXPECT_EQ(num_pending_host_request_change_events_before_call,
observer_->num_pending_host_request_changes());
} else {
EXPECT_EQ(num_pending_host_request_change_events_before_call + 1u,
observer_->num_pending_host_request_changes());
}
// TODO(khorimoto): Check that the parameters passed to
// |fake_device_sync_client_| are correct. Currently, FakeDeviceSyncClient
// does provide a mechanism for checking these parameters.
}
void SetHostInDeviceSyncClient(
const base::Optional<cryptauth::RemoteDeviceRef>& host_device) {
for (const auto& remote_device : test_devices_) {
bool should_be_host =
host_device != base::nullopt &&
host_device->GetDeviceId() == remote_device.GetDeviceId();
GetMutableRemoteDevice(remote_device)
->software_features
[cryptauth::SoftwareFeature::BETTER_TOGETHER_HOST] =
should_be_host ? cryptauth::SoftwareFeatureState::kEnabled
: cryptauth::SoftwareFeatureState::kSupported;
}
}
device_sync::FakeDeviceSyncClient* fake_device_sync_client() {
return fake_device_sync_client_.get();
}
FakeHostBackendDelegateObserver* observer() { return observer_.get(); }
base::MockOneShotTimer* mock_timer() { return mock_timer_; }
HostBackendDelegate* delegate() { return delegate_.get(); }
const cryptauth::RemoteDeviceRefList& test_devices() const {
return test_devices_;
}
private:
cryptauth::RemoteDeviceRefList test_devices_;
std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
test_pref_service_;
std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_;
base::MockOneShotTimer* mock_timer_;
std::unique_ptr<FakeHostBackendDelegateObserver> observer_;
std::unique_ptr<HostBackendDelegate> delegate_;
DISALLOW_COPY_AND_ASSIGN(MultiDeviceSetupHostBackendDelegateImplTest);
};
TEST_F(MultiDeviceSetupHostBackendDelegateImplTest, Success) {
CreateDelegate(base::nullopt /* initial_host */);
// Set device 0.
AttemptToSetMultiDeviceHostOnBackend(test_devices()[0]);
InvokePendingSetSoftwareFeatureStateCallback(
base::nullopt /* error_code */,
false /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_TRUE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[0], delegate()->GetPendingHostRequest());
SimulateNewHostDevicesSynced(test_devices()[0] /* host_device_after_sync */,
true /* expected_to_fulfill_pending_request */);
EXPECT_FALSE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[0], delegate()->GetMultiDeviceHostFromBackend());
// Remove device 0 such that there is no longer a host..
AttemptToSetMultiDeviceHostOnBackend(base::nullopt);
InvokePendingSetSoftwareFeatureStateCallback(
base::nullopt /* error_code */,
false /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_TRUE(delegate()->HasPendingHostRequest());
EXPECT_EQ(base::nullopt, delegate()->GetPendingHostRequest());
SimulateNewHostDevicesSynced(base::nullopt /* host_device_after_sync */,
true /* expected_to_fulfill_pending_request */);
EXPECT_FALSE(delegate()->HasPendingHostRequest());
EXPECT_EQ(base::nullopt, delegate()->GetMultiDeviceHostFromBackend());
// Set device 1.
AttemptToSetMultiDeviceHostOnBackend(test_devices()[1]);
InvokePendingSetSoftwareFeatureStateCallback(
base::nullopt /* error_code */,
false /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_TRUE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[1], delegate()->GetPendingHostRequest());
SimulateNewHostDevicesSynced(test_devices()[1] /* host_device_after_sync */,
true /* expected_to_fulfill_pending_request */);
EXPECT_FALSE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[1], delegate()->GetMultiDeviceHostFromBackend());
}
TEST_F(MultiDeviceSetupHostBackendDelegateImplTest, Failure) {
CreateDelegate(base::nullopt /* initial_host */);
// Attempt to set device 0, but fail.
AttemptToSetMultiDeviceHostOnBackend(test_devices()[0]);
InvokePendingSetSoftwareFeatureStateCallback(
"errorCode1" /* error_code */,
true /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_TRUE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[0], delegate()->GetPendingHostRequest());
EXPECT_EQ(base::nullopt, delegate()->GetMultiDeviceHostFromBackend());
// A retry should have been scheduled, so fire the timer to start the retry.
mock_timer()->Fire();
// Simulate another failure.
InvokePendingSetSoftwareFeatureStateCallback(
"errorCode2" /* error_code */,
true /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_TRUE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[0], delegate()->GetPendingHostRequest());
EXPECT_EQ(base::nullopt, delegate()->GetMultiDeviceHostFromBackend());
// Attempt to set device 1, but fail.
AttemptToSetMultiDeviceHostOnBackend(test_devices()[1]);
InvokePendingSetSoftwareFeatureStateCallback(
"errorCode3" /* error_code */,
true /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_TRUE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[1], delegate()->GetPendingHostRequest());
EXPECT_EQ(base::nullopt, delegate()->GetMultiDeviceHostFromBackend());
}
TEST_F(MultiDeviceSetupHostBackendDelegateImplTest,
StartWithDevice_SimultaneousRequests) {
// Start with device 0 as the active host.
CreateDelegate(test_devices()[0] /* initial_host */);
// Attempt to set device 1, but do not invoke the callback yet.
AttemptToSetMultiDeviceHostOnBackend(test_devices()[1]);
EXPECT_TRUE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[1], delegate()->GetPendingHostRequest());
EXPECT_EQ(test_devices()[0], delegate()->GetMultiDeviceHostFromBackend());
// Attempt to set device 2, but do not invoke device 1's callback yet.
AttemptToSetMultiDeviceHostOnBackend(test_devices()[2]);
EXPECT_TRUE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[2], delegate()->GetPendingHostRequest());
EXPECT_EQ(test_devices()[0], delegate()->GetMultiDeviceHostFromBackend());
// Attempt to set device 3.
AttemptToSetMultiDeviceHostOnBackend(test_devices()[3]);
EXPECT_TRUE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[3], delegate()->GetPendingHostRequest());
EXPECT_EQ(test_devices()[0], delegate()->GetMultiDeviceHostFromBackend());
// Fire the callback for device 1, but have it fail. This is not expected to
// notify the observer or start the retry timer, since the failure was for
// device 1's request and device 3 is the pending host request.
InvokePendingSetSoftwareFeatureStateCallback(
"errorCode" /* error_code */,
false /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_TRUE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[3], delegate()->GetPendingHostRequest());
EXPECT_EQ(test_devices()[0], delegate()->GetMultiDeviceHostFromBackend());
// Fire the callback for device 2, and have it succeed. This should affect the
// value of GetMultiDeviceHostFromBackend(), but there should still be a
// pending request for device 3.
InvokePendingSetSoftwareFeatureStateCallback(
base::nullopt /* error_code */,
false /* expected_to_notify_observer_and_start_retry_timer */);
SimulateNewHostDevicesSynced(test_devices()[2] /* host_device_after_sync */,
false /* expected_to_fulfill_pending_request */);
EXPECT_TRUE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[3], delegate()->GetPendingHostRequest());
EXPECT_EQ(test_devices()[2], delegate()->GetMultiDeviceHostFromBackend());
// Fire the callback for device 3, and have it succeed.
InvokePendingSetSoftwareFeatureStateCallback(
base::nullopt /* error_code */,
false /* expected_to_notify_observer_and_start_retry_timer */);
SimulateNewHostDevicesSynced(test_devices()[3] /* host_device_after_sync */,
true /* expected_to_fulfill_pending_request */);
EXPECT_FALSE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[3], delegate()->GetMultiDeviceHostFromBackend());
}
TEST_F(MultiDeviceSetupHostBackendDelegateImplTest,
StartWithDevice_SimultaneousRequestsToSameDevice) {
CreateDelegate(base::nullopt /* initial_host */);
// Attempt to set device 0, but do not invoke the callback yet.
AttemptToSetMultiDeviceHostOnBackend(test_devices()[0]);
EXPECT_TRUE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[0], delegate()->GetPendingHostRequest());
EXPECT_EQ(base::nullopt, delegate()->GetMultiDeviceHostFromBackend());
// Attempt to set device 0 again, and still do not invoke the callback.
AttemptToSetMultiDeviceHostOnBackend(test_devices()[0]);
EXPECT_TRUE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[0], delegate()->GetPendingHostRequest());
EXPECT_EQ(base::nullopt, delegate()->GetMultiDeviceHostFromBackend());
// Attempt to set device 0 one more time.
AttemptToSetMultiDeviceHostOnBackend(test_devices()[0]);
EXPECT_TRUE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[0], delegate()->GetPendingHostRequest());
EXPECT_EQ(base::nullopt, delegate()->GetMultiDeviceHostFromBackend());
// Fire the first callback, which should successfully transition the host.
InvokePendingSetSoftwareFeatureStateCallback(
base::nullopt /* error_code */,
false /* expected_to_notify_observer_and_start_retry_timer */);
SimulateNewHostDevicesSynced(test_devices()[0] /* host_device_after_sync */,
true /* expected_to_fulfill_pending_request */);
EXPECT_FALSE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[0], delegate()->GetMultiDeviceHostFromBackend());
// Fire the second callback, but have it fail. No state should be affected.
InvokePendingSetSoftwareFeatureStateCallback(
"errorCode" /* error_code */,
false /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_FALSE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[0], delegate()->GetMultiDeviceHostFromBackend());
// Fire the third callback, and have it succeed. Still, no state should be
// affected.
InvokePendingSetSoftwareFeatureStateCallback(
base::nullopt /* error_code */,
false /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_FALSE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[0], delegate()->GetMultiDeviceHostFromBackend());
}
TEST_F(MultiDeviceSetupHostBackendDelegateImplTest,
InitialPendingRequestButNoInitialDevice) {
CreateDelegate(
base::nullopt /* initial_host */,
test_devices()[0].GetDeviceId() /* initial_pending_host_request */);
// The delegate should have started a request as soon as it was created.
// Simulate it succeeding.
InvokePendingSetSoftwareFeatureStateCallback(
base::nullopt /* error_code */,
false /* expected_to_notify_observer_and_start_retry_timer */);
SimulateNewHostDevicesSynced(test_devices()[0] /* host_device_after_sync */,
true /* expected_to_fulfill_pending_request */);
EXPECT_FALSE(delegate()->HasPendingHostRequest());
EXPECT_EQ(test_devices()[0], delegate()->GetMultiDeviceHostFromBackend());
}
TEST_F(MultiDeviceSetupHostBackendDelegateImplTest,
InitialDeviceWithPendingRequestToRemoveIt) {
CreateDelegate(
test_devices()[0] /* initial_host */,
kPendingRemovalOfCurrentHost /* initial_pending_host_request */);
// The delegate should have started a request as soon as it was created.
// Simulate it succeeding.
InvokePendingSetSoftwareFeatureStateCallback(
base::nullopt /* error_code */,
false /* expected_to_notify_observer_and_start_retry_timer */);
SimulateNewHostDevicesSynced(base::nullopt /* host_device_after_sync */,
true /* expected_to_fulfill_pending_request */);
EXPECT_FALSE(delegate()->HasPendingHostRequest());
EXPECT_EQ(base::nullopt, delegate()->GetMultiDeviceHostFromBackend());
}
TEST_F(MultiDeviceSetupHostBackendDelegateImplTest, ChangedFromOtherDevice) {
CreateDelegate(base::nullopt /* initial_host */);
// The device changed from another device (i.e.,
// AttemptToSetMultiDeviceHostOnBackend() was not called).
SimulateNewHostDevicesSynced(test_devices()[0] /* host_device_after_sync */,
false /* expected_to_fulfill_pending_request */);
// One more change.
SimulateNewHostDevicesSynced(test_devices()[1] /* host_device_after_sync */,
false /* expected_to_fulfill_pending_request */);
}
TEST_F(MultiDeviceSetupHostBackendDelegateImplTest,
PendingRequestCanceledIfDeviceToSetNoLongerExists) {
CreateDelegate(base::nullopt /* initial_host */,
"nonexistentDeviceId" /* initial_pending_host_request */);
// An initial pending host request exists, but it is for a host that is not
// present in the DeviceSyncClient. Thus, the request should be canceled.
EXPECT_FALSE(delegate()->HasPendingHostRequest());
}
TEST_F(MultiDeviceSetupHostBackendDelegateImplTest,
PendingRequestCanceledIfDeviceToRemoveNoLongerExists) {
CreateDelegate(
base::nullopt /* initial_host */,
kPendingRemovalOfCurrentHost /* initial_pending_host_request */);
// An initial pending host request exists to remove the current host, but
// there actually is no current host. Thus, the request should be canceled.
EXPECT_FALSE(delegate()->HasPendingHostRequest());
}
} // namespace multidevice_setup
} // namespace chromeos
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/time/default_clock.h" #include "base/time/default_clock.h"
#include "chromeos/components/proximity_auth/logging/logging.h" #include "chromeos/components/proximity_auth/logging/logging.h"
#include "chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h" #include "chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h"
#include "chromeos/services/multidevice_setup/host_backend_delegate_impl.h"
#include "chromeos/services/multidevice_setup/setup_flow_completion_recorder_impl.h" #include "chromeos/services/multidevice_setup/setup_flow_completion_recorder_impl.h"
namespace chromeos { namespace chromeos {
...@@ -49,7 +50,11 @@ MultiDeviceSetupImpl::MultiDeviceSetupImpl( ...@@ -49,7 +50,11 @@ MultiDeviceSetupImpl::MultiDeviceSetupImpl(
PrefService* pref_service, PrefService* pref_service,
device_sync::DeviceSyncClient* device_sync_client, device_sync::DeviceSyncClient* device_sync_client,
secure_channel::SecureChannelClient* secure_channel_client) secure_channel::SecureChannelClient* secure_channel_client)
: setup_flow_completion_recorder_( : host_backend_delegate_(
HostBackendDelegateImpl::Factory::Get()->BuildInstance(
pref_service,
device_sync_client)),
setup_flow_completion_recorder_(
SetupFlowCompletionRecorderImpl::Factory::Get()->BuildInstance( SetupFlowCompletionRecorderImpl::Factory::Get()->BuildInstance(
pref_service, pref_service,
base::DefaultClock::GetInstance())), base::DefaultClock::GetInstance())),
......
...@@ -25,6 +25,7 @@ class SecureChannelClient; ...@@ -25,6 +25,7 @@ class SecureChannelClient;
namespace multidevice_setup { namespace multidevice_setup {
class AccountStatusChangeDelegateNotifier; class AccountStatusChangeDelegateNotifier;
class HostBackendDelegate;
class SetupFlowCompletionRecorder; class SetupFlowCompletionRecorder;
// Concrete MultiDeviceSetup implementation. // Concrete MultiDeviceSetup implementation.
...@@ -59,6 +60,7 @@ class MultiDeviceSetupImpl : public mojom::MultiDeviceSetup { ...@@ -59,6 +60,7 @@ class MultiDeviceSetupImpl : public mojom::MultiDeviceSetup {
mojom::EventTypeForDebugging type, mojom::EventTypeForDebugging type,
TriggerEventForDebuggingCallback callback) override; TriggerEventForDebuggingCallback callback) override;
std::unique_ptr<HostBackendDelegate> host_backend_delegate_;
std::unique_ptr<SetupFlowCompletionRecorder> setup_flow_completion_recorder_; std::unique_ptr<SetupFlowCompletionRecorder> setup_flow_completion_recorder_;
std::unique_ptr<AccountStatusChangeDelegateNotifier> delegate_notifier_; std::unique_ptr<AccountStatusChangeDelegateNotifier> delegate_notifier_;
......
...@@ -10,7 +10,9 @@ ...@@ -10,7 +10,9 @@
#include "chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h" #include "chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h"
#include "chromeos/services/multidevice_setup/fake_account_status_change_delegate.h" #include "chromeos/services/multidevice_setup/fake_account_status_change_delegate.h"
#include "chromeos/services/multidevice_setup/fake_account_status_change_delegate_notifier.h" #include "chromeos/services/multidevice_setup/fake_account_status_change_delegate_notifier.h"
#include "chromeos/services/multidevice_setup/fake_host_backend_delegate.h"
#include "chromeos/services/multidevice_setup/fake_setup_flow_completion_recorder.h" #include "chromeos/services/multidevice_setup/fake_setup_flow_completion_recorder.h"
#include "chromeos/services/multidevice_setup/host_backend_delegate_impl.h"
#include "chromeos/services/multidevice_setup/multidevice_setup_impl.h" #include "chromeos/services/multidevice_setup/multidevice_setup_impl.h"
#include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
#include "chromeos/services/multidevice_setup/setup_flow_completion_recorder_impl.h" #include "chromeos/services/multidevice_setup/setup_flow_completion_recorder_impl.h"
...@@ -24,6 +26,42 @@ namespace multidevice_setup { ...@@ -24,6 +26,42 @@ namespace multidevice_setup {
namespace { namespace {
class FakeHostBackendDelegateFactory : public HostBackendDelegateImpl::Factory {
public:
FakeHostBackendDelegateFactory(
sync_preferences::TestingPrefServiceSyncable*
expected_testing_pref_service,
device_sync::FakeDeviceSyncClient* expected_device_sync_client)
: expected_testing_pref_service_(expected_testing_pref_service),
expected_device_sync_client_(expected_device_sync_client) {}
~FakeHostBackendDelegateFactory() override = default;
FakeHostBackendDelegate* instance() { return instance_; }
private:
// HostBackendDelegateImpl::Factory:
std::unique_ptr<HostBackendDelegate> BuildInstance(
PrefService* pref_service,
device_sync::DeviceSyncClient* device_sync_client,
std::unique_ptr<base::OneShotTimer> timer) override {
EXPECT_FALSE(instance_);
EXPECT_EQ(expected_testing_pref_service_, pref_service);
EXPECT_EQ(expected_device_sync_client_, device_sync_client);
auto instance = std::make_unique<FakeHostBackendDelegate>();
instance_ = instance.get();
return instance;
}
sync_preferences::TestingPrefServiceSyncable* expected_testing_pref_service_;
device_sync::FakeDeviceSyncClient* expected_device_sync_client_;
FakeHostBackendDelegate* instance_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(FakeHostBackendDelegateFactory);
};
class FakeSetupFlowCompletionRecorderFactory class FakeSetupFlowCompletionRecorderFactory
: public SetupFlowCompletionRecorderImpl::Factory { : public SetupFlowCompletionRecorderImpl::Factory {
public: public:
...@@ -117,6 +155,12 @@ class MultiDeviceSetupImplTest : public testing::Test { ...@@ -117,6 +155,12 @@ class MultiDeviceSetupImplTest : public testing::Test {
fake_secure_channel_client_ = fake_secure_channel_client_ =
std::make_unique<secure_channel::FakeSecureChannelClient>(); std::make_unique<secure_channel::FakeSecureChannelClient>();
fake_host_backend_delegate_factory_ =
std::make_unique<FakeHostBackendDelegateFactory>(
test_pref_service_.get(), fake_device_sync_client_.get());
HostBackendDelegateImpl::Factory::SetFactoryForTesting(
fake_host_backend_delegate_factory_.get());
fake_setup_flow_completion_recorder_factory_ = fake_setup_flow_completion_recorder_factory_ =
std::make_unique<FakeSetupFlowCompletionRecorderFactory>( std::make_unique<FakeSetupFlowCompletionRecorderFactory>(
test_pref_service_.get()); test_pref_service_.get());
...@@ -136,6 +180,7 @@ class MultiDeviceSetupImplTest : public testing::Test { ...@@ -136,6 +180,7 @@ class MultiDeviceSetupImplTest : public testing::Test {
} }
void TearDown() override { void TearDown() override {
HostBackendDelegateImpl::Factory::SetFactoryForTesting(nullptr);
SetupFlowCompletionRecorderImpl::Factory::SetFactoryForTesting(nullptr); SetupFlowCompletionRecorderImpl::Factory::SetFactoryForTesting(nullptr);
AccountStatusChangeDelegateNotifierImpl::Factory::SetFactoryForTesting( AccountStatusChangeDelegateNotifierImpl::Factory::SetFactoryForTesting(
nullptr); nullptr);
...@@ -195,6 +240,8 @@ class MultiDeviceSetupImplTest : public testing::Test { ...@@ -195,6 +240,8 @@ class MultiDeviceSetupImplTest : public testing::Test {
std::unique_ptr<secure_channel::FakeSecureChannelClient> std::unique_ptr<secure_channel::FakeSecureChannelClient>
fake_secure_channel_client_; fake_secure_channel_client_;
std::unique_ptr<FakeHostBackendDelegateFactory>
fake_host_backend_delegate_factory_;
std::unique_ptr<FakeSetupFlowCompletionRecorderFactory> std::unique_ptr<FakeSetupFlowCompletionRecorderFactory>
fake_setup_flow_completion_recorder_factory_; fake_setup_flow_completion_recorder_factory_;
std::unique_ptr<FakeAccountStatusChangeDelegateNotifierFactory> std::unique_ptr<FakeAccountStatusChangeDelegateNotifierFactory>
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "chromeos/components/proximity_auth/logging/logging.h" #include "chromeos/components/proximity_auth/logging/logging.h"
#include "chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h" #include "chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h"
#include "chromeos/services/multidevice_setup/host_backend_delegate_impl.h"
#include "chromeos/services/multidevice_setup/multidevice_setup_base.h" #include "chromeos/services/multidevice_setup/multidevice_setup_base.h"
#include "chromeos/services/multidevice_setup/multidevice_setup_initializer.h" #include "chromeos/services/multidevice_setup/multidevice_setup_initializer.h"
...@@ -17,6 +18,7 @@ namespace multidevice_setup { ...@@ -17,6 +18,7 @@ namespace multidevice_setup {
void MultiDeviceSetupService::RegisterProfilePrefs( void MultiDeviceSetupService::RegisterProfilePrefs(
PrefRegistrySimple* registry) { PrefRegistrySimple* registry) {
AccountStatusChangeDelegateNotifierImpl::RegisterPrefs(registry); AccountStatusChangeDelegateNotifierImpl::RegisterPrefs(registry);
HostBackendDelegateImpl::RegisterPrefs(registry);
} }
MultiDeviceSetupService::MultiDeviceSetupService( MultiDeviceSetupService::MultiDeviceSetupService(
......
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