Commit b4f91407 authored by Claude van der Merwe's avatar Claude van der Merwe Committed by Commit Bot

Add WifiSyncFeatureManager to multidevice setup

WifiFeatureManager adds global enable/disable functionality for
Wifi Sync V2.

WifiFeatureManager asynchronously enables/disables WIFI_SYNC_HOST
for a synced device on the back-end, and provides retry logic if the
network request fails by storing the last pending request in prefs.

Bug: 1117619
Change-Id: Ide945c00e5eb060dd86b11b31e1a830aa1fde3ed
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2419332
Commit-Queue: Claude van der Merwe <cvandermerwe@google.com>
Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Reviewed-by: default avatarJon Mann <jonmann@chromium.org>
Cr-Commit-Position: refs/heads/master@{#811024}
parent dab8c1ff
......@@ -52,6 +52,9 @@ static_library("multidevice_setup") {
"privileged_host_device_setter_base.h",
"privileged_host_device_setter_impl.cc",
"privileged_host_device_setter_impl.h",
"wifi_sync_feature_manager.h",
"wifi_sync_feature_manager_impl.cc",
"wifi_sync_feature_manager_impl.h",
]
deps = [
......@@ -139,6 +142,7 @@ source_set("unit_tests") {
"multidevice_setup_impl_unittest.cc",
"multidevice_setup_service_unittest.cc",
"privileged_host_device_setter_impl_unittest.cc",
"wifi_sync_feature_manager_impl_unittest.cc",
]
deps = [
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_SERVICES_MULTIDEVICE_SETUP_WIFI_SYNC_FEATURE_MANAGER_H_
#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_WIFI_SYNC_FEATURE_MANAGER_H_
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/optional.h"
#include "chromeos/components/multidevice/remote_device_ref.h"
#include "chromeos/services/multidevice_setup/host_status_provider.h"
namespace chromeos {
namespace multidevice_setup {
// Manager for setting and receiving the Wifi Sync Host enabled/disabled state.
// This class is considered the source of truth for the current state of Wifi
// Sync Host.
class WifiSyncFeatureManager {
public:
virtual ~WifiSyncFeatureManager() = default;
WifiSyncFeatureManager(const WifiSyncFeatureManager&) = delete;
WifiSyncFeatureManager& operator=(const WifiSyncFeatureManager&) = delete;
// Attempts to enable/disable Wifi Sync on the backend for the host
// device that is synced at the time SetIsWifiSyncEnabled is called.
virtual void SetIsWifiSyncEnabled(bool enabled) = 0;
// Returns whether Wifi Sync is enabled/disabled.
virtual bool IsWifiSyncEnabled() = 0;
protected:
WifiSyncFeatureManager() = default;
};
} // namespace multidevice_setup
} // namespace chromeos
#endif // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_WIFI_SYNC_FEATURE_MANAGER_H_
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/services/multidevice_setup/wifi_sync_feature_manager_impl.h"
#include <sstream>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/stl_util.h"
#include "chromeos/components/multidevice/logging/logging.h"
#include "chromeos/components/multidevice/software_feature.h"
#include "chromeos/components/multidevice/software_feature_state.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/services/device_sync/feature_status_change.h"
#include "chromeos/services/multidevice_setup/host_status_provider.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
namespace chromeos {
namespace multidevice_setup {
namespace {
const char kPendingWifiSyncRequestEnabledPrefName[] =
"multidevice_setup.pending_set_wifi_sync_enabled_request";
// The number of minutes to wait before retrying a failed attempt.
const int kNumMinutesBetweenRetries = 5;
} // namespace
// static
WifiSyncFeatureManagerImpl::Factory*
WifiSyncFeatureManagerImpl::Factory::test_factory_ = nullptr;
// static
std::unique_ptr<WifiSyncFeatureManager>
WifiSyncFeatureManagerImpl::Factory::Create(
HostStatusProvider* host_status_provider,
PrefService* pref_service,
device_sync::DeviceSyncClient* device_sync_client,
std::unique_ptr<base::OneShotTimer> timer) {
if (test_factory_) {
return test_factory_->CreateInstance(host_status_provider, pref_service,
device_sync_client, std::move(timer));
}
return base::WrapUnique(
new WifiSyncFeatureManagerImpl(host_status_provider, pref_service,
device_sync_client, std::move(timer)));
}
// static
void WifiSyncFeatureManagerImpl::Factory::SetFactoryForTesting(
Factory* test_factory) {
test_factory_ = test_factory;
}
WifiSyncFeatureManagerImpl::Factory::~Factory() = default;
void WifiSyncFeatureManagerImpl::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterIntegerPref(kPendingWifiSyncRequestEnabledPrefName,
static_cast<int>(PendingState::kPendingNone));
}
WifiSyncFeatureManagerImpl::~WifiSyncFeatureManagerImpl() = default;
WifiSyncFeatureManagerImpl::WifiSyncFeatureManagerImpl(
HostStatusProvider* host_status_provider,
PrefService* pref_service,
device_sync::DeviceSyncClient* device_sync_client,
std::unique_ptr<base::OneShotTimer> timer)
: WifiSyncFeatureManager(),
host_status_provider_(host_status_provider),
pref_service_(pref_service),
device_sync_client_(device_sync_client),
timer_(std::move(timer)) {
host_status_provider_->AddObserver(this);
device_sync_client_->AddObserver(this);
if (GetCurrentState() == CurrentState::kValidPendingRequest) {
AttemptSetWifiSyncHostStateNetworkRequest(false /* is_retry */);
}
}
void WifiSyncFeatureManagerImpl::OnHostStatusChange(
const HostStatusProvider::HostStatusWithDevice& host_status_with_device) {
if (GetCurrentState() == CurrentState::kNoVerifiedHost) {
ResetPendingWifiSyncHostNetworkRequest();
}
}
void WifiSyncFeatureManagerImpl::OnNewDevicesSynced() {
if (GetCurrentState() != CurrentState::kValidPendingRequest) {
ResetPendingWifiSyncHostNetworkRequest();
}
}
void WifiSyncFeatureManagerImpl::SetIsWifiSyncEnabled(bool enabled) {
if (GetCurrentState() == CurrentState::kNoVerifiedHost) {
ResetPendingWifiSyncHostNetworkRequest();
PA_LOG(ERROR)
<< "WifiSyncFeatureManagerImpl::SetIsWifiSyncEnabled: Network request "
"not attempted because there is No Verified Host";
return;
}
SetPendingWifiSyncHostNetworkRequest(enabled ? PendingState::kPendingEnable
: PendingState::kPendingDisable);
// Stop timer since new attempt is started.
timer_->Stop();
AttemptSetWifiSyncHostStateNetworkRequest(false /* is_retry */);
}
bool WifiSyncFeatureManagerImpl::IsWifiSyncEnabled() {
CurrentState current_state = GetCurrentState();
if (current_state == CurrentState::kNoVerifiedHost) {
return false;
}
if (current_state == CurrentState::kValidPendingRequest) {
return GetPendingState() == PendingState::kPendingEnable;
}
return host_status_provider_->GetHostWithStatus()
.host_device()
->GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost) ==
multidevice::SoftwareFeatureState::kEnabled;
}
void WifiSyncFeatureManagerImpl::ResetPendingWifiSyncHostNetworkRequest() {
SetPendingWifiSyncHostNetworkRequest(PendingState::kPendingNone);
timer_->Stop();
}
WifiSyncFeatureManagerImpl::PendingState
WifiSyncFeatureManagerImpl::GetPendingState() {
return static_cast<PendingState>(
pref_service_->GetInteger(kPendingWifiSyncRequestEnabledPrefName));
}
WifiSyncFeatureManagerImpl::CurrentState
WifiSyncFeatureManagerImpl::GetCurrentState() {
if (host_status_provider_->GetHostWithStatus().host_status() !=
mojom::HostStatus::kHostVerified) {
return CurrentState::kNoVerifiedHost;
}
if (GetPendingState() == PendingState::kPendingNone) {
return CurrentState::kNoPendingRequest;
}
bool enabled_on_host =
(host_status_provider_->GetHostWithStatus()
.host_device()
->GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost) ==
multidevice::SoftwareFeatureState::kEnabled);
bool pending_enabled = (GetPendingState() == PendingState::kPendingEnable);
if (pending_enabled == enabled_on_host) {
return CurrentState::kPendingMatchesBackend;
}
return CurrentState::kValidPendingRequest;
}
void WifiSyncFeatureManagerImpl::SetPendingWifiSyncHostNetworkRequest(
PendingState pending_state) {
pref_service_->SetInteger(kPendingWifiSyncRequestEnabledPrefName,
static_cast<int>(pending_state));
}
void WifiSyncFeatureManagerImpl::AttemptSetWifiSyncHostStateNetworkRequest(
bool is_retry) {
if (network_request_in_flight_) {
return;
}
bool pending_enabled = (GetPendingState() == PendingState::kPendingEnable);
PA_LOG(INFO) << "WifiSyncFeatureManagerImpl::"
<< "AttemptSetWifiSyncHostStateNetworkRequest(): "
<< (is_retry ? "Retrying attempt" : "Attempting") << " to "
<< (pending_enabled ? "enable" : "disable") << " wifi sync.";
network_request_in_flight_ = true;
multidevice::RemoteDeviceRef host_device =
*host_status_provider_->GetHostWithStatus().host_device();
if (features::ShouldUseV1DeviceSync()) {
// Even if the |device_to_set| has a non-trivial Instance ID, we still
// invoke the v1 DeviceSync RPC to set the feature state. This ensures that
// GmsCore will be notified of the change regardless of what version of
// DeviceSync it is running. The v1 and v2 RPCs to change feature states
// ultimately update the same backend database entry. Note: The
// RemoteDeviceProvider guarantees that every device will have a public key
// while v1 DeviceSync is enabled.
device_sync_client_->SetSoftwareFeatureState(
host_device.public_key(), multidevice::SoftwareFeature::kWifiSyncHost,
pending_enabled /* enabled */, pending_enabled /* is_exclusive */,
base::BindOnce(&WifiSyncFeatureManagerImpl::
OnSetWifiSyncHostStateNetworkRequestFinished,
weak_ptr_factory_.GetWeakPtr(), pending_enabled));
} else {
device_sync_client_->SetFeatureStatus(
host_device.instance_id(), multidevice::SoftwareFeature::kWifiSyncHost,
pending_enabled ? device_sync::FeatureStatusChange::kEnableExclusively
: device_sync::FeatureStatusChange::kDisable,
base::BindOnce(&WifiSyncFeatureManagerImpl::
OnSetWifiSyncHostStateNetworkRequestFinished,
weak_ptr_factory_.GetWeakPtr(), pending_enabled));
}
}
void WifiSyncFeatureManagerImpl::OnSetWifiSyncHostStateNetworkRequestFinished(
bool attempted_to_enable,
device_sync::mojom::NetworkRequestResult result_code) {
network_request_in_flight_ = false;
bool has_valid_pending_request =
(GetCurrentState() == CurrentState::kValidPendingRequest);
if (!has_valid_pending_request) {
ResetPendingWifiSyncHostNetworkRequest();
}
bool success =
(result_code == device_sync::mojom::NetworkRequestResult::kSuccess);
std::stringstream ss;
ss << "WifiSyncFeatureManagerImpl::"
<< "OnSetWifiSyncHostStateNetworkRequestFinished(): "
<< (success ? "Completed successful" : "Failure requesting") << " "
<< "set WIFI_SYNC_HOST "
<< ". Attempted to enable: " << (attempted_to_enable ? "true" : "false");
if (success) {
PA_LOG(VERBOSE) << ss.str();
// If the network request was successful but there is still a pending
// network request then trigger a network request immediately. This could
// happen if there was a second attempt to set the backend while the first
// one was still in progress.
if (has_valid_pending_request) {
AttemptSetWifiSyncHostStateNetworkRequest(false /* is_retry */);
}
return;
}
ss << ", Error code: " << result_code;
PA_LOG(WARNING) << ss.str();
// If the network request failed and there is still a pending network request,
// schedule a retry.
if (has_valid_pending_request) {
timer_->Start(FROM_HERE,
base::TimeDelta::FromMinutes(kNumMinutesBetweenRetries),
base::BindOnce(&WifiSyncFeatureManagerImpl::
AttemptSetWifiSyncHostStateNetworkRequest,
base::Unretained(this), true /* is_retry */));
}
}
} // namespace multidevice_setup
} // namespace chromeos
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_SERVICES_MULTIDEVICE_SETUP_WIFI_SYNC_FEATURE_MANAGER_IMPL_H_
#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_WIFI_SYNC_FEATURE_MANAGER_IMPL_H_
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/timer/timer.h"
#include "chromeos/components/multidevice/remote_device_ref.h"
#include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
#include "chromeos/services/multidevice_setup/wifi_sync_feature_manager.h"
class PrefRegistrySimple;
class PrefService;
namespace chromeos {
namespace multidevice_setup {
// Concrete WifiSyncFeatureManager implementation, which utilizes
// DeviceSyncClient to communicate with the back-end.
//
// This toggles WIFI_SYNC_HOST between enabled/supported on cryptauth for a
// synced phone, where supported is considered disabled by user.
//
// Toggling WIFI_SYNC_HOST is a global action, so it will be reflected on all
// synced devices.
class WifiSyncFeatureManagerImpl
: public WifiSyncFeatureManager,
public HostStatusProvider::Observer,
public device_sync::DeviceSyncClient::Observer {
public:
class Factory {
public:
static std::unique_ptr<WifiSyncFeatureManager> Create(
HostStatusProvider* host_status_provider,
PrefService* pref_service,
device_sync::DeviceSyncClient* device_sync_client,
std::unique_ptr<base::OneShotTimer> timer =
std::make_unique<base::OneShotTimer>());
static void SetFactoryForTesting(Factory* test_factory);
protected:
virtual ~Factory();
virtual std::unique_ptr<WifiSyncFeatureManager> CreateInstance(
HostStatusProvider* host_status_provider,
PrefService* pref_service,
device_sync::DeviceSyncClient* device_sync_client,
std::unique_ptr<base::OneShotTimer> timer) = 0;
private:
static Factory* test_factory_;
};
static void RegisterPrefs(PrefRegistrySimple* registry);
~WifiSyncFeatureManagerImpl() override;
WifiSyncFeatureManagerImpl(const WifiSyncFeatureManagerImpl&) = delete;
WifiSyncFeatureManagerImpl& operator=(const WifiSyncFeatureManagerImpl&) =
delete;
private:
WifiSyncFeatureManagerImpl(HostStatusProvider* host_status_provider,
PrefService* pref_service,
device_sync::DeviceSyncClient* device_sync_client,
std::unique_ptr<base::OneShotTimer> timer);
// HostStatusProvider::Observer,
void OnHostStatusChange(const HostStatusProvider::HostStatusWithDevice&
host_status_with_device) override;
// DeviceSyncClient::Observer:
void OnNewDevicesSynced() override;
// WifiSyncFeatureManager:
// Attempts to enable/disable WIFI_SYNC_HOST on the backend for the host
// device that is synced at the time SetIsWifiSyncEnabled is called.
//
// 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 one of the following happens: the
// request succeeds, SetIsWifiSyncEnabled() called is with different value, or
// the synced host device changes.
//
// If there is already a pending request and this function is called with the
// same request, a retry will be attempted immediately.
void SetIsWifiSyncEnabled(bool enabled) override;
// Returns whether WIFI_SYNC_HOST is enabled/disabled. If there is a pending
// request to enable or disable WIFI_SYNC_HOST, the state that the pending
// request is intending to set WIFI_SYNC_HOST to is returned, otherwise the
// state on the back-end is returned.
bool IsWifiSyncEnabled() override;
// Numerical values cannot be changed because they map to integers that are
// stored persistently in prefs.
enum class PendingState {
kPendingNone = 0,
kPendingEnable = 1,
kPendingDisable = 2
};
enum class CurrentState {
kNoVerifiedHost,
kNoPendingRequest,
kPendingMatchesBackend,
kValidPendingRequest
};
void ResetPendingWifiSyncHostNetworkRequest();
PendingState GetPendingState();
CurrentState GetCurrentState();
void SetPendingWifiSyncHostNetworkRequest(PendingState pending_state);
void AttemptSetWifiSyncHostStateNetworkRequest(bool is_retry);
void OnSetWifiSyncHostStateNetworkRequestFinished(
bool attempted_to_enable,
device_sync::mojom::NetworkRequestResult result_code);
HostStatusProvider* host_status_provider_;
PrefService* pref_service_;
device_sync::DeviceSyncClient* device_sync_client_;
std::unique_ptr<base::OneShotTimer> timer_;
bool network_request_in_flight_ = false;
base::WeakPtrFactory<WifiSyncFeatureManagerImpl> weak_ptr_factory_{this};
};
} // namespace multidevice_setup
} // namespace chromeos
#endif // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_WIFI_SYNC_FEATURE_MANAGER_IMPL_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/services/multidevice_setup/wifi_sync_feature_manager_impl.h"
#include <memory>
#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/test/scoped_feature_list.h"
#include "base/timer/mock_timer.h"
#include "base/unguessable_token.h"
#include "chromeos/components/multidevice/remote_device_test_util.h"
#include "chromeos/components/multidevice/software_feature.h"
#include "chromeos/components/multidevice/software_feature_state.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h"
#include "chromeos/services/multidevice_setup/fake_host_status_provider.h"
#include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.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 kPendingWifiSyncRequestEnabledPrefName[] =
"multidevice_setup.pending_set_wifi_sync_enabled_request";
enum PendingState { PendingNone = 0, PendingEnable = 1, PendingDisable = 2 };
const size_t kNumTestDevices = 4;
} // namespace
class MultiDeviceSetupWifiSyncFeatureManagerImplTest
: public ::testing::TestWithParam<bool> {
protected:
MultiDeviceSetupWifiSyncFeatureManagerImplTest()
: test_devices_(
multidevice::CreateRemoteDeviceRefListForTest(kNumTestDevices)) {}
~MultiDeviceSetupWifiSyncFeatureManagerImplTest() override = default;
// testing::Test:
void SetUp() override {
SetFeatureFlags(GetParam() /* use_v1_devicesync */);
// Tests are run once to simulate when v1 DeviceSync is enabled and once to
// simulate when it is disabled, leaving only v2 DeviceSync operational. In
// the former case, only public keys are needed, and in the latter case,
// only Instance IDs are needed.
for (multidevice::RemoteDeviceRef device : test_devices_) {
if (features::ShouldUseV1DeviceSync())
GetMutableRemoteDevice(device)->instance_id.clear();
else
GetMutableRemoteDevice(device)->public_key.clear();
}
SetWifiSyncSupportedInDeviceSyncClient();
fake_host_status_provider_ = std::make_unique<FakeHostStatusProvider>();
test_pref_service_ =
std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
WifiSyncFeatureManagerImpl::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 {}
void SetHostInDeviceSyncClient(
const base::Optional<multidevice::RemoteDeviceRef>& host_device) {
for (const auto& remote_device : test_devices_) {
bool should_be_host =
host_device != base::nullopt &&
((!remote_device.instance_id().empty() &&
host_device->instance_id() == remote_device.instance_id()) ||
(!remote_device.GetDeviceId().empty() &&
host_device->GetDeviceId() == remote_device.GetDeviceId()));
GetMutableRemoteDevice(remote_device)
->software_features
[multidevice::SoftwareFeature::kBetterTogetherHost] =
should_be_host ? multidevice::SoftwareFeatureState::kEnabled
: multidevice::SoftwareFeatureState::kSupported;
}
fake_device_sync_client_->NotifyNewDevicesSynced();
}
void SetWifiSyncSupportedInDeviceSyncClient() {
for (const auto& remote_device : test_devices_) {
GetMutableRemoteDevice(remote_device)
->software_features[multidevice::SoftwareFeature::kWifiSyncHost] =
multidevice::SoftwareFeatureState::kSupported;
}
}
void CreateDelegate(
const base::Optional<multidevice::RemoteDeviceRef>& initial_host,
int initial_pending_wifi_sync_request = PendingNone) {
SetHostInDeviceSyncClient(initial_host);
test_pref_service_->SetInteger(kPendingWifiSyncRequestEnabledPrefName,
initial_pending_wifi_sync_request);
auto mock_timer = std::make_unique<base::MockOneShotTimer>();
mock_timer_ = mock_timer.get();
SetHostWithStatus(initial_host);
delegate_ = WifiSyncFeatureManagerImpl::Factory::Create(
fake_host_status_provider_.get(), test_pref_service_.get(),
fake_device_sync_client_.get(), std::move(mock_timer));
}
void SetHostWithStatus(
const base::Optional<multidevice::RemoteDeviceRef>& host_device) {
mojom::HostStatus host_status =
(host_device == base::nullopt ? mojom::HostStatus::kNoEligibleHosts
: mojom::HostStatus::kHostVerified);
fake_host_status_provider_->SetHostWithStatus(host_status, host_device);
}
void SetIsWifiSyncEnabled(bool enabled) {
const base::Optional<multidevice::RemoteDeviceRef>& host_device =
fake_host_status_provider_->GetHostWithStatus().host_device();
delegate_->SetIsWifiSyncEnabled(enabled);
if (fake_host_status_provider_->GetHostWithStatus().host_status() !=
mojom::HostStatus::kHostVerified) {
return;
}
bool enabled_on_backend =
(host_device->GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost) ==
multidevice::SoftwareFeatureState::kEnabled);
bool pending_request_state_same_as_backend =
(enabled == enabled_on_backend);
if (pending_request_state_same_as_backend) {
return;
}
VerifyLatestSetWifiSyncHostNetworkRequest(*host_device, enabled);
}
void VerifyLatestSetWifiSyncHostNetworkRequest(
const multidevice::RemoteDeviceRef expected_host,
bool expected_should_enable) {
if (features::ShouldUseV1DeviceSync()) {
ASSERT_FALSE(
fake_device_sync_client_->set_software_feature_state_inputs_queue()
.empty());
const device_sync::FakeDeviceSyncClient::SetSoftwareFeatureStateInputs&
inputs = fake_device_sync_client_
->set_software_feature_state_inputs_queue()
.back();
EXPECT_EQ(expected_host.public_key(), inputs.public_key);
EXPECT_EQ(multidevice::SoftwareFeature::kWifiSyncHost,
inputs.software_feature);
EXPECT_EQ(expected_should_enable, inputs.enabled);
EXPECT_EQ(expected_should_enable, inputs.is_exclusive);
return;
}
// Verify inputs to SetFeatureStatus().
ASSERT_FALSE(
fake_device_sync_client_->set_feature_status_inputs_queue().empty());
const device_sync::FakeDeviceSyncClient::SetFeatureStatusInputs& inputs =
fake_device_sync_client_->set_feature_status_inputs_queue().back();
EXPECT_EQ(expected_host.instance_id(), inputs.device_instance_id);
EXPECT_EQ(multidevice::SoftwareFeature::kWifiSyncHost, inputs.feature);
EXPECT_EQ(expected_should_enable
? device_sync::FeatureStatusChange::kEnableExclusively
: device_sync::FeatureStatusChange::kDisable,
inputs.status_change);
}
int GetSetHostNetworkRequestCallbackQueueSize() {
return features::ShouldUseV1DeviceSync()
? fake_device_sync_client_
->GetSetSoftwareFeatureStateInputsQueueSize()
: fake_device_sync_client_->GetSetFeatureStatusInputsQueueSize();
}
void InvokePendingSetWifiSyncHostNetworkRequestCallback(
device_sync::mojom::NetworkRequestResult result_code,
bool expected_to_notify_observer_and_start_retry_timer) {
if (features::ShouldUseV1DeviceSync()) {
fake_device_sync_client_->InvokePendingSetSoftwareFeatureStateCallback(
result_code);
} else {
fake_device_sync_client_->InvokePendingSetFeatureStatusCallback(
result_code);
}
EXPECT_EQ(expected_to_notify_observer_and_start_retry_timer,
mock_timer_->IsRunning());
}
void SetWifiSyncHostInDeviceSyncClient(
const base::Optional<multidevice::RemoteDeviceRef>& host_device,
bool enabled) {
GetMutableRemoteDevice(*host_device)
->software_features[multidevice::SoftwareFeature::kWifiSyncHost] =
(enabled ? multidevice::SoftwareFeatureState::kEnabled
: multidevice::SoftwareFeatureState::kSupported);
fake_device_sync_client_->NotifyNewDevicesSynced();
}
FakeHostStatusProvider* fake_host_status_provider() {
return fake_host_status_provider_.get();
}
device_sync::FakeDeviceSyncClient* fake_device_sync_client() {
return fake_device_sync_client_.get();
}
base::MockOneShotTimer* mock_timer() { return mock_timer_; }
WifiSyncFeatureManager* delegate() { return delegate_.get(); }
sync_preferences::TestingPrefServiceSyncable* test_pref_service() {
return test_pref_service_.get();
}
const multidevice::RemoteDeviceRefList& test_devices() const {
return test_devices_;
}
private:
void SetFeatureFlags(bool use_v1_devicesync) {
std::vector<base::Feature> enabled_features;
std::vector<base::Feature> disabled_features;
// These flags have no direct effect of on the host backend delegate;
// however, v2 Enrollment and DeviceSync must be enabled before v1
// DeviceSync can be disabled.
enabled_features.push_back(chromeos::features::kCryptAuthV2Enrollment);
enabled_features.push_back(chromeos::features::kCryptAuthV2DeviceSync);
if (use_v1_devicesync) {
disabled_features.push_back(
chromeos::features::kDisableCryptAuthV1DeviceSync);
} else {
enabled_features.push_back(
chromeos::features::kDisableCryptAuthV1DeviceSync);
}
scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
}
multidevice::RemoteDeviceRefList test_devices_;
std::unique_ptr<FakeHostStatusProvider> fake_host_status_provider_;
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<WifiSyncFeatureManager> delegate_;
base::test::ScopedFeatureList scoped_feature_list_;
DISALLOW_COPY_AND_ASSIGN(MultiDeviceSetupWifiSyncFeatureManagerImplTest);
};
TEST_P(MultiDeviceSetupWifiSyncFeatureManagerImplTest, Success) {
CreateDelegate(test_devices()[0] /* initial_host */);
// Attempt to enable wifi sync on host device and succeed
EXPECT_FALSE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
SetIsWifiSyncEnabled(true);
EXPECT_EQ(1, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_TRUE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
SetWifiSyncHostInDeviceSyncClient(test_devices()[0], true /* enabled */);
InvokePendingSetWifiSyncHostNetworkRequestCallback(
device_sync::mojom::NetworkRequestResult::kSuccess,
false /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_EQ(0, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kEnabled);
// Attempt to disable wifi sync on host device and succeed
SetIsWifiSyncEnabled(false);
EXPECT_EQ(1, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_FALSE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kEnabled);
SetWifiSyncHostInDeviceSyncClient(test_devices()[0], false /* enabled */);
InvokePendingSetWifiSyncHostNetworkRequestCallback(
device_sync::mojom::NetworkRequestResult::kSuccess,
false /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_EQ(0, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
}
TEST_P(MultiDeviceSetupWifiSyncFeatureManagerImplTest, Failure) {
CreateDelegate(test_devices()[0] /* initial_host */);
// Attempt to enable wifi sync on host device and fail
EXPECT_FALSE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
SetIsWifiSyncEnabled(true);
EXPECT_EQ(1, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_TRUE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
InvokePendingSetWifiSyncHostNetworkRequestCallback(
device_sync::mojom::NetworkRequestResult::kOffline,
true /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_EQ(0, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
// A retry should have been scheduled, so fire the timer to start the retry.
mock_timer()->Fire();
// Simulate another failure.
EXPECT_EQ(1, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_TRUE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
InvokePendingSetWifiSyncHostNetworkRequestCallback(
device_sync::mojom::NetworkRequestResult::kOffline,
true /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_EQ(0, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
}
TEST_P(MultiDeviceSetupWifiSyncFeatureManagerImplTest,
MultipleRequests_FirstFail_ThenSucceed) {
CreateDelegate(test_devices()[0] /* initial_host */);
// Attempt to enable wifi sync on host device and fail
EXPECT_FALSE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
SetIsWifiSyncEnabled(true);
EXPECT_EQ(1, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_TRUE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
InvokePendingSetWifiSyncHostNetworkRequestCallback(
device_sync::mojom::NetworkRequestResult::kOffline,
true /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
// The retry timer is running; however, instead of relying on that, call
// SetIsWifiSyncEnabled() again to trigger an immediate
// retry without the timer.
SetIsWifiSyncEnabled(true);
EXPECT_EQ(1, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_TRUE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
SetWifiSyncHostInDeviceSyncClient(test_devices()[0], true /* enabled */);
InvokePendingSetWifiSyncHostNetworkRequestCallback(
device_sync::mojom::NetworkRequestResult::kSuccess,
false /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_EQ(0, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kEnabled);
}
TEST_P(MultiDeviceSetupWifiSyncFeatureManagerImplTest,
PendingRequest_NoSyncedHostDevice) {
CreateDelegate(test_devices()[0] /* initial_host */);
// Attempt to enable wifi sync on test_device 0
SetIsWifiSyncEnabled(true);
EXPECT_EQ(1, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_TRUE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
// Fail to set wifi sync on test_device 0
InvokePendingSetWifiSyncHostNetworkRequestCallback(
device_sync::mojom::NetworkRequestResult::kOffline,
true /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_EQ(0, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
EXPECT_TRUE(mock_timer()->IsRunning());
// Remove synced device. This should remove the pending request and stop the
// retry timer.
SetHostInDeviceSyncClient(base::nullopt);
SetHostWithStatus(base::nullopt);
EXPECT_FALSE(mock_timer()->IsRunning());
}
TEST_P(MultiDeviceSetupWifiSyncFeatureManagerImplTest,
InitialPendingEnableRequest_NoInitialDevice) {
CreateDelegate(base::nullopt /* initial_host */,
PendingEnable /* initial_pending_wifi_sync_request_enabled */);
EXPECT_EQ(0, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
}
TEST_P(MultiDeviceSetupWifiSyncFeatureManagerImplTest,
InitialPendingEnableRequest_Success) {
CreateDelegate(test_devices()[0] /* initial_host */,
PendingEnable /* initial_pending_wifi_sync_request_enabled */);
EXPECT_EQ(1, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_TRUE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
SetWifiSyncHostInDeviceSyncClient(test_devices()[0], true /* enabled */);
InvokePendingSetWifiSyncHostNetworkRequestCallback(
device_sync::mojom::NetworkRequestResult::kSuccess,
false /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_EQ(0, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kEnabled);
}
TEST_P(MultiDeviceSetupWifiSyncFeatureManagerImplTest,
MultiplePendingRequests_EnableDisable) {
CreateDelegate(test_devices()[0] /* initial_host */);
// Attempt to enable->disable->enable wifi sync without invoking any
// callbacks.
EXPECT_FALSE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
SetIsWifiSyncEnabled(true);
EXPECT_EQ(1, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_TRUE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
// Wifi sync is already disabled on back-end so there should be no new pending
// request
SetIsWifiSyncEnabled(false);
EXPECT_EQ(1, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_FALSE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
}
TEST_P(MultiDeviceSetupWifiSyncFeatureManagerImplTest,
PendingRequest_SyncedHostBecomesUnverified) {
CreateDelegate(test_devices()[0] /* initial_host */,
PendingEnable /* initial_pending_wifi_sync_request_enabled */);
fake_host_status_provider()->SetHostWithStatus(
mojom::HostStatus::kHostSetButNotYetVerified, test_devices()[0]);
EXPECT_EQ(
test_pref_service()->GetInteger(kPendingWifiSyncRequestEnabledPrefName),
PendingNone);
}
TEST_P(MultiDeviceSetupWifiSyncFeatureManagerImplTest,
Retrying_SyncedHostBecomesUnverified) {
CreateDelegate(test_devices()[0] /* initial_host */);
SetIsWifiSyncEnabled(true);
EXPECT_EQ(1, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_TRUE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
InvokePendingSetWifiSyncHostNetworkRequestCallback(
device_sync::mojom::NetworkRequestResult::kOffline,
true /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_EQ(0, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_TRUE(mock_timer()->IsRunning());
// Host becomes unverified, this should stop timer and clear pending request
fake_host_status_provider()->SetHostWithStatus(
mojom::HostStatus::kHostSetButNotYetVerified, test_devices()[0]);
EXPECT_EQ(
test_pref_service()->GetInteger(kPendingWifiSyncRequestEnabledPrefName),
PendingNone);
EXPECT_FALSE(mock_timer()->IsRunning());
EXPECT_FALSE(delegate()->IsWifiSyncEnabled());
}
TEST_P(MultiDeviceSetupWifiSyncFeatureManagerImplTest,
FailureCallback_SyncedHostBecomesUnverified) {
CreateDelegate(test_devices()[0] /* initial_host */);
SetIsWifiSyncEnabled(true);
EXPECT_EQ(1, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_TRUE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
// Set host unverified. This should reset pending request.
fake_host_status_provider()->SetHostWithStatus(
mojom::HostStatus::kHostSetButNotYetVerified, test_devices()[0]);
EXPECT_EQ(
test_pref_service()->GetInteger(kPendingWifiSyncRequestEnabledPrefName),
PendingNone);
// Invoke failure callback. No retry should be scheduled.
InvokePendingSetWifiSyncHostNetworkRequestCallback(
device_sync::mojom::NetworkRequestResult::kOffline,
false /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_EQ(0, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_FALSE(mock_timer()->IsRunning());
EXPECT_FALSE(delegate()->IsWifiSyncEnabled());
}
TEST_P(MultiDeviceSetupWifiSyncFeatureManagerImplTest,
NoVerifiedHost_AttemptToEnable) {
CreateDelegate(test_devices()[0] /* initial_host */);
fake_host_status_provider()->SetHostWithStatus(
mojom::HostStatus::kHostSetButNotYetVerified, test_devices()[0]);
// Attempt to enable wifi sync on host device
EXPECT_FALSE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
SetIsWifiSyncEnabled(true);
EXPECT_EQ(0, GetSetHostNetworkRequestCallbackQueueSize());
EXPECT_FALSE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
}
TEST_P(MultiDeviceSetupWifiSyncFeatureManagerImplTest,
StatusChangedOnRemoteDevice) {
CreateDelegate(test_devices()[0] /* initial_host */);
EXPECT_FALSE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
// Simulate enabled on a remote device.
SetWifiSyncHostInDeviceSyncClient(test_devices()[0], true /* enabled */);
EXPECT_TRUE(delegate()->IsWifiSyncEnabled());
}
TEST_P(MultiDeviceSetupWifiSyncFeatureManagerImplTest,
SimultaneousRequests_StartOff_ToggleOnOff) {
CreateDelegate(test_devices()[0] /* initial_host */);
// Attempt to enable
SetIsWifiSyncEnabled(true);
// Attempt to disable
SetIsWifiSyncEnabled(false);
// Only one network request should be in flight at a time
EXPECT_EQ(1, GetSetHostNetworkRequestCallbackQueueSize());
// Successfully enable on host
SetWifiSyncHostInDeviceSyncClient(test_devices()[0], true /* enabled */);
InvokePendingSetWifiSyncHostNetworkRequestCallback(
device_sync::mojom::NetworkRequestResult::kSuccess,
false /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_FALSE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kEnabled);
// A new network request should be scheduled to disable
EXPECT_EQ(1, GetSetHostNetworkRequestCallbackQueueSize());
SetWifiSyncHostInDeviceSyncClient(test_devices()[0], false /* enabled */);
InvokePendingSetWifiSyncHostNetworkRequestCallback(
device_sync::mojom::NetworkRequestResult::kSuccess,
false /* expected_to_notify_observer_and_start_retry_timer */);
EXPECT_FALSE(delegate()->IsWifiSyncEnabled());
EXPECT_EQ(test_devices()[0].GetSoftwareFeatureState(
multidevice::SoftwareFeature::kWifiSyncHost),
multidevice::SoftwareFeatureState::kSupported);
}
// Runs tests twice; once with v1 DeviceSync enabled and once with it disabled.
// TODO(https://crbug.com/1019206): Remove when v1 DeviceSync is disabled,
// when all devices should have an Instance ID.
INSTANTIATE_TEST_SUITE_P(All,
MultiDeviceSetupWifiSyncFeatureManagerImplTest,
::testing::Bool());
} // namespace multidevice_setup
} // namespace chromeos
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