Commit d971b4ca authored by Kyle Horimoto's avatar Kyle Horimoto Committed by Commit Bot

[CrOS MultiDevice] Implement HostVerifierImpl.

This class verifies that a MultiDevice host device (i.e., a phone) has
been enabled by listening for at least one of the known host features to
become enabled. If the host changes or if the host's features are
disabled, the device is no longer considered verified.

Bug: 824568
Change-Id: Ifa6ed67eb6eb5a77943fe0da3ebc653bb05a7370
Reviewed-on: https://chromium-review.googlesource.com/1136851
Commit-Queue: Kyle Horimoto <khorimoto@chromium.org>
Reviewed-by: default avatarRyan Hansberry <hansberry@chromium.org>
Cr-Commit-Position: refs/heads/master@{#575499}
parent 57750511
......@@ -13,9 +13,14 @@ namespace chromeos {
namespace multidevice_setup {
// Verifies that this device can connect to the currently-set MultiDevice host.
// The verification process consists of creating a Bluetooth connection to the
// device, performing an authentication handshake, and enabling the per-device
// features which are supported.
// In order for a host device to be considered set, its BETTER_TOGETHER_HOST
// software feature must be enabled, and in order for a host device to be
// considered verified, at least one of its other host software features must be
// enabled.
//
// HostVerifier waits for that situation to occur and has the ability (via its
// AttemptVerificationNow() function) to send a tickle message to the phone to
// ask it to enable its software features.
class HostVerifier {
public:
class Observer {
......@@ -26,9 +31,9 @@ class HostVerifier {
virtual ~HostVerifier();
// Returns whether the host has completed verification; note that if
// verification is still in the process of being completed but has not
// finished, this function still returns false.
// Returns whether verification for the current MultiDevice host device has
// completed (see description above). If no MultiDevice host is set at all,
// false is returned.
virtual bool IsHostVerified() = 0;
// Attempts the verification flow; successful completion of the flow is
......
......@@ -7,13 +7,48 @@
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
#include "chromeos/services/secure_channel/public/cpp/client/secure_channel_client.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 {
// Software features which, when enabled, represent a verified host.
constexpr const cryptauth::SoftwareFeature kPotentialHostFeatures[] = {
cryptauth::SoftwareFeature::EASY_UNLOCK_HOST,
cryptauth::SoftwareFeature::MAGIC_TETHER_HOST,
cryptauth::SoftwareFeature::SMS_CONNECT_HOST};
// Name of the preference containing the time (in milliseconds since Unix
// epoch) at which a verification attempt should be retried. If the preference
// value is kTimestampNotSet, no retry is scheduled.
const char kRetryTimestampPrefName[] =
"multidevice_setup.current_retry_timestamp_ms";
// Value set for the kRetryTimestampPrefName preference when no retry attempt is
// underway (i.e., verification is complete or there is no current host).
const int64_t kTimestampNotSet = 0;
// Name of the preference containing the time delta (in ms) between the
// timestamp present in the kRetryTimestampPrefName preference and the attempt
// before that one. If the value of kRetryTimestampPrefName is kTimestampNotSet,
// the value at this preference is meaningless.
const char kLastUsedTimeDeltaMsPrefName[] =
"multidevice_setup.last_used_time_delta_ms";
// Delta to set for the first retry.
constexpr const base::TimeDelta kFirstRetryDelta =
base::TimeDelta::FromMinutes(10);
// The multiplier for increasing the backoff timer between retries.
const double kExponentialBackoffMultiplier = 1.5;
} // namespace
// static
HostVerifierImpl::Factory* HostVerifierImpl::Factory::test_factory_ = nullptr;
......@@ -36,40 +71,171 @@ HostVerifierImpl::Factory::~Factory() = default;
std::unique_ptr<HostVerifier> HostVerifierImpl::Factory::BuildInstance(
HostBackendDelegate* host_backend_delegate,
device_sync::DeviceSyncClient* device_sync_client,
secure_channel::SecureChannelClient* secure_channel_client) {
return base::WrapUnique(new HostVerifierImpl(
host_backend_delegate, device_sync_client, secure_channel_client));
PrefService* pref_service,
base::Clock* clock,
std::unique_ptr<base::OneShotTimer> timer) {
return base::WrapUnique(new HostVerifierImpl(host_backend_delegate,
device_sync_client, pref_service,
clock, std::move(timer)));
}
// static
void HostVerifierImpl::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterInt64Pref(kRetryTimestampPrefName, kTimestampNotSet);
registry->RegisterInt64Pref(kLastUsedTimeDeltaMsPrefName, 0);
}
HostVerifierImpl::HostVerifierImpl(
HostBackendDelegate* host_backend_delegate,
device_sync::DeviceSyncClient* device_sync_client,
secure_channel::SecureChannelClient* secure_channel_client)
PrefService* pref_service,
base::Clock* clock,
std::unique_ptr<base::OneShotTimer> timer)
: host_backend_delegate_(host_backend_delegate),
device_sync_client_(device_sync_client),
secure_channel_client_(secure_channel_client) {
pref_service_(pref_service),
clock_(clock),
timer_(std::move(timer)) {
host_backend_delegate_->AddObserver(this);
device_sync_client_->AddObserver(this);
UpdateRetryState();
}
HostVerifierImpl::~HostVerifierImpl() {
host_backend_delegate_->RemoveObserver(this);
device_sync_client_->RemoveObserver(this);
}
bool HostVerifierImpl::IsHostVerified() {
NOTIMPLEMENTED();
// Use both |device_sync_client_| and |secure_channel_client_| to prevent
// unused field compiler warning.
return static_cast<void*>(device_sync_client_) ==
static_cast<void*>(secure_channel_client_);
base::Optional<cryptauth::RemoteDeviceRef> current_host =
host_backend_delegate_->GetMultiDeviceHostFromBackend();
if (!current_host)
return false;
// If one or more potential host sofware features is enabled, the host is
// considered verified.
for (const auto& software_feature : kPotentialHostFeatures) {
if (current_host->GetSoftwareFeatureState(software_feature) ==
cryptauth::SoftwareFeatureState::kEnabled) {
return true;
}
}
return false;
}
void HostVerifierImpl::PerformAttemptVerificationNow() {
NOTIMPLEMENTED();
AttemptHostVerification();
}
void HostVerifierImpl::OnHostChangedOnBackend() {
NOTIMPLEMENTED();
UpdateRetryState();
}
void HostVerifierImpl::OnNewDevicesSynced() {
UpdateRetryState();
}
void HostVerifierImpl::UpdateRetryState() {
// If there is no host, verification is not applicable.
if (!host_backend_delegate_->GetMultiDeviceHostFromBackend()) {
StopTimerAndClearPrefs();
return;
}
// If there is a host and it is verified, verification is no longer necessary.
if (IsHostVerified()) {
bool was_timer_running = timer_->IsRunning();
StopTimerAndClearPrefs();
if (was_timer_running)
NotifyHostVerified();
return;
}
// If |timer_| is running, an ongoing retry attempt is in progress.
if (timer_->IsRunning())
return;
int64_t timestamp_from_prefs =
pref_service_->GetInt64(kRetryTimestampPrefName);
// If no retry timer was set, set the timer to the initial value and attempt
// to verify now.
if (timestamp_from_prefs == kTimestampNotSet) {
AttemptVerificationWithInitialTimeout();
return;
}
base::Time retry_time_from_prefs =
base::Time::FromJavaTime(timestamp_from_prefs);
// If a timeout value was set but has not yet occurred, start the timer.
if (clock_->Now() < retry_time_from_prefs) {
StartTimer(retry_time_from_prefs);
return;
}
AttemptVerificationAfterInitialTimeout(retry_time_from_prefs);
}
void HostVerifierImpl::StopTimerAndClearPrefs() {
timer_->Stop();
pref_service_->SetInt64(kRetryTimestampPrefName, kTimestampNotSet);
pref_service_->SetInt64(kLastUsedTimeDeltaMsPrefName, 0);
}
void HostVerifierImpl::AttemptVerificationWithInitialTimeout() {
base::Time retry_time = clock_->Now() + kFirstRetryDelta;
pref_service_->SetInt64(kRetryTimestampPrefName, retry_time.ToJavaTime());
pref_service_->SetInt64(kLastUsedTimeDeltaMsPrefName,
kFirstRetryDelta.InMilliseconds());
StartTimer(retry_time);
AttemptHostVerification();
}
void HostVerifierImpl::AttemptVerificationAfterInitialTimeout(
const base::Time& retry_time_from_prefs) {
int64_t time_delta_ms = pref_service_->GetInt64(kLastUsedTimeDeltaMsPrefName);
DCHECK(time_delta_ms > 0);
base::Time retry_time = retry_time_from_prefs;
while (clock_->Now() >= retry_time) {
time_delta_ms *= kExponentialBackoffMultiplier;
retry_time += base::TimeDelta::FromMilliseconds(time_delta_ms);
}
pref_service_->SetInt64(kRetryTimestampPrefName, retry_time.ToJavaTime());
pref_service_->SetInt64(kLastUsedTimeDeltaMsPrefName, time_delta_ms);
StartTimer(retry_time);
AttemptHostVerification();
}
void HostVerifierImpl::StartTimer(const base::Time& time_to_fire) {
base::Time now = clock_->Now();
DCHECK(now < time_to_fire);
timer_->Start(
FROM_HERE, time_to_fire - now /* delay */,
base::Bind(&HostVerifierImpl::UpdateRetryState, base::Unretained(this)));
}
void HostVerifierImpl::AttemptHostVerification() {
base::Optional<cryptauth::RemoteDeviceRef> current_host =
host_backend_delegate_->GetMultiDeviceHostFromBackend();
if (!current_host) {
PA_LOG(WARNING) << "HostVerifierImpl::AttemptHostVerification(): Cannot "
<< "attempt verification because there is no active host.";
return;
}
PA_LOG(INFO) << "HostVerifierImpl::AttemptHostVerification(): Attempting "
<< "host verification now.";
device_sync_client_->FindEligibleDevices(
cryptauth::SoftwareFeature::BETTER_TOGETHER_HOST, base::DoNothing());
}
} // namespace multidevice_setup
......
......@@ -5,19 +5,19 @@
#ifndef CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_VERIFIER_IMPL_H_
#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_VERIFIER_IMPL_H_
#include <memory>
#include "base/macros.h"
#include "base/time/default_clock.h"
#include "base/timer/timer.h"
#include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
#include "chromeos/services/multidevice_setup/host_backend_delegate.h"
#include "chromeos/services/multidevice_setup/host_verifier.h"
namespace chromeos {
namespace device_sync {
class DeviceSyncClient;
} // namespace device_sync
class PrefRegistrySimple;
class PrefService;
namespace secure_channel {
class SecureChannelClient;
} // namespace secure_channel
namespace chromeos {
namespace multidevice_setup {
......@@ -28,10 +28,9 @@ namespace multidevice_setup {
// If the MultiDevice host is changed while verification is in progress, the
// previous verification attempt is canceled and a new attempt begins with the
// updated device.
//
// TODO(khorimoto): Fill out implementation.
class HostVerifierImpl : public HostVerifier,
public HostBackendDelegate::Observer {
public HostBackendDelegate::Observer,
public device_sync::DeviceSyncClient::Observer {
public:
class Factory {
public:
......@@ -41,18 +40,25 @@ class HostVerifierImpl : public HostVerifier,
virtual std::unique_ptr<HostVerifier> BuildInstance(
HostBackendDelegate* host_backend_delegate,
device_sync::DeviceSyncClient* device_sync_client,
secure_channel::SecureChannelClient* secure_channel_client);
PrefService* pref_service,
base::Clock* clock = base::DefaultClock::GetInstance(),
std::unique_ptr<base::OneShotTimer> timer =
std::make_unique<base::OneShotTimer>());
private:
static Factory* test_factory_;
};
static void RegisterPrefs(PrefRegistrySimple* registry);
~HostVerifierImpl() override;
private:
HostVerifierImpl(HostBackendDelegate* host_backend_delegate,
device_sync::DeviceSyncClient* device_sync_client,
secure_channel::SecureChannelClient* secure_channel_client);
PrefService* pref_service,
base::Clock* clock,
std::unique_ptr<base::OneShotTimer> timer);
// HostVerifier:
bool IsHostVerified() override;
......@@ -61,9 +67,22 @@ class HostVerifierImpl : public HostVerifier,
// HostBackendDelegate::Observer:
void OnHostChangedOnBackend() override;
// device_sync::DeviceSyncClient::Observer:
void OnNewDevicesSynced() override;
void UpdateRetryState();
void StopTimerAndClearPrefs();
void AttemptVerificationWithInitialTimeout();
void AttemptVerificationAfterInitialTimeout(
const base::Time& retry_time_from_prefs);
void StartTimer(const base::Time& time_to_fire);
void AttemptHostVerification();
HostBackendDelegate* host_backend_delegate_;
device_sync::DeviceSyncClient* device_sync_client_;
secure_channel::SecureChannelClient* secure_channel_client_;
PrefService* pref_service_;
base::Clock* clock_;
std::unique_ptr<base::OneShotTimer> timer_;
DISALLOW_COPY_AND_ASSIGN(HostVerifierImpl);
};
......
......@@ -64,7 +64,7 @@ MultiDeviceSetupImpl::MultiDeviceSetupImpl(
host_verifier_(HostVerifierImpl::Factory::Get()->BuildInstance(
host_backend_delegate_.get(),
device_sync_client,
secure_channel_client)),
pref_service)),
host_status_provider_(
HostStatusProviderImpl::Factory::Get()->BuildInstance(
eligible_host_devices_provider_.get(),
......
......@@ -136,10 +136,11 @@ class FakeHostVerifierFactory : public HostVerifierImpl::Factory {
FakeHostVerifierFactory(
FakeHostBackendDelegateFactory* fake_host_backend_delegate_factory,
device_sync::FakeDeviceSyncClient* expected_device_sync_client,
secure_channel::FakeSecureChannelClient* expected_secure_channel_client)
sync_preferences::TestingPrefServiceSyncable*
expected_testing_pref_service)
: fake_host_backend_delegate_factory_(fake_host_backend_delegate_factory),
expected_device_sync_client_(expected_device_sync_client),
expected_secure_channel_client_(expected_secure_channel_client) {}
expected_testing_pref_service_(expected_testing_pref_service) {}
~FakeHostVerifierFactory() override = default;
......@@ -150,12 +151,14 @@ class FakeHostVerifierFactory : public HostVerifierImpl::Factory {
std::unique_ptr<HostVerifier> BuildInstance(
HostBackendDelegate* host_backend_delegate,
device_sync::DeviceSyncClient* device_sync_client,
secure_channel::SecureChannelClient* secure_channel_client) override {
PrefService* pref_service,
base::Clock* clock,
std::unique_ptr<base::OneShotTimer> timer) override {
EXPECT_FALSE(instance_);
EXPECT_EQ(fake_host_backend_delegate_factory_->instance(),
host_backend_delegate);
EXPECT_EQ(expected_device_sync_client_, device_sync_client);
EXPECT_EQ(expected_secure_channel_client_, secure_channel_client);
EXPECT_EQ(expected_testing_pref_service_, pref_service);
auto instance = std::make_unique<FakeHostVerifier>();
instance_ = instance.get();
......@@ -164,7 +167,7 @@ class FakeHostVerifierFactory : public HostVerifierImpl::Factory {
FakeHostBackendDelegateFactory* fake_host_backend_delegate_factory_;
device_sync::FakeDeviceSyncClient* expected_device_sync_client_;
secure_channel::FakeSecureChannelClient* expected_secure_channel_client_;
sync_preferences::TestingPrefServiceSyncable* expected_testing_pref_service_;
FakeHostVerifier* instance_ = nullptr;
......@@ -330,7 +333,7 @@ class MultiDeviceSetupImplTest : public testing::Test {
fake_host_verifier_factory_ = std::make_unique<FakeHostVerifierFactory>(
fake_host_backend_delegate_factory_.get(),
fake_device_sync_client_.get(), fake_secure_channel_client_.get());
fake_device_sync_client_.get(), test_pref_service_.get());
HostVerifierImpl::Factory::SetFactoryForTesting(
fake_host_verifier_factory_.get());
......
......@@ -7,6 +7,7 @@
#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/host_backend_delegate_impl.h"
#include "chromeos/services/multidevice_setup/host_verifier_impl.h"
#include "chromeos/services/multidevice_setup/multidevice_setup_base.h"
#include "chromeos/services/multidevice_setup/multidevice_setup_initializer.h"
......@@ -19,6 +20,7 @@ void MultiDeviceSetupService::RegisterProfilePrefs(
PrefRegistrySimple* registry) {
AccountStatusChangeDelegateNotifierImpl::RegisterPrefs(registry);
HostBackendDelegateImpl::RegisterPrefs(registry);
HostVerifierImpl::RegisterPrefs(registry);
}
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