Commit 4f3725c4 authored by Kyle Horimoto's avatar Kyle Horimoto Committed by Commit Bot

[CrOS MultiDevice] Implement basic DeviceSync service functionality.

This CL implements the initialization flow for the service as well as
the implementation for GetSyncedDevices() and observer callbacks.
Additionally, it tweaks the API for Force{Enrollment,Sync}Now() to
return a boolean of whether the forced action was successfully
handled by the service (it cannot be handled before the service
starts up).

Bug: 824568, 752273
Change-Id: I787c5ac9cb5fe6011e26e0ddb8217910b9c495f0
Reviewed-on: https://chromium-review.googlesource.com/998795
Commit-Queue: Kyle Horimoto <khorimoto@chromium.org>
Reviewed-by: default avatarPeter Beverloo <peter@chromium.org>
Reviewed-by: default avatarRyan Hansberry <hansberry@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarJonathan Ross <jonross@chromium.org>
Reviewed-by: default avatarJeremy Klein <jlklein@chromium.org>
Cr-Commit-Position: refs/heads/master@{#551474}
parent e1b73f45
......@@ -36,6 +36,7 @@ static_library("device_sync") {
"//chromeos/services/device_sync/public/mojom",
"//net",
"//services/identity/public/cpp",
"//services/preferences/public/cpp",
"//services/service_manager/public/cpp",
]
}
......@@ -78,10 +79,13 @@ source_set("unit_tests") {
":test_support",
"//base",
"//base/test:test_support",
"//chromeos",
"//chromeos/services/device_sync/public/mojom",
"//chromeos/services/device_sync/public/mojom:unit_tests",
"//components/cryptauth",
"//components/cryptauth:test_support",
"//components/gcm_driver:test_support",
"//components/prefs:test_support",
"//mojo/common",
"//services/identity/public/cpp:test_support",
"//services/service_manager/public/cpp:service_test_support",
......
include_rules = [
"+components/cryptauth",
"+components/gcm_driver",
"+components/proximity_auth/logging",
"+components/signin/core/browser",
"+google_apis/gaia",
"+mojo/public/cpp",
"+net/url_request",
"+services/identity",
"+services/preferences/public",
"+services/service_manager/public/cpp",
"+components/cryptauth"
]
......@@ -4,31 +4,130 @@
#include "chromeos/services/device_sync/device_sync_impl.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "base/time/default_clock.h"
#include "chromeos/components/proximity_auth/logging/logging.h"
#include "chromeos/services/device_sync/cryptauth_client_factory_impl.h"
#include "chromeos/services/device_sync/cryptauth_enroller_factory_impl.h"
#include "components/cryptauth/cryptauth_device_manager_impl.h"
#include "components/cryptauth/cryptauth_enrollment_manager_impl.h"
#include "components/cryptauth/cryptauth_gcm_manager_impl.h"
#include "components/cryptauth/device_classifier_util.h"
#include "components/cryptauth/gcm_device_info_provider.h"
#include "components/cryptauth/proto/cryptauth_api.pb.h"
#include "components/cryptauth/remote_device_provider_impl.h"
#include "components/cryptauth/secure_message_delegate_impl.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "net/url_request/url_request_context_getter.h"
#include "services/identity/public/cpp/identity_manager.h"
#include "services/service_manager/public/cpp/connector.h"
namespace chromeos {
namespace device_sync {
DeviceSyncImpl::DeviceSyncImpl() = default;
namespace {
DeviceSyncImpl::~DeviceSyncImpl() = default;
void RegisterDeviceSyncPrefs(PrefRegistrySimple* registry) {
cryptauth::CryptAuthGCMManager::RegisterPrefs(registry);
cryptauth::CryptAuthDeviceManager::RegisterPrefs(registry);
cryptauth::CryptAuthEnrollmentManager::RegisterPrefs(registry);
}
void DeviceSyncImpl::BindRequest(mojom::DeviceSyncRequest request) {
bindings_.AddBinding(this, std::move(request));
} // namespace
// static
DeviceSyncImpl::Factory* DeviceSyncImpl::Factory::test_factory_instance_ =
nullptr;
// static
std::unique_ptr<DeviceSyncImpl> DeviceSyncImpl::Factory::NewInstance(
identity::IdentityManager* identity_manager,
gcm::GCMDriver* gcm_driver,
service_manager::Connector* connector,
cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
scoped_refptr<net::URLRequestContextGetter> url_request_context) {
if (test_factory_instance_) {
return test_factory_instance_->BuildInstance(
identity_manager, gcm_driver, connector, gcm_device_info_provider,
std::move(url_request_context));
}
static base::NoDestructor<DeviceSyncImpl::Factory> default_factory;
return default_factory->BuildInstance(identity_manager, gcm_driver, connector,
gcm_device_info_provider,
std::move(url_request_context));
}
void DeviceSyncImpl::ForceEnrollmentNow() {
// TODO(khorimoto): Actually perform enrollment. Currently, we immediately
// alert observers that a successful enrollment occurred.
observers_.ForAllPtrs([](auto* observer) {
observer->OnEnrollmentFinished(true /* success */);
});
// static
void DeviceSyncImpl::Factory::SetInstanceForTesting(Factory* test_factory) {
test_factory_instance_ = test_factory;
}
void DeviceSyncImpl::ForceSyncNow() {
// TODO(khorimoto): Actually perform a sync. Currently, we immediately
// alert observers that a successful sync occurred.
observers_.ForAllPtrs(
[](auto* observer) { observer->OnDevicesSynced(true /* success */); });
DeviceSyncImpl::Factory::~Factory() = default;
std::unique_ptr<DeviceSyncImpl> DeviceSyncImpl::Factory::BuildInstance(
identity::IdentityManager* identity_manager,
gcm::GCMDriver* gcm_driver,
service_manager::Connector* connector,
cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
scoped_refptr<net::URLRequestContextGetter> url_request_context) {
return base::WrapUnique(new DeviceSyncImpl(
identity_manager, gcm_driver, connector, gcm_device_info_provider,
std::move(url_request_context), base::DefaultClock::GetInstance(),
std::make_unique<PrefConnectionDelegate>()));
}
DeviceSyncImpl::PrefConnectionDelegate::~PrefConnectionDelegate() = default;
scoped_refptr<PrefRegistrySimple>
DeviceSyncImpl::PrefConnectionDelegate::CreatePrefRegistry() {
return base::MakeRefCounted<PrefRegistrySimple>();
}
void DeviceSyncImpl::PrefConnectionDelegate::ConnectToPrefService(
service_manager::Connector* connector,
scoped_refptr<PrefRegistrySimple> pref_registry,
prefs::ConnectCallback callback) {
prefs::mojom::PrefStoreConnectorPtr pref_store_connector;
connector->BindInterface(prefs::mojom::kServiceName, &pref_store_connector);
prefs::ConnectToPrefService(std::move(pref_store_connector),
std::move(pref_registry), std::move(callback));
}
DeviceSyncImpl::DeviceSyncImpl(
identity::IdentityManager* identity_manager,
gcm::GCMDriver* gcm_driver,
service_manager::Connector* connector,
cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
scoped_refptr<net::URLRequestContextGetter> url_request_context,
base::Clock* clock,
std::unique_ptr<PrefConnectionDelegate> pref_connection_delegate)
: identity_manager_(identity_manager),
gcm_driver_(gcm_driver),
connector_(connector),
gcm_device_info_provider_(gcm_device_info_provider),
url_request_context_(url_request_context),
clock_(clock),
pref_connection_delegate_(std::move(pref_connection_delegate)),
status_(Status::FETCHING_ACCOUNT_INFO),
weak_ptr_factory_(this) {
PA_LOG(INFO) << "DeviceSyncImpl: Initializing.";
ProcessPrimaryAccountInfo(identity_manager_->GetPrimaryAccountInfo());
}
DeviceSyncImpl::~DeviceSyncImpl() {
if (cryptauth_enrollment_manager_)
cryptauth_enrollment_manager_->RemoveObserver(this);
if (remote_device_provider_)
remote_device_provider_->RemoveObserver(this);
}
void DeviceSyncImpl::BindRequest(mojom::DeviceSyncRequest request) {
bindings_.AddBinding(this, std::move(request));
}
void DeviceSyncImpl::AddObserver(mojom::DeviceSyncObserverPtr observer,
......@@ -37,6 +136,177 @@ void DeviceSyncImpl::AddObserver(mojom::DeviceSyncObserverPtr observer,
std::move(callback).Run();
}
void DeviceSyncImpl::ForceEnrollmentNow(ForceEnrollmentNowCallback callback) {
if (status_ != Status::READY) {
PA_LOG(WARNING) << "DeviceSyncImpl::ForceEnrollmentNow() invoked before "
<< "initialization was complete. Cannot force enrollment.";
std::move(callback).Run(false /* success */);
return;
}
cryptauth_enrollment_manager_->ForceEnrollmentNow(
cryptauth::INVOCATION_REASON_MANUAL);
std::move(callback).Run(true /* success */);
}
void DeviceSyncImpl::ForceSyncNow(ForceSyncNowCallback callback) {
if (status_ != Status::READY) {
PA_LOG(WARNING) << "DeviceSyncImpl::ForceSyncNow() invoked before "
<< "initialization was complete. Cannot force sync.";
std::move(callback).Run(false /* success */);
return;
}
cryptauth_device_manager_->ForceSyncNow(cryptauth::INVOCATION_REASON_MANUAL);
std::move(callback).Run(true /* success */);
}
void DeviceSyncImpl::GetSyncedDevices(GetSyncedDevicesCallback callback) {
if (status_ != Status::READY) {
PA_LOG(WARNING) << "DeviceSyncImpl::GetSyncedDevices() invoked before "
<< "initialization was complete. Returning empty list of "
<< "synced devices.";
std::move(callback).Run(std::vector<cryptauth::RemoteDevice>());
return;
}
std::move(callback).Run(remote_device_provider_->GetSyncedDevices());
}
void DeviceSyncImpl::OnEnrollmentFinished(bool success) {
PA_LOG(INFO) << "DeviceSyncImpl: Enrollment finished; success = " << success;
if (!success)
return;
if (status_ == Status::WAITING_FOR_ENROLLMENT)
CompleteInitializationAfterSuccessfulEnrollment();
observers_.ForAllPtrs(
[](auto* observer) { observer->OnEnrollmentFinished(); });
}
void DeviceSyncImpl::OnSyncDeviceListChanged() {
PA_LOG(INFO) << "DeviceSyncImpl: Synced devices changed; notifying "
<< "observers.";
observers_.ForAllPtrs([](auto* observer) { observer->OnNewDevicesSynced(); });
}
void DeviceSyncImpl::ProcessPrimaryAccountInfo(
const AccountInfo& primary_account_info) {
// Note: We cannot use |primary_account_info.IsValid()| here because
// IdentityTestEnvironment is buggy: https://crbug.com/830122. For now,
// we simply check that the account ID is set.
// TODO(khorimoto): Use IsValid() once the aforementioned bug is fixed.
if (primary_account_info.account_id.empty()) {
PA_LOG(ERROR) << "Primary account information is invalid; cannot proceed.";
// This situation should never occur in practice. The log above is added to
// ensure that this is flagged in release builds where NOTREACHED() does not
// crash the process.
NOTREACHED();
return;
}
primary_account_info_ = primary_account_info;
ConnectToPrefStore();
}
void DeviceSyncImpl::ConnectToPrefStore() {
DCHECK(status_ == Status::FETCHING_ACCOUNT_INFO);
status_ = Status::CONNECTING_TO_USER_PREFS;
auto pref_registry = pref_connection_delegate_->CreatePrefRegistry();
RegisterDeviceSyncPrefs(pref_registry.get());
PA_LOG(INFO) << "DeviceSyncImpl: Connecting to pref service.";
pref_connection_delegate_->ConnectToPrefService(
connector_, std::move(pref_registry),
base::Bind(&DeviceSyncImpl::OnConnectedToPrefService,
weak_ptr_factory_.GetWeakPtr()));
}
void DeviceSyncImpl::OnConnectedToPrefService(
std::unique_ptr<PrefService> pref_service) {
DCHECK(status_ == Status::CONNECTING_TO_USER_PREFS);
status_ = Status::WAITING_FOR_ENROLLMENT;
PA_LOG(INFO) << "DeviceSyncImpl: Connected to pref service; initializing "
<< "CryptAuth managers.";
pref_service_ = std::move(pref_service);
InitializeCryptAuthManagementObjects();
// If enrollment has not yet completed successfully, initialization cannot
// continue. Once enrollment has finished, OnEnrollmentFinished() is invoked,
// which finishes the initialization flow.
if (!cryptauth_enrollment_manager_->IsEnrollmentValid()) {
PA_LOG(INFO) << "DeviceSyncImpl: Waiting for enrollment to complete.";
return;
}
CompleteInitializationAfterSuccessfulEnrollment();
}
void DeviceSyncImpl::InitializeCryptAuthManagementObjects() {
DCHECK(status_ == Status::WAITING_FOR_ENROLLMENT);
// Initialize |cryptauth_gcm_manager_| and have it start listening for GCM
// tickles.
cryptauth_gcm_manager_ =
cryptauth::CryptAuthGCMManagerImpl::Factory::NewInstance(
gcm_driver_, pref_service_.get());
cryptauth_gcm_manager_->StartListening();
// Initialize |crypauth_device_manager_| and start observing. Start() is not
// called yet since the device has not completed enrollment.
cryptauth_device_manager_ =
cryptauth::CryptAuthDeviceManagerImpl::Factory::NewInstance(
clock_,
std::make_unique<CryptAuthClientFactoryImpl>(
identity_manager_, url_request_context_,
cryptauth::device_classifier_util::GetDeviceClassifier()),
cryptauth_gcm_manager_.get(), pref_service_.get());
// Initialize |cryptauth_enrollment_manager_| and start observing, then call
// Start() immediately to schedule enrollment.
cryptauth_enrollment_manager_ =
cryptauth::CryptAuthEnrollmentManagerImpl::Factory::NewInstance(
clock_,
std::make_unique<CryptAuthEnrollerFactoryImpl>(
identity_manager_, url_request_context_,
cryptauth::device_classifier_util::GetDeviceClassifier()),
cryptauth::SecureMessageDelegateImpl::Factory::NewInstance(),
gcm_device_info_provider_->GetGcmDeviceInfo(),
cryptauth_gcm_manager_.get(), pref_service_.get());
cryptauth_enrollment_manager_->AddObserver(this);
cryptauth_enrollment_manager_->Start();
}
void DeviceSyncImpl::CompleteInitializationAfterSuccessfulEnrollment() {
DCHECK(status_ == Status::WAITING_FOR_ENROLLMENT);
DCHECK(cryptauth_enrollment_manager_->IsEnrollmentValid());
// Now that enrollment has completed, the current device has been registered
// with the CryptAuth back-end and can begin monitoring synced devices.
cryptauth_device_manager_->Start();
remote_device_provider_ =
cryptauth::RemoteDeviceProviderImpl::Factory::NewInstance(
cryptauth_device_manager_.get(), primary_account_info_.account_id,
cryptauth_enrollment_manager_->GetUserPrivateKey());
remote_device_provider_->AddObserver(this);
status_ = Status::READY;
PA_LOG(INFO) << "DeviceSyncImpl: CryptAuth Enrollment is valid; service "
<< "fully initialized.";
}
void DeviceSyncImpl::SetPrefConnectionDelegateForTesting(
std::unique_ptr<PrefConnectionDelegate> pref_connection_delegate) {
pref_connection_delegate_ = std::move(pref_connection_delegate);
}
} // namespace device_sync
} // namespace chromeos
......@@ -6,34 +6,167 @@
#define CHROMEOS_SERVICES_DEVICE_SYNC_DEVICE_SYNC_IMPL_H_
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
#include "components/cryptauth/cryptauth_enrollment_manager.h"
#include "components/cryptauth/cryptauth_gcm_manager.h"
#include "components/cryptauth/remote_device_provider.h"
#include "components/signin/core/browser/account_info.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "mojo/public/cpp/bindings/interface_ptr_set.h"
#include "services/preferences/public/cpp/pref_service_factory.h"
#include "services/preferences/public/mojom/preferences.mojom.h"
class PrefService;
namespace base {
class Clock;
} // namespace base
namespace cryptauth {
class CryptAuthDeviceManager;
class GcmDeviceInfoProvider;
} // namespace cryptauth
namespace gcm {
class GCMDriver;
} // namespace gcm
namespace identity {
class IdentityManager;
} // namespace identity
namespace net {
class URLRequestContextGetter;
} // namespace net
namespace service_manager {
class Connector;
} // namespace service_manager
namespace chromeos {
namespace device_sync {
// Concrete DeviceSync implementation.
class DeviceSyncImpl : public mojom::DeviceSync {
// Concrete DeviceSync implementation. When DeviceSyncImpl is constructed, it
// starts an initialization flow with the following steps:
// (1) Verify that the primary user is logged in with a valid account ID.
// (2) Connect to the Prefs Service associated with that account.
// (3) Instantiate classes which communicate with the CryptAuth back-end.
// (4) Check enrollment state; if not yet enrolled, enroll the device.
// (5) When enrollment is valid, listen for device sync updates.
class DeviceSyncImpl : public mojom::DeviceSync,
public cryptauth::CryptAuthEnrollmentManager::Observer,
public cryptauth::RemoteDeviceProvider::Observer {
public:
DeviceSyncImpl();
class Factory {
public:
static std::unique_ptr<DeviceSyncImpl> NewInstance(
identity::IdentityManager* identity_manager,
gcm::GCMDriver* gcm_driver,
service_manager::Connector* connector,
cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
scoped_refptr<net::URLRequestContextGetter> url_request_context);
static void SetInstanceForTesting(Factory* test_factory);
virtual ~Factory();
virtual std::unique_ptr<DeviceSyncImpl> BuildInstance(
identity::IdentityManager* identity_manager,
gcm::GCMDriver* gcm_driver,
service_manager::Connector* connector,
cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
scoped_refptr<net::URLRequestContextGetter> url_request_context);
private:
static Factory* test_factory_instance_;
};
~DeviceSyncImpl() override;
// Binds a request to this implementation. Should be called each time that the
// service receives a request.
void BindRequest(mojom::DeviceSyncRequest request);
protected:
// mojom::DeviceSync:
void ForceEnrollmentNow() override;
void ForceSyncNow() override;
void AddObserver(mojom::DeviceSyncObserverPtr observer,
AddObserverCallback callback) override;
void ForceEnrollmentNow(ForceEnrollmentNowCallback callback) override;
void ForceSyncNow(ForceSyncNowCallback callback) override;
void GetSyncedDevices(GetSyncedDevicesCallback callback) override;
// cryptauth::CryptAuthEnrollmentManager::Observer:
void OnEnrollmentFinished(bool success) override;
// cryptauth::RemoteDeviceProvider::Observer:
void OnSyncDeviceListChanged() override;
private:
friend class DeviceSyncServiceTest;
enum class Status {
FETCHING_ACCOUNT_INFO,
CONNECTING_TO_USER_PREFS,
WAITING_FOR_ENROLLMENT,
READY
};
// Wrapper around preferences code. This class is necessary so that tests can
// override this functionality to use a fake PrefService rather than a real
// connection to the Preferences service.
class PrefConnectionDelegate {
public:
virtual ~PrefConnectionDelegate();
virtual scoped_refptr<PrefRegistrySimple> CreatePrefRegistry();
virtual void ConnectToPrefService(
service_manager::Connector* connector,
scoped_refptr<PrefRegistrySimple> pref_registry,
prefs::ConnectCallback callback);
};
DeviceSyncImpl(
identity::IdentityManager* identity_manager,
gcm::GCMDriver* gcm_driver,
service_manager::Connector* connector,
cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
scoped_refptr<net::URLRequestContextGetter> url_request_context,
base::Clock* clock,
std::unique_ptr<PrefConnectionDelegate> pref_connection_delegate);
void ProcessPrimaryAccountInfo(const AccountInfo& primary_account_info);
void ConnectToPrefStore();
void OnConnectedToPrefService(std::unique_ptr<PrefService> pref_service);
void InitializeCryptAuthManagementObjects();
void CompleteInitializationAfterSuccessfulEnrollment();
void SetPrefConnectionDelegateForTesting(
std::unique_ptr<PrefConnectionDelegate> pref_connection_delegate);
identity::IdentityManager* identity_manager_;
gcm::GCMDriver* gcm_driver_;
service_manager::Connector* connector_;
cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider_;
scoped_refptr<net::URLRequestContextGetter> url_request_context_;
base::Clock* clock_;
std::unique_ptr<PrefConnectionDelegate> pref_connection_delegate_;
Status status_;
AccountInfo primary_account_info_;
std::unique_ptr<PrefService> pref_service_;
std::unique_ptr<cryptauth::CryptAuthGCMManager> cryptauth_gcm_manager_;
std::unique_ptr<cryptauth::CryptAuthEnrollmentManager>
cryptauth_enrollment_manager_;
std::unique_ptr<cryptauth::CryptAuthDeviceManager> cryptauth_device_manager_;
std::unique_ptr<cryptauth::RemoteDeviceProvider> remote_device_provider_;
mojo::InterfacePtrSet<mojom::DeviceSyncObserver> observers_;
mojo::BindingSet<mojom::DeviceSync> bindings_;
base::WeakPtrFactory<DeviceSyncImpl> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(DeviceSyncImpl);
};
......
......@@ -6,18 +6,34 @@
#include "chromeos/components/proximity_auth/logging/logging.h"
#include "chromeos/services/device_sync/device_sync_impl.h"
#include "net/url_request/url_request_context_getter.h"
#include "services/service_manager/public/cpp/service_context.h"
namespace chromeos {
namespace device_sync {
DeviceSyncService::DeviceSyncService()
: device_sync_impl_(std::make_unique<DeviceSyncImpl>()) {}
DeviceSyncService::DeviceSyncService(
identity::IdentityManager* identity_manager,
gcm::GCMDriver* gcm_driver,
cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
scoped_refptr<net::URLRequestContextGetter> url_request_context)
: identity_manager_(identity_manager),
gcm_driver_(gcm_driver),
gcm_device_info_provider_(gcm_device_info_provider),
url_request_context_(url_request_context) {}
DeviceSyncService::~DeviceSyncService() = default;
void DeviceSyncService::OnStart() {
PA_LOG(INFO) << "DeviceSyncService::OnStart()";
// context() cannot be invoked until after the constructor is run, so
// |device_sync_impl_| cannot be initialized until OnStart().
device_sync_impl_ = DeviceSyncImpl::Factory::NewInstance(
identity_manager_, gcm_driver_, context()->connector(),
gcm_device_info_provider_, url_request_context_);
registry_.AddInterface(base::Bind(&DeviceSyncImpl::BindRequest,
base::Unretained(device_sync_impl_.get())));
}
......
......@@ -7,10 +7,27 @@
#include <memory>
#include "base/memory/ref_counted.h"
#include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
#include "services/service_manager/public/cpp/binder_registry.h"
#include "services/service_manager/public/cpp/service.h"
namespace cryptauth {
class GcmDeviceInfoProvider;
} // namespace cryptauth
namespace gcm {
class GCMDriver;
} // namespace gcm
namespace identity {
class IdentityManager;
} // namespace identity
namespace net {
class URLRequestContextGetter;
} // namespace net
namespace chromeos {
namespace device_sync {
......@@ -22,7 +39,11 @@ class DeviceSyncImpl;
// implementation and shares it among all connection requests.
class DeviceSyncService : public service_manager::Service {
public:
DeviceSyncService();
DeviceSyncService(
identity::IdentityManager* identity_manager,
gcm::GCMDriver* gcm_driver,
cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
scoped_refptr<net::URLRequestContextGetter> url_request_context);
~DeviceSyncService() override;
protected:
......@@ -33,6 +54,11 @@ class DeviceSyncService : public service_manager::Service {
mojo::ScopedMessagePipeHandle interface_pipe) override;
private:
identity::IdentityManager* identity_manager_;
gcm::GCMDriver* gcm_driver_;
cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider_;
scoped_refptr<net::URLRequestContextGetter> url_request_context_;
std::unique_ptr<DeviceSyncImpl> device_sync_impl_;
service_manager::BinderRegistry registry_;
......
......@@ -4,12 +4,33 @@
#include <memory>
#include "base/memory/scoped_refptr.h"
#include "base/no_destructor.h"
#include "base/run_loop.h"
#include "base/test/null_task_runner.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/simple_test_clock.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/services/device_sync/device_sync_impl.h"
#include "chromeos/services/device_sync/device_sync_service.h"
#include "chromeos/services/device_sync/fake_device_sync_observer.h"
#include "chromeos/services/device_sync/public/mojom/constants.mojom.h"
#include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
#include "components/cryptauth/cryptauth_device_manager_impl.h"
#include "components/cryptauth/cryptauth_enrollment_manager_impl.h"
#include "components/cryptauth/cryptauth_gcm_manager_impl.h"
#include "components/cryptauth/fake_cryptauth_device_manager.h"
#include "components/cryptauth/fake_cryptauth_enrollment_manager.h"
#include "components/cryptauth/fake_cryptauth_gcm_manager.h"
#include "components/cryptauth/fake_gcm_device_info_provider.h"
#include "components/cryptauth/fake_remote_device_provider.h"
#include "components/cryptauth/remote_device_provider_impl.h"
#include "components/cryptauth/remote_device_test_util.h"
#include "components/gcm_driver/fake_gcm_driver.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "net/url_request/url_request_context_getter.h"
#include "services/identity/public/cpp/identity_test_environment.h"
#include "services/service_manager/public/cpp/test/test_connector_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -19,127 +40,773 @@ namespace device_sync {
namespace {
class FakeDeviceSyncObserverDelegate : public FakeDeviceSyncObserver::Delegate {
const char kTestEmail[] = "example@gmail.com";
const char kTestGcmDeviceInfoLongDeviceId[] = "longDeviceId";
const char kTestCryptAuthGCMRegistrationId[] = "cryptAuthRegistrationId";
const size_t kNumTestDevices = 5u;
const cryptauth::GcmDeviceInfo& GetTestGcmDeviceInfo() {
static const base::NoDestructor<cryptauth::GcmDeviceInfo> gcm_device_info([] {
cryptauth::GcmDeviceInfo gcm_device_info;
gcm_device_info.set_long_device_id(kTestGcmDeviceInfoLongDeviceId);
return gcm_device_info;
}());
return *gcm_device_info;
}
cryptauth::RemoteDeviceList GenerateTestRemoteDevices() {
cryptauth::RemoteDeviceList devices =
cryptauth::GenerateTestRemoteDevices(kNumTestDevices);
// Load an empty set of BeaconSeeds for each device.
// TODO(khorimoto): Adjust device_sync_mojom_traits.h/cc to allow passing
// devices without BeaconSeeds to be sent across Mojo.
for (auto& device : devices)
device.LoadBeaconSeeds(std::vector<cryptauth::BeaconSeed>());
return devices;
}
class FakeCryptAuthGCMManagerFactory
: public cryptauth::CryptAuthGCMManagerImpl::Factory {
public:
FakeDeviceSyncObserverDelegate(
const base::Closure& on_delegate_function_called_closure)
: on_delegate_function_called_closure_(
on_delegate_function_called_closure) {}
~FakeDeviceSyncObserverDelegate() = default;
FakeCryptAuthGCMManagerFactory(gcm::FakeGCMDriver* fake_gcm_driver,
TestingPrefServiceSimple* test_pref_service)
: fake_gcm_driver_(fake_gcm_driver),
test_pref_service_(test_pref_service) {}
~FakeCryptAuthGCMManagerFactory() override = default;
cryptauth::FakeCryptAuthGCMManager* instance() { return instance_; }
// cryptauth::CryptAuthGCMManagerImpl::Factory:
std::unique_ptr<cryptauth::CryptAuthGCMManager> BuildInstance(
gcm::GCMDriver* gcm_driver,
PrefService* pref_service) override {
EXPECT_EQ(fake_gcm_driver_, gcm_driver);
EXPECT_EQ(test_pref_service_, pref_service);
// Only one instance is expected to be created per test.
EXPECT_FALSE(instance_);
auto instance = std::make_unique<cryptauth::FakeCryptAuthGCMManager>(
kTestCryptAuthGCMRegistrationId);
instance_ = instance.get();
return std::move(instance);
}
private:
// FakeDeviceSyncObserver::Delegate:
void OnEnrollmentFinishedCalled() override {
on_delegate_function_called_closure_.Run();
gcm::FakeGCMDriver* fake_gcm_driver_;
TestingPrefServiceSimple* test_pref_service_;
cryptauth::FakeCryptAuthGCMManager* instance_ = nullptr;
};
class FakeCryptAuthDeviceManagerFactory
: public cryptauth::CryptAuthDeviceManagerImpl::Factory {
public:
FakeCryptAuthDeviceManagerFactory(
base::SimpleTestClock* simple_test_clock,
FakeCryptAuthGCMManagerFactory* fake_cryptauth_gcm_manager_factory,
TestingPrefServiceSimple* test_pref_service)
: simple_test_clock_(simple_test_clock),
fake_cryptauth_gcm_manager_factory_(fake_cryptauth_gcm_manager_factory),
test_pref_service_(test_pref_service) {}
~FakeCryptAuthDeviceManagerFactory() override = default;
cryptauth::FakeCryptAuthDeviceManager* instance() { return instance_; }
// cryptauth::CryptAuthDeviceManagerImpl::Factory:
std::unique_ptr<cryptauth::CryptAuthDeviceManager> BuildInstance(
base::Clock* clock,
std::unique_ptr<cryptauth::CryptAuthClientFactory> client_factory,
cryptauth::CryptAuthGCMManager* gcm_manager,
PrefService* pref_service) override {
EXPECT_EQ(simple_test_clock_, clock);
EXPECT_EQ(fake_cryptauth_gcm_manager_factory_->instance(), gcm_manager);
EXPECT_EQ(test_pref_service_, pref_service);
// Only one instance is expected to be created per test.
EXPECT_FALSE(instance_);
auto instance = std::make_unique<cryptauth::FakeCryptAuthDeviceManager>();
instance_ = instance.get();
return std::move(instance);
}
private:
base::SimpleTestClock* simple_test_clock_;
FakeCryptAuthGCMManagerFactory* fake_cryptauth_gcm_manager_factory_;
TestingPrefServiceSimple* test_pref_service_;
cryptauth::FakeCryptAuthDeviceManager* instance_ = nullptr;
};
class FakeCryptAuthEnrollmentManagerFactory
: public cryptauth::CryptAuthEnrollmentManagerImpl::Factory {
public:
FakeCryptAuthEnrollmentManagerFactory(
base::SimpleTestClock* simple_test_clock,
FakeCryptAuthGCMManagerFactory* fake_cryptauth_gcm_manager_factory,
TestingPrefServiceSimple* test_pref_service)
: simple_test_clock_(simple_test_clock),
fake_cryptauth_gcm_manager_factory_(fake_cryptauth_gcm_manager_factory),
test_pref_service_(test_pref_service) {}
~FakeCryptAuthEnrollmentManagerFactory() override = default;
void set_device_already_enrolled_in_cryptauth(
bool device_already_enrolled_in_cryptauth) {
device_already_enrolled_in_cryptauth_ =
device_already_enrolled_in_cryptauth;
}
void OnDevicesSyncedCalled() override {
on_delegate_function_called_closure_.Run();
cryptauth::FakeCryptAuthEnrollmentManager* instance() { return instance_; }
// cryptauth::CryptAuthEnrollmentManagerImpl::Factory:
std::unique_ptr<cryptauth::CryptAuthEnrollmentManager> BuildInstance(
base::Clock* clock,
std::unique_ptr<cryptauth::CryptAuthEnrollerFactory> enroller_factory,
std::unique_ptr<cryptauth::SecureMessageDelegate> secure_message_delegate,
const cryptauth::GcmDeviceInfo& device_info,
cryptauth::CryptAuthGCMManager* gcm_manager,
PrefService* pref_service) override {
EXPECT_EQ(simple_test_clock_, clock);
EXPECT_EQ(kTestGcmDeviceInfoLongDeviceId, device_info.long_device_id());
EXPECT_EQ(fake_cryptauth_gcm_manager_factory_->instance(), gcm_manager);
EXPECT_EQ(test_pref_service_, pref_service);
// Only one instance is expected to be created per test.
EXPECT_FALSE(instance_);
auto instance =
std::make_unique<cryptauth::FakeCryptAuthEnrollmentManager>();
instance->set_is_enrollment_valid(device_already_enrolled_in_cryptauth_);
instance_ = instance.get();
return std::move(instance);
}
base::Closure on_delegate_function_called_closure_;
private:
base::SimpleTestClock* simple_test_clock_;
FakeCryptAuthGCMManagerFactory* fake_cryptauth_gcm_manager_factory_;
TestingPrefServiceSimple* test_pref_service_;
bool device_already_enrolled_in_cryptauth_ = false;
cryptauth::FakeCryptAuthEnrollmentManager* instance_ = nullptr;
};
class FakeRemoteDeviceProviderFactory
: public cryptauth::RemoteDeviceProviderImpl::Factory {
public:
FakeRemoteDeviceProviderFactory(
const cryptauth::RemoteDeviceList& initial_devices,
identity::IdentityManager* identity_manager,
FakeCryptAuthDeviceManagerFactory* fake_cryptauth_device_manager_factory,
FakeCryptAuthEnrollmentManagerFactory*
fake_cryptauth_enrollment_manager_factory)
: initial_devices_(initial_devices),
identity_manager_(identity_manager),
fake_cryptauth_device_manager_factory_(
fake_cryptauth_device_manager_factory),
fake_cryptauth_enrollment_manager_factory_(
fake_cryptauth_enrollment_manager_factory) {}
~FakeRemoteDeviceProviderFactory() override = default;
cryptauth::FakeRemoteDeviceProvider* instance() { return instance_; }
// cryptauth::RemoteDeviceProviderImpl::Factory:
std::unique_ptr<cryptauth::RemoteDeviceProvider> BuildInstance(
cryptauth::CryptAuthDeviceManager* device_manager,
const std::string& user_id,
const std::string& user_private_key) override {
EXPECT_EQ(fake_cryptauth_device_manager_factory_->instance(),
device_manager);
EXPECT_EQ(identity_manager_->GetPrimaryAccountInfo().account_id, user_id);
EXPECT_EQ(fake_cryptauth_enrollment_manager_factory_->instance()
->GetUserPrivateKey(),
user_private_key);
// Only one instance is expected to be created per test.
EXPECT_FALSE(instance_);
auto instance = std::make_unique<cryptauth::FakeRemoteDeviceProvider>();
instance->set_synced_remote_devices(initial_devices_);
instance_ = instance.get();
return std::move(instance);
}
private:
const cryptauth::RemoteDeviceList& initial_devices_;
identity::IdentityManager* identity_manager_;
FakeCryptAuthDeviceManagerFactory* fake_cryptauth_device_manager_factory_;
FakeCryptAuthEnrollmentManagerFactory*
fake_cryptauth_enrollment_manager_factory_;
cryptauth::FakeRemoteDeviceProvider* instance_ = nullptr;
};
class FakeURLRequestContextGetter : public net::URLRequestContextGetter {
public:
FakeURLRequestContextGetter() : null_task_runner_(new base::NullTaskRunner) {}
net::URLRequestContext* GetURLRequestContext() override { return nullptr; }
scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
const override {
return null_task_runner_;
}
private:
~FakeURLRequestContextGetter() override {}
scoped_refptr<base::SingleThreadTaskRunner> null_task_runner_;
};
} // namespace
class DeviceSyncServiceTest : public testing::Test {
public:
DeviceSyncServiceTest() = default;
class FakePrefConnectionDelegate
: public DeviceSyncImpl::PrefConnectionDelegate {
public:
FakePrefConnectionDelegate(
std::unique_ptr<TestingPrefServiceSimple> test_pref_service)
: test_pref_service_(std::move(test_pref_service)),
test_pref_registry_(
base::WrapRefCounted(test_pref_service_->registry())) {}
~FakePrefConnectionDelegate() override = default;
void InvokePendingCallback() {
EXPECT_FALSE(pending_callback_.is_null());
std::move(pending_callback_).Run(std::move(test_pref_service_));
// Note: |pending_callback_| was passed from within the service, so it is
// necessary to let the rest of the current RunLoop run to ensure that
// the callback is executed before returning from this function.
base::RunLoop().RunUntilIdle();
}
bool HasStartedPrefConnection() {
return HasFinishedPrefConnection() || !pending_callback_.is_null();
}
bool HasFinishedPrefConnection() { return !test_pref_service_.get(); }
// DeviceSyncImpl::PrefConnectionDelegate:
scoped_refptr<PrefRegistrySimple> CreatePrefRegistry() override {
return test_pref_registry_;
}
void ConnectToPrefService(service_manager::Connector* connector,
scoped_refptr<PrefRegistrySimple> pref_registry,
prefs::ConnectCallback callback) override {
EXPECT_EQ(test_pref_service_->registry(), pref_registry.get());
pending_callback_ = std::move(callback);
}
private:
std::unique_ptr<TestingPrefServiceSimple> test_pref_service_;
scoped_refptr<PrefRegistrySimple> test_pref_registry_;
prefs::ConnectCallback pending_callback_;
};
class FakeDeviceSyncImplFactory : public DeviceSyncImpl::Factory {
public:
FakeDeviceSyncImplFactory(std::unique_ptr<FakePrefConnectionDelegate>
fake_pref_connection_delegate,
base::SimpleTestClock* simple_test_clock)
: fake_pref_connection_delegate_(
std::move(fake_pref_connection_delegate)),
simple_test_clock_(simple_test_clock) {}
~FakeDeviceSyncImplFactory() override = default;
// DeviceSyncImpl::Factory:
std::unique_ptr<DeviceSyncImpl> BuildInstance(
identity::IdentityManager* identity_manager,
gcm::GCMDriver* gcm_driver,
service_manager::Connector* connector,
cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
scoped_refptr<net::URLRequestContextGetter> url_request_context)
override {
return base::WrapUnique(new DeviceSyncImpl(
identity_manager, gcm_driver, connector, gcm_device_info_provider,
std::move(url_request_context), simple_test_clock_,
std::move(fake_pref_connection_delegate_)));
}
private:
std::unique_ptr<FakePrefConnectionDelegate> fake_pref_connection_delegate_;
base::SimpleTestClock* simple_test_clock_;
};
DeviceSyncServiceTest() : test_devices_(GenerateTestRemoteDevices()) {}
~DeviceSyncServiceTest() override = default;
void SetUp() override {
DBusThreadManager::Initialize();
fake_gcm_driver_ = std::make_unique<gcm::FakeGCMDriver>();
auto test_pref_service = std::make_unique<TestingPrefServiceSimple>();
test_pref_service_ = test_pref_service.get();
simple_test_clock_ = std::make_unique<base::SimpleTestClock>();
// Note: The primary account is guaranteed to be available when the service
// starts up since this is a CrOS-only service, and CrOS requires that
// the user logs in.
identity_test_environment_ =
std::make_unique<identity::IdentityTestEnvironment>();
identity_test_environment_->MakePrimaryAccountAvailable(kTestEmail);
fake_cryptauth_gcm_manager_factory_ =
std::make_unique<FakeCryptAuthGCMManagerFactory>(fake_gcm_driver_.get(),
test_pref_service_);
cryptauth::CryptAuthGCMManagerImpl::Factory::SetInstanceForTesting(
fake_cryptauth_gcm_manager_factory_.get());
fake_cryptauth_device_manager_factory_ =
std::make_unique<FakeCryptAuthDeviceManagerFactory>(
simple_test_clock_.get(), fake_cryptauth_gcm_manager_factory_.get(),
test_pref_service_);
cryptauth::CryptAuthDeviceManagerImpl::Factory::SetInstanceForTesting(
fake_cryptauth_device_manager_factory_.get());
fake_cryptauth_enrollment_manager_factory_ =
std::make_unique<FakeCryptAuthEnrollmentManagerFactory>(
simple_test_clock_.get(), fake_cryptauth_gcm_manager_factory_.get(),
test_pref_service_);
cryptauth::CryptAuthEnrollmentManagerImpl::Factory::SetInstanceForTesting(
fake_cryptauth_enrollment_manager_factory_.get());
fake_remote_device_provider_factory_ =
std::make_unique<FakeRemoteDeviceProviderFactory>(
test_devices_, identity_test_environment_->identity_manager(),
fake_cryptauth_device_manager_factory_.get(),
fake_cryptauth_enrollment_manager_factory_.get());
cryptauth::RemoteDeviceProviderImpl::Factory::SetInstanceForTesting(
fake_remote_device_provider_factory_.get());
auto fake_pref_connection_delegate =
std::make_unique<FakePrefConnectionDelegate>(
std::move(test_pref_service));
fake_pref_connection_delegate_ = fake_pref_connection_delegate.get();
fake_device_sync_impl_factory_ =
std::make_unique<FakeDeviceSyncImplFactory>(
std::move(fake_pref_connection_delegate), simple_test_clock_.get());
DeviceSyncImpl::Factory::SetInstanceForTesting(
fake_device_sync_impl_factory_.get());
fake_gcm_device_info_provider_ =
std::make_unique<cryptauth::FakeGcmDeviceInfoProvider>(
GetTestGcmDeviceInfo());
fake_url_request_context_getter_ =
base::MakeRefCounted<FakeURLRequestContextGetter>();
fake_device_sync_observer_ = std::make_unique<FakeDeviceSyncObserver>();
connector_factory_ =
service_manager::TestConnectorFactory::CreateForUniqueService(
std::make_unique<DeviceSyncService>());
std::make_unique<DeviceSyncService>(
identity_test_environment_->identity_manager(),
fake_gcm_driver_.get(), fake_gcm_device_info_provider_.get(),
fake_url_request_context_getter_.get()));
}
mojom::DeviceSync* GetDeviceSync() {
if (!device_sync_) {
EXPECT_EQ(nullptr, connector_);
void TearDown() override { DBusThreadManager::Shutdown(); }
void ConnectToDeviceSyncService(bool device_already_enrolled_in_cryptauth) {
// Used in CompleteConnectionToPrefService().
device_already_enrolled_in_cryptauth_ =
device_already_enrolled_in_cryptauth;
fake_cryptauth_enrollment_manager_factory_
->set_device_already_enrolled_in_cryptauth(
device_already_enrolled_in_cryptauth);
// Must not have already connected.
EXPECT_FALSE(connector_);
// Create the Connector and bind it to |device_sync_|.
connector_ = connector_factory_->CreateConnector();
connector_->BindInterface(mojom::kServiceName, &device_sync_);
// Create the Connector and bind it to |device_sync_|.
connector_ = connector_factory_->CreateConnector();
connector_->BindInterface(mojom::kServiceName, &device_sync_);
// Set |fake_device_sync_observer_|.
CallAddObserver();
}
void CompleteConnectionToPrefService() {
EXPECT_TRUE(fake_pref_connection_delegate()->HasStartedPrefConnection());
EXPECT_FALSE(fake_pref_connection_delegate()->HasFinishedPrefConnection());
fake_pref_connection_delegate_->InvokePendingCallback();
EXPECT_TRUE(fake_pref_connection_delegate()->HasFinishedPrefConnection());
// When connection to preferences is complete, CryptAuth classes are
// expected to be created and initialized.
EXPECT_TRUE(fake_cryptauth_gcm_manager_factory_->instance()
->has_started_listening());
EXPECT_TRUE(
fake_cryptauth_enrollment_manager_factory_->instance()->has_started());
// If the device was already enrolled in CryptAuth, initialization should
// now be complete; otherwise, enrollment needs to finish before
// the flow has finished up.
VerifyInitializationStatus(
device_already_enrolled_in_cryptauth_ /* expected_to_be_initialized */);
if (!device_already_enrolled_in_cryptauth_)
return;
// Now that the service is initialized, RemoteDeviceProvider is expected to
// load all relevant RemoteDevice objects.
fake_remote_device_provider_factory_->instance()
->NotifyObserversDeviceListChanged();
}
// Set |fake_device_sync_observer_|.
CallAddObserver();
void VerifyInitializationStatus(bool expected_to_be_initialized) {
// CryptAuthDeviceManager::Start() is called as the last step of the
// initialization flow.
EXPECT_EQ(
expected_to_be_initialized,
fake_cryptauth_device_manager_factory_->instance()->has_started());
}
// Simulates an enrollment with success == |success|. If enrollment was not
// yet in progress before this call, it is started before it is completed.
void SimulateEnrollment(bool success) {
cryptauth::FakeCryptAuthEnrollmentManager* enrollment_manager =
fake_cryptauth_enrollment_manager_factory_->instance();
bool had_valid_enrollment_before_call =
enrollment_manager->IsEnrollmentValid();
if (!enrollment_manager->IsEnrollmentInProgress()) {
enrollment_manager->ForceEnrollmentNow(
cryptauth::InvocationReason::INVOCATION_REASON_MANUAL);
}
enrollment_manager->FinishActiveEnrollment(success);
// If this was the first successful enrollment for this device,
// RemoteDeviceProvider is expected to load all relevant RemoteDevice
// objects.
if (success && !had_valid_enrollment_before_call) {
fake_remote_device_provider_factory_->instance()
->NotifyObserversDeviceListChanged();
}
}
// Simulates a device sync with success == |success|. Optionally, if
// |updated_devices| is provided, these devices will set on the
// FakeRemoteDeviceProvider.
void SimulateSync(bool success,
const cryptauth::RemoteDeviceList& updated_devices =
cryptauth::RemoteDeviceList()) {
cryptauth::FakeCryptAuthDeviceManager* device_manager =
fake_cryptauth_device_manager_factory_->instance();
cryptauth::FakeRemoteDeviceProvider* remote_device_provider =
fake_remote_device_provider_factory_->instance();
EXPECT_TRUE(device_manager->IsSyncInProgress());
device_manager->FinishActiveSync(
success ? cryptauth::CryptAuthDeviceManager::SyncResult::SUCCESS
: cryptauth::CryptAuthDeviceManager::SyncResult::FAILURE,
updated_devices.empty()
? cryptauth::CryptAuthDeviceManager::DeviceChangeResult::UNCHANGED
: cryptauth::CryptAuthDeviceManager::DeviceChangeResult::CHANGED);
if (!updated_devices.empty()) {
remote_device_provider->set_synced_remote_devices(updated_devices);
remote_device_provider->NotifyObserversDeviceListChanged();
}
}
void InitializeServiceSuccessfully() {
ConnectToDeviceSyncService(true /* device_already_enrolled_in_cryptauth */);
CompleteConnectionToPrefService();
VerifyInitializationStatus(true /* expected_to_be_initialized */);
return device_sync_.get();
base::RunLoop().RunUntilIdle();
// Enrollment did not occur since the device was already in a valid state.
EXPECT_EQ(0u, fake_device_sync_observer()->num_enrollment_events());
// The initial set of synced devices was set.
EXPECT_EQ(1u, fake_device_sync_observer()->num_sync_events());
}
const cryptauth::RemoteDeviceList& test_devices() { return test_devices_; }
FakeDeviceSyncObserver* fake_device_sync_observer() {
return fake_device_sync_observer_.get();
}
FakePrefConnectionDelegate* fake_pref_connection_delegate() {
return fake_pref_connection_delegate_;
}
void CallAddObserver() {
base::RunLoop run_loop;
GetDeviceSync()->AddObserver(
device_sync_->AddObserver(
fake_device_sync_observer_->GenerateInterfacePtr(),
base::BindOnce(&DeviceSyncServiceTest::OnObserverRegistered,
base::BindOnce(&DeviceSyncServiceTest::OnAddObserverCompleted,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
}
void CallForceEnrollmentNow() {
bool CallForceEnrollmentNow() {
base::RunLoop run_loop;
device_sync_->ForceEnrollmentNow(
base::BindOnce(&DeviceSyncServiceTest::OnForceEnrollmentNowCompleted,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
if (fake_cryptauth_enrollment_manager_factory_->instance()) {
EXPECT_EQ(last_force_enrollment_now_result_,
fake_cryptauth_enrollment_manager_factory_->instance()
->IsEnrollmentInProgress());
}
FakeDeviceSyncObserverDelegate delegate(run_loop.QuitClosure());
fake_device_sync_observer()->SetDelegate(&delegate);
return last_force_enrollment_now_result_;
}
GetDeviceSync()->ForceEnrollmentNow();
bool CallForceSyncNow() {
base::RunLoop run_loop;
device_sync_->ForceSyncNow(
base::BindOnce(&DeviceSyncServiceTest::OnForceSyncNowCompleted,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
fake_device_sync_observer()->SetDelegate(nullptr);
if (fake_cryptauth_device_manager_factory_->instance()) {
EXPECT_EQ(last_force_sync_now_result_,
fake_cryptauth_device_manager_factory_->instance()
->IsSyncInProgress());
}
return last_force_sync_now_result_;
}
void CallForceSyncNow() {
const cryptauth::RemoteDeviceList& CallGetSyncedDevices() {
base::RunLoop run_loop;
device_sync_->GetSyncedDevices(
base::BindOnce(&DeviceSyncServiceTest::OnGetSyncedDevicesCompleted,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
return last_synced_devices_result_;
}
FakeDeviceSyncObserverDelegate delegate(run_loop.QuitClosure());
fake_device_sync_observer()->SetDelegate(&delegate);
private:
void OnAddObserverCompleted(base::OnceClosure quit_closure) {
std::move(quit_closure).Run();
}
GetDeviceSync()->ForceSyncNow();
run_loop.Run();
void OnForceEnrollmentNowCompleted(base::OnceClosure quit_closure,
bool success) {
last_force_enrollment_now_result_ = success;
std::move(quit_closure).Run();
}
fake_device_sync_observer()->SetDelegate(nullptr);
void OnForceSyncNowCompleted(base::OnceClosure quit_closure, bool success) {
last_force_sync_now_result_ = success;
std::move(quit_closure).Run();
}
private:
void OnObserverRegistered(base::OnceClosure quit_closure) {
void OnGetSyncedDevicesCompleted(
base::OnceClosure quit_closure,
const cryptauth::RemoteDeviceList& synced_devices) {
last_synced_devices_result_ = synced_devices;
std::move(quit_closure).Run();
}
const base::test::ScopedTaskEnvironment scoped_task_environment_;
const cryptauth::RemoteDeviceList test_devices_;
TestingPrefServiceSimple* test_pref_service_;
FakePrefConnectionDelegate* fake_pref_connection_delegate_;
std::unique_ptr<base::SimpleTestClock> simple_test_clock_;
std::unique_ptr<FakeDeviceSyncImplFactory> fake_device_sync_impl_factory_;
std::unique_ptr<FakeCryptAuthGCMManagerFactory>
fake_cryptauth_gcm_manager_factory_;
std::unique_ptr<FakeCryptAuthDeviceManagerFactory>
fake_cryptauth_device_manager_factory_;
std::unique_ptr<FakeCryptAuthEnrollmentManagerFactory>
fake_cryptauth_enrollment_manager_factory_;
std::unique_ptr<FakeRemoteDeviceProviderFactory>
fake_remote_device_provider_factory_;
std::unique_ptr<identity::IdentityTestEnvironment> identity_test_environment_;
std::unique_ptr<gcm::FakeGCMDriver> fake_gcm_driver_;
std::unique_ptr<cryptauth::FakeGcmDeviceInfoProvider>
fake_gcm_device_info_provider_;
scoped_refptr<FakeURLRequestContextGetter> fake_url_request_context_getter_;
std::unique_ptr<service_manager::TestConnectorFactory> connector_factory_;
std::unique_ptr<service_manager::Connector> connector_;
bool device_already_enrolled_in_cryptauth_;
bool last_force_enrollment_now_result_;
bool last_force_sync_now_result_;
cryptauth::RemoteDeviceList last_synced_devices_result_;
std::unique_ptr<FakeDeviceSyncObserver> fake_device_sync_observer_;
mojom::DeviceSyncPtr device_sync_;
DISALLOW_COPY_AND_ASSIGN(DeviceSyncServiceTest);
};
TEST_F(DeviceSyncServiceTest, TestForceEnrollment) {
// Note: Currently, ForceEnrollmentNow() instantly replies with success ==
// true. Enrollment will be implemented in a future CL.
CallForceEnrollmentNow();
EXPECT_EQ(1u, fake_device_sync_observer()->num_enrollment_success_events());
TEST_F(DeviceSyncServiceTest, PreferencesNeverConnect) {
ConnectToDeviceSyncService(false /* device_already_enrolled_in_cryptauth */);
// A connection to the Preferences service should have started.
EXPECT_TRUE(fake_pref_connection_delegate()->HasStartedPrefConnection());
EXPECT_FALSE(fake_pref_connection_delegate()->HasFinishedPrefConnection());
// Do not complete the connection; without this step, the other API functions
// should fail.
EXPECT_FALSE(CallForceEnrollmentNow());
EXPECT_FALSE(CallForceSyncNow());
EXPECT_TRUE(CallGetSyncedDevices().empty());
// No observer callbacks should have been invoked.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0u, fake_device_sync_observer()->num_enrollment_events());
EXPECT_EQ(0u, fake_device_sync_observer()->num_sync_events());
}
TEST_F(DeviceSyncServiceTest,
DeviceNotAlreadyEnrolledInCryptAuth_FailsEnrollment) {
ConnectToDeviceSyncService(false /* device_already_enrolled_in_cryptauth */);
CompleteConnectionToPrefService();
// Simulate enrollment failing.
SimulateEnrollment(false /* success */);
VerifyInitializationStatus(false /* success */);
// Fail again; initialization still should not complete.
SimulateEnrollment(false /* success */);
VerifyInitializationStatus(false /* expected_to_be_initialized */);
// Other API functions should still fail since initialization never completed.
EXPECT_FALSE(CallForceEnrollmentNow());
EXPECT_FALSE(CallForceSyncNow());
EXPECT_TRUE(CallGetSyncedDevices().empty());
// No observer callbacks should have been invoked.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0u, fake_device_sync_observer()->num_enrollment_events());
EXPECT_EQ(0u, fake_device_sync_observer()->num_sync_events());
}
TEST_F(DeviceSyncServiceTest,
DeviceNotAlreadyEnrolledInCryptAuth_FailsEnrollment_ThenSucceeds) {
ConnectToDeviceSyncService(false /* device_already_enrolled_in_cryptauth */);
CompleteConnectionToPrefService();
// Initialization has not yet completed, so no devices should be available.
EXPECT_TRUE(CallGetSyncedDevices().empty());
// Simulate enrollment failing.
SimulateEnrollment(false /* success */);
VerifyInitializationStatus(false /* success */);
// Simulate enrollment succeeding; this should result in a fully-initialized
// service.
SimulateEnrollment(true /* success */);
VerifyInitializationStatus(true /* expected_to_be_initialized */);
// Enrollment occurred successfully, and the initial set of synced devices was
// set.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, fake_device_sync_observer()->num_enrollment_events());
EXPECT_EQ(1u, fake_device_sync_observer()->num_sync_events());
// Now that the service is initialized, API functions should be operation and
// synced devices should be available.
EXPECT_TRUE(CallForceEnrollmentNow());
EXPECT_TRUE(CallForceSyncNow());
EXPECT_EQ(test_devices(), CallGetSyncedDevices());
}
TEST_F(DeviceSyncServiceTest,
DeviceAlreadyEnrolledInCryptAuth_InitializationFlow) {
InitializeServiceSuccessfully();
// Now that the service is initialized, API functions should be operation and
// synced devices should be available.
EXPECT_TRUE(CallForceEnrollmentNow());
EXPECT_TRUE(CallForceSyncNow());
EXPECT_EQ(test_devices(), CallGetSyncedDevices());
}
TEST_F(DeviceSyncServiceTest, EnrollAgainAfterInitialization) {
InitializeServiceSuccessfully();
// Force an enrollment.
EXPECT_TRUE(CallForceEnrollmentNow());
// Simulate that enrollment failing.
SimulateEnrollment(false /* success */);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0u, fake_device_sync_observer()->num_enrollment_events());
CallForceEnrollmentNow();
EXPECT_EQ(2u, fake_device_sync_observer()->num_enrollment_success_events());
// Force an enrollment again.
EXPECT_TRUE(CallForceEnrollmentNow());
// This time, simulate the enrollment succeeding.
SimulateEnrollment(true /* success */);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, fake_device_sync_observer()->num_enrollment_events());
}
TEST_F(DeviceSyncServiceTest, TestForceSync) {
// Note: Currently, ForceSyncNow() instantly replies with success == true.
// Syncing will be implemented in a future CL.
CallForceSyncNow();
EXPECT_EQ(1u, fake_device_sync_observer()->num_sync_success_events());
TEST_F(DeviceSyncServiceTest, SyncedDeviceUpdates) {
InitializeServiceSuccessfully();
EXPECT_EQ(1u, fake_device_sync_observer()->num_sync_events());
// Force a device sync.
EXPECT_TRUE(CallForceSyncNow());
// Simulate failed sync.
SimulateSync(false /* success */);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, fake_device_sync_observer()->num_sync_events());
// Force a sync again.
EXPECT_TRUE(CallForceSyncNow());
// Simulate successful sync which does not change the synced device list.
SimulateSync(true /* success */);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, fake_device_sync_observer()->num_sync_events());
// Force a sync again.
EXPECT_TRUE(CallForceSyncNow());
// Create a new list which is the same as the initial test devices except that
// the first device is removed.
cryptauth::RemoteDeviceList updated_device_list(test_devices().begin() + 1,
test_devices().end());
EXPECT_EQ(kNumTestDevices - 1, updated_device_list.size());
// Simulate successful sync which does change the synced device list.
SimulateSync(true /* success */, updated_device_list);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2u, fake_device_sync_observer()->num_sync_events());
CallForceSyncNow();
EXPECT_EQ(2u, fake_device_sync_observer()->num_sync_success_events());
// The updated list should be available via GetSyncedDevices().
EXPECT_EQ(updated_device_list, CallGetSyncedDevices());
}
} // namespace device_sync
......
......@@ -12,34 +12,18 @@ FakeDeviceSyncObserver::FakeDeviceSyncObserver() = default;
FakeDeviceSyncObserver::~FakeDeviceSyncObserver() = default;
void FakeDeviceSyncObserver::SetDelegate(Delegate* delegate) {
delegate_ = delegate;
}
mojom::DeviceSyncObserverPtr FakeDeviceSyncObserver::GenerateInterfacePtr() {
mojom::DeviceSyncObserverPtr interface_ptr;
bindings_.AddBinding(this, mojo::MakeRequest(&interface_ptr));
return interface_ptr;
}
void FakeDeviceSyncObserver::OnEnrollmentFinished(bool success) {
if (success)
++num_enrollment_success_events_;
else
++num_enrollment_failure_events_;
if (delegate_)
delegate_->OnEnrollmentFinishedCalled();
void FakeDeviceSyncObserver::OnEnrollmentFinished() {
++num_enrollment_events_;
}
void FakeDeviceSyncObserver::OnDevicesSynced(bool success) {
if (success)
++num_sync_success_events_;
else
++num_sync_failure_events_;
if (delegate_)
delegate_->OnDevicesSyncedCalled();
void FakeDeviceSyncObserver::OnNewDevicesSynced() {
++num_sync_events_;
}
} // namespace device_sync
......
......@@ -19,41 +19,18 @@ class FakeDeviceSyncObserver : public device_sync::mojom::DeviceSyncObserver {
FakeDeviceSyncObserver();
~FakeDeviceSyncObserver() override;
// Receives callbacks when the relevant DeviceSyncObserver functions have been
// handled.
class Delegate {
public:
virtual void OnEnrollmentFinishedCalled() = 0;
virtual void OnDevicesSyncedCalled() = 0;
};
void SetDelegate(Delegate* delegate);
mojom::DeviceSyncObserverPtr GenerateInterfacePtr();
size_t num_enrollment_success_events() {
return num_enrollment_success_events_;
}
size_t num_enrollment_failure_events() {
return num_enrollment_failure_events_;
}
size_t num_sync_success_events() { return num_sync_success_events_; }
size_t num_sync_failure_events() { return num_sync_failure_events_; }
size_t num_enrollment_events() { return num_enrollment_events_; }
size_t num_sync_events() { return num_sync_events_; }
// device_sync::mojom::DeviceSyncObserver:
void OnEnrollmentFinished(bool success) override;
void OnDevicesSynced(bool success) override;
void OnEnrollmentFinished() override;
void OnNewDevicesSynced() override;
private:
Delegate* delegate_ = nullptr;
size_t num_enrollment_success_events_ = 0u;
size_t num_enrollment_failure_events_ = 0u;
size_t num_sync_success_events_ = 0u;
size_t num_sync_failure_events_ = 0u;
size_t num_enrollment_events_ = 0u;
size_t num_sync_events_ = 0u;
mojo::BindingSet<mojom::DeviceSyncObserver> bindings_;
......
......@@ -52,71 +52,43 @@ struct RemoteDevice {
array<BeaconSeed> beacon_seeds;
};
enum ResultCode {
SUCCESS,
ERROR_INTERNAL,
ERROR_NO_VALID_ACCESS_TOKEN,
ERROR_SERVER_FAILED_TO_RESPOND,
ERROR_CANNOT_PARSE_SERVER_RESPONSE,
};
enum PromotableFeature {
EASY_UNLOCK,
};
struct SetCapabilityResponse {
ResultCode result_code;
};
struct IneligibleDevice {
string device_id;
string reason; // Reason why the device is not eligible.
};
struct FindEligibleDevicesResponse {
ResultCode result_code;
// Only set when result_code == SUCCESS.
array<string> eligible_device_ids;
array<IneligibleDevice> ineligible_devices;
};
struct IsCapabilityPromotableResponse {
ResultCode result_code;
// Only set when result_code == SUCCESS.
bool is_promotable;
};
// Properties associated with a device which can be set and retrieved by calls
// to the server.
// TODO(khorimoto): Add additional capabilities, including the Tether
// capability.
enum RemoteDeviceCapability {
UNLOCK_KEY,
};
interface DeviceSyncObserver {
// Invoked when the current device has finished enrolling itself (i.e., when
// it has contacted the server to provide device metadata). Devices must
// enroll themselves before other device can establish connections to them,
// and they continue to re-enroll themselves on a periodic basis after that.
OnEnrollmentFinished(bool success);
// Invoked when new devices have been synced from the server.
OnDevicesSynced(bool success);
// Invoked when the current device has successfully completed enrollment. Note
// that enrollment occurs once when the device first starts up, then the
// device re-enrolls periodically (and on-command when ForceEnrollmentNow() is
// called).
OnEnrollmentFinished();
// Invoked when new devices have been synced from the server. Note that this
// function is not invoked if a device sync failed or if a device sync
// succeeded but did not result in a change of devices.
OnNewDevicesSynced();
};
// TODO(khorimoto): Flesh out API.
interface DeviceSync {
// Triggers an enrollment. Result of the enrollment will be relayed via the
// OnEnrollmentFinished() observer function.
ForceEnrollmentNow();
// Triggers a device sync. Result of the sync will be relayed via the
// OnDevicesSynced() observer function.
ForceSyncNow();
// Adds an Observer of this API.
AddObserver(DeviceSyncObserver observer) => ();
// Triggers an enrollment; result is relayed via the OnEnrollmentFinished()
// observer function. Returns whether the call could be completed
// successfully. If the function returns false, this means that the device has
// not yet completed registration with the back-end; clients should observe
// this service and wait for an OnEnrollmentFinished() callback before
// retrying.
[Sync]
ForceEnrollmentNow() => (bool success);
// Triggers a device sync; result is relayed via the OnDevicesSynced()
// observer function. Returns whether the call could be completed
// successfully. If the function returns false, this means that the device has
// not yet completed registration with the back-end; clients should observe
// this service and wait for an OnEnrollmentFinished() callback before
// retrying.
[Sync]
ForceSyncNow() => (bool success);
// Returns all synced devices associated with the primary account.
[Sync]
GetSyncedDevices() => (array<RemoteDevice> devices);
};
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