Commit d93edf21 authored by Josh Nohle's avatar Josh Nohle Committed by Commit Bot

[DeviceSync v2] Add SetFeatureStatus to DeviceSync service

Bug: 951969
Change-Id: Id12f9a7513aa73608b4b4586cd233cf5967fbb77
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1831010Reviewed-by: default avatarKen Buchanan <kenrb@chromium.org>
Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Commit-Queue: Josh Nohle <nohle@chromium.org>
Cr-Commit-Position: refs/heads/master@{#714581}
parent 73365d6c
......@@ -21,6 +21,7 @@
#include "chromeos/services/device_sync/cryptauth_device_registry_impl.h"
#include "chromeos/services/device_sync/cryptauth_enroller_factory_impl.h"
#include "chromeos/services/device_sync/cryptauth_enrollment_manager_impl.h"
#include "chromeos/services/device_sync/cryptauth_feature_status_setter_impl.h"
#include "chromeos/services/device_sync/cryptauth_gcm_manager_impl.h"
#include "chromeos/services/device_sync/cryptauth_key_registry_impl.h"
#include "chromeos/services/device_sync/cryptauth_scheduler_impl.h"
......@@ -239,7 +240,7 @@ DeviceSyncImpl::PendingSetSoftwareFeatureRequest::
bool DeviceSyncImpl::PendingSetSoftwareFeatureRequest::IsFulfilled() const {
const auto& synced_devices = remote_device_provider_->GetSyncedDevices();
const auto& devices_it =
const auto devices_it =
std::find_if(synced_devices.begin(), synced_devices.end(),
[this](const auto& remote_device) {
return device_public_key_ == remote_device.public_key;
......@@ -249,7 +250,7 @@ bool DeviceSyncImpl::PendingSetSoftwareFeatureRequest::IsFulfilled() const {
if (devices_it == synced_devices.end())
return false;
const auto& features_map_it =
const auto features_map_it =
devices_it->software_features.find(software_feature_);
// If the device does not contain an entry for |software_feature_|, the
......@@ -290,6 +291,83 @@ void DeviceSyncImpl::RegisterProfilePrefs(PrefRegistrySimple* registry) {
}
}
DeviceSyncImpl::PendingSetFeatureStatusRequest::PendingSetFeatureStatusRequest(
const std::string& device_instance_id,
multidevice::SoftwareFeature software_feature,
FeatureStatusChange status_change,
RemoteDeviceProvider* remote_device_provider,
SetFeatureStatusCallback callback)
: device_instance_id_(device_instance_id),
software_feature_(software_feature),
status_change_(status_change),
remote_device_provider_(remote_device_provider),
callback_(std::move(callback)) {
DCHECK(!device_instance_id.empty());
}
DeviceSyncImpl::PendingSetFeatureStatusRequest::
~PendingSetFeatureStatusRequest() = default;
bool DeviceSyncImpl::PendingSetFeatureStatusRequest::IsFulfilled() const {
// True if the device from the request is included in the synced-devices list.
bool is_requested_device_in_list = false;
// True if the feature from the request is enabled on the device from the
// request.
bool is_feature_enabled_for_requested_device = false;
// True if the feature from the request is enabled on any synced device other
// than the device from the request.
bool is_feature_enabled_for_any_other_device = false;
for (const multidevice::RemoteDevice& remote_device :
remote_device_provider_->GetSyncedDevices()) {
const auto it = remote_device.software_features.find(software_feature_);
bool is_feature_set_for_device =
it != remote_device.software_features.end();
bool is_feature_enabled_for_device =
is_feature_set_for_device &&
it->second == multidevice::SoftwareFeatureState::kEnabled;
if (device_instance_id_ == remote_device.instance_id) {
DCHECK(!is_requested_device_in_list);
is_requested_device_in_list = true;
// If the requested device does not contain an entry for
// |software_feature_|, the request is not fulfilled.
if (!is_feature_set_for_device)
return false;
is_feature_enabled_for_requested_device = is_feature_enabled_for_device;
} else {
is_feature_enabled_for_any_other_device =
is_feature_enabled_for_any_other_device ||
is_feature_enabled_for_device;
}
}
// If the requested device no longer exists, the request is not fulfilled.
if (!is_requested_device_in_list)
return false;
switch (status_change_) {
case FeatureStatusChange::kEnableExclusively:
return is_feature_enabled_for_requested_device &&
!is_feature_enabled_for_any_other_device;
case FeatureStatusChange::kEnableNonExclusively:
return is_feature_enabled_for_requested_device;
case FeatureStatusChange::kDisable:
return !is_feature_enabled_for_requested_device;
}
}
void DeviceSyncImpl::PendingSetFeatureStatusRequest::InvokeCallback(
mojom::NetworkRequestResult result) {
// Callback should only be invoked once.
DCHECK(callback_);
std::move(callback_).Run(result);
}
DeviceSyncImpl::DeviceSyncImpl(
signin::IdentityManager* identity_manager,
gcm::GCMDriver* gcm_driver,
......@@ -434,6 +512,35 @@ void DeviceSyncImpl::SetSoftwareFeatureState(
is_exclusive);
}
void DeviceSyncImpl::SetFeatureStatus(const std::string& device_instance_id,
multidevice::SoftwareFeature feature,
FeatureStatusChange status_change,
SetFeatureStatusCallback callback) {
DCHECK(!device_instance_id.empty());
if (status_ != Status::READY) {
PA_LOG(WARNING) << "DeviceSyncImpl::SetFeatureStatus() invoked before "
<< "initialization was complete. Cannot enable/disable "
<< "feature.";
std::move(callback).Run(
mojom::NetworkRequestResult::kServiceNotYetInitialized);
return;
}
auto request_id = base::UnguessableToken::Create();
id_to_pending_set_feature_status_request_map_.emplace(
request_id, std::make_unique<PendingSetFeatureStatusRequest>(
device_instance_id, feature, status_change,
remote_device_provider_.get(), std::move(callback)));
feature_status_setter_->SetFeatureStatus(
device_instance_id, feature, status_change,
base::BindOnce(&DeviceSyncImpl::OnSetFeatureStatusSuccess,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&DeviceSyncImpl::OnSetFeatureStatusError,
weak_ptr_factory_.GetWeakPtr(), request_id));
}
void DeviceSyncImpl::FindEligibleDevices(
multidevice::SoftwareFeature software_feature,
FindEligibleDevicesCallback callback) {
......@@ -544,8 +651,8 @@ void DeviceSyncImpl::OnSyncDeviceListChanged() {
// Iterate through pending SetSoftwareFeature() requests. If any of them have
// been fulfilled, invoke their callbacks.
auto it = id_to_pending_set_software_feature_request_map_.begin();
while (it != id_to_pending_set_software_feature_request_map_.end()) {
for (auto it = id_to_pending_set_software_feature_request_map_.begin();
it != id_to_pending_set_software_feature_request_map_.end();) {
if (!it->second->IsFulfilled()) {
++it;
continue;
......@@ -557,10 +664,27 @@ void DeviceSyncImpl::OnSyncDeviceListChanged() {
it->second->InvokeCallback(mojom::NetworkRequestResult::kSuccess);
it = id_to_pending_set_software_feature_request_map_.erase(it);
}
// Iterate through pending SetFeatureStatus() requests. If any of them have
// been fulfilled, invoke their callbacks.
for (auto it = id_to_pending_set_feature_status_request_map_.begin();
it != id_to_pending_set_feature_status_request_map_.end();) {
if (!it->second->IsFulfilled()) {
++it;
continue;
}
PA_LOG(VERBOSE) << "DeviceSyncImpl::OnSyncDeviceListChanged(): Feature "
<< "status updated via device sync; notifying success "
<< "callbacks.";
it->second->InvokeCallback(mojom::NetworkRequestResult::kSuccess);
it = id_to_pending_set_feature_status_request_map_.erase(it);
}
}
void DeviceSyncImpl::Shutdown() {
software_feature_manager_.reset();
feature_status_setter_.reset();
remote_device_provider_.reset();
cryptauth_v2_device_manager_.reset();
cryptauth_device_manager_.reset();
......@@ -702,6 +826,11 @@ void DeviceSyncImpl::CompleteInitializationAfterSuccessfulEnrollment() {
software_feature_manager_ = SoftwareFeatureManagerImpl::Factory::NewInstance(
cryptauth_client_factory_.get());
feature_status_setter_ =
CryptAuthFeatureStatusSetterImpl::Factory::Get()->BuildInstance(
client_app_metadata_provider_, cryptauth_client_factory_.get(),
cryptauth_gcm_manager_.get());
status_ = Status::READY;
PA_LOG(VERBOSE) << "DeviceSyncImpl: CryptAuth Enrollment is valid; service "
......@@ -715,10 +844,10 @@ DeviceSyncImpl::GetSyncedDeviceWithPublicKey(
<< "DeviceSyncImpl::GetSyncedDeviceWithPublicKey() called before ready.";
const auto& synced_devices = remote_device_provider_->GetSyncedDevices();
const auto& it = std::find_if(synced_devices.begin(), synced_devices.end(),
[&public_key](const auto& remote_device) {
return public_key == remote_device.public_key;
});
const auto it = std::find_if(synced_devices.begin(), synced_devices.end(),
[&public_key](const auto& remote_device) {
return public_key == remote_device.public_key;
});
if (it == synced_devices.end())
return base::nullopt;
......@@ -765,6 +894,36 @@ void DeviceSyncImpl::OnSetSoftwareFeatureStateError(
id_to_pending_set_software_feature_request_map_.erase(it);
}
void DeviceSyncImpl::OnSetFeatureStatusSuccess() {
PA_LOG(VERBOSE) << "DeviceSyncImpl::OnSetFeatureStatusSuccess(): "
<< "Successfully completed SetFeatureStatus() call; "
<< "requesting force sync.";
cryptauth_device_manager_->ForceSyncNow(
cryptauth::INVOCATION_REASON_FEATURE_TOGGLED);
if (features::ShouldUseV2DeviceSync()) {
cryptauth_v2_device_manager_->ForceDeviceSyncNow(
cryptauthv2::ClientMetadata::FEATURE_TOGGLED,
base::nullopt /* session_id */);
}
}
void DeviceSyncImpl::OnSetFeatureStatusError(
const base::UnguessableToken& request_id,
NetworkRequestError error) {
auto it = id_to_pending_set_feature_status_request_map_.find(request_id);
if (it == id_to_pending_set_feature_status_request_map_.end()) {
PA_LOG(ERROR) << "DeviceSyncImpl::OnSetFeatureStatusError(): "
<< "Could not find request entry with ID " << request_id;
NOTREACHED();
return;
}
it->second->InvokeCallback(
mojo::ConvertTo<mojom::NetworkRequestResult>(error));
id_to_pending_set_feature_status_request_map_.erase(it);
}
void DeviceSyncImpl::OnFindEligibleDevicesSuccess(
const base::RepeatingCallback<void(mojom::NetworkRequestResult,
mojom::FindEligibleDevicesResponsePtr)>&
......
......@@ -14,6 +14,7 @@
#include "chromeos/services/device_sync/cryptauth_enrollment_manager.h"
#include "chromeos/services/device_sync/cryptauth_gcm_manager.h"
#include "chromeos/services/device_sync/device_sync_base.h"
#include "chromeos/services/device_sync/feature_status_change.h"
#include "chromeos/services/device_sync/network_request_error.h"
#include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
#include "chromeos/services/device_sync/remote_device_provider.h"
......@@ -44,6 +45,7 @@ class ClientAppMetadataProvider;
class CryptAuthClientFactory;
class CryptAuthDeviceManager;
class CryptAuthDeviceRegistry;
class CryptAuthFeatureStatusSetter;
class CryptAuthKeyRegistry;
class CryptAuthScheduler;
class CryptAuthV2DeviceManager;
......@@ -100,6 +102,10 @@ class DeviceSyncImpl : public DeviceSyncBase,
bool enabled,
bool is_exclusive,
SetSoftwareFeatureStateCallback callback) override;
void SetFeatureStatus(const std::string& device_instance_id,
multidevice::SoftwareFeature feature,
FeatureStatusChange status_change,
SetFeatureStatusCallback callback) override;
void FindEligibleDevices(multidevice::SoftwareFeature software_feature,
FindEligibleDevicesCallback callback) override;
void GetDevicesActivityStatus(
......@@ -147,6 +153,30 @@ class DeviceSyncImpl : public DeviceSyncBase,
SetSoftwareFeatureStateCallback callback_;
};
class PendingSetFeatureStatusRequest {
public:
PendingSetFeatureStatusRequest(
const std::string& device_instance_id,
multidevice::SoftwareFeature software_feature,
FeatureStatusChange status_change,
RemoteDeviceProvider* remote_device_provider,
SetFeatureStatusCallback callback);
~PendingSetFeatureStatusRequest();
// True if the device and software feature status specified in the request
// agrees with the device data returned by CryptAuth.
bool IsFulfilled() const;
void InvokeCallback(mojom::NetworkRequestResult result);
private:
std::string device_instance_id_;
multidevice::SoftwareFeature software_feature_;
FeatureStatusChange status_change_;
RemoteDeviceProvider* remote_device_provider_;
SetFeatureStatusCallback callback_;
};
DeviceSyncImpl(
signin::IdentityManager* identity_manager,
gcm::GCMDriver* gcm_driver,
......@@ -174,6 +204,9 @@ class DeviceSyncImpl : public DeviceSyncBase,
void OnSetSoftwareFeatureStateSuccess();
void OnSetSoftwareFeatureStateError(const base::UnguessableToken& request_id,
NetworkRequestError error);
void OnSetFeatureStatusSuccess();
void OnSetFeatureStatusError(const base::UnguessableToken& request_id,
NetworkRequestError error);
void OnFindEligibleDevicesSuccess(
const base::RepeatingCallback<
void(mojom::NetworkRequestResult,
......@@ -213,6 +246,9 @@ class DeviceSyncImpl : public DeviceSyncBase,
base::flat_map<base::UnguessableToken,
std::unique_ptr<PendingSetSoftwareFeatureRequest>>
id_to_pending_set_software_feature_request_map_;
base::flat_map<base::UnguessableToken,
std::unique_ptr<PendingSetFeatureStatusRequest>>
id_to_pending_set_feature_status_request_map_;
base::flat_map<base::UnguessableToken, GetDevicesActivityStatusCallback>
get_devices_activity_status_callbacks_;
......@@ -231,6 +267,7 @@ class DeviceSyncImpl : public DeviceSyncBase,
std::unique_ptr<CryptAuthDeviceManager> cryptauth_device_manager_;
std::unique_ptr<RemoteDeviceProvider> remote_device_provider_;
std::unique_ptr<SoftwareFeatureManager> software_feature_manager_;
std::unique_ptr<CryptAuthFeatureStatusSetter> feature_status_setter_;
std::unique_ptr<CryptAuthDeviceActivityGetter>
cryptauth_device_activity_getter_;
......
......@@ -31,6 +31,7 @@
#include "chromeos/services/device_sync/device_sync_impl.h"
#include "chromeos/services/device_sync/fake_cryptauth_device_manager.h"
#include "chromeos/services/device_sync/fake_cryptauth_enrollment_manager.h"
#include "chromeos/services/device_sync/fake_cryptauth_feature_status_setter.h"
#include "chromeos/services/device_sync/fake_cryptauth_gcm_manager.h"
#include "chromeos/services/device_sync/fake_cryptauth_scheduler.h"
#include "chromeos/services/device_sync/fake_cryptauth_v2_device_manager.h"
......@@ -133,6 +134,24 @@ class FakeSoftwareFeatureManagerDelegate
base::Closure on_delegate_call_closure_;
};
// Delegate which invokes the Closure provided to its constructor when a
// delegate function is invoked.
class FakeCryptAuthFeatureStatusSetterDelegate
: public FakeCryptAuthFeatureStatusSetter::Delegate {
public:
explicit FakeCryptAuthFeatureStatusSetterDelegate(
base::Closure on_delegate_call_closure)
: on_delegate_call_closure_(on_delegate_call_closure) {}
~FakeCryptAuthFeatureStatusSetterDelegate() override = default;
// FakeCryptAuthFeatureStatusSetter::Delegate:
void OnSetFeatureStatusCalled() override { on_delegate_call_closure_.Run(); }
private:
base::Closure on_delegate_call_closure_;
};
class FakeCryptAuthGCMManagerFactory : public CryptAuthGCMManagerImpl::Factory {
public:
FakeCryptAuthGCMManagerFactory(gcm::FakeGCMDriver* fake_gcm_driver,
......@@ -750,6 +769,11 @@ class DeviceSyncServiceTest
SoftwareFeatureManagerImpl::Factory::SetInstanceForTesting(
fake_software_feature_manager_factory_.get());
fake_feature_status_setter_factory_ =
std::make_unique<FakeCryptAuthFeatureStatusSetterFactory>();
CryptAuthFeatureStatusSetterImpl::Factory::SetFactoryForTesting(
fake_feature_status_setter_factory_.get());
auto mock_timer = std::make_unique<base::MockOneShotTimer>();
mock_timer_ = mock_timer.get();
......@@ -962,6 +986,18 @@ class DeviceSyncServiceTest
return fake_software_feature_manager_factory_->instance();
}
FakeCryptAuthFeatureStatusSetter* fake_feature_status_setter() {
if (fake_feature_status_setter_factory_->instances().empty())
return nullptr;
EXPECT_EQ(1u, fake_feature_status_setter_factory_->instances().size());
return fake_feature_status_setter_factory_->instances()[0];
}
const std::vector<mojom::NetworkRequestResult>& set_feature_status_results() {
return set_feature_status_results_;
}
std::unique_ptr<mojom::NetworkRequestResult>
GetLastSetSoftwareFeatureStateResponseAndReset() {
return std::move(last_set_software_feature_state_response_);
......@@ -997,6 +1033,15 @@ class DeviceSyncServiceTest
EXPECT_EQ(mojom::NetworkRequestResult::kServiceNotYetInitialized,
*last_set_response);
// SetFeatureStatus() should return a struct with the special
// kErrorNotInitialized error code.
CallSetFeatureStatus(test_devices()[0].instance_id,
multidevice::SoftwareFeature::kBetterTogetherHost,
FeatureStatusChange::kEnableExclusively);
EXPECT_EQ(1u, set_feature_status_results_.size());
EXPECT_EQ(mojom::NetworkRequestResult::kServiceNotYetInitialized,
set_feature_status_results_[0]);
// Likewise, FindEligibleDevices() should also return a struct with the same
// error code.
CallFindEligibleDevices(multidevice::SoftwareFeature::kBetterTogetherHost);
......@@ -1120,6 +1165,34 @@ class DeviceSyncServiceTest
fake_software_feature_manager_factory_->instance()->set_delegate(nullptr);
}
void CallSetFeatureStatus(const std::string& device_instance_id,
multidevice::SoftwareFeature software_feature,
FeatureStatusChange status_change) {
base::RunLoop run_loop;
// If the feature setter has not yet been created, the service has not been
// initialized. SetFeatureStatus() is expected to respond synchronously with
// an error.
if (!fake_feature_status_setter()) {
device_sync_->SetFeatureStatus(
device_instance_id, software_feature, status_change,
base::Bind(
&DeviceSyncServiceTest::OnSetFeatureStatusCompletedSynchronously,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
return;
}
FakeCryptAuthFeatureStatusSetterDelegate delegate(run_loop.QuitClosure());
fake_feature_status_setter()->set_delegate(&delegate);
device_sync_->SetFeatureStatus(
device_instance_id, software_feature, status_change,
base::Bind(&DeviceSyncServiceTest::OnSetFeatureStatusCompleted,
base::Unretained(this)));
run_loop.Run();
fake_feature_status_setter()->set_delegate(nullptr);
}
void CallFindEligibleDevices(multidevice::SoftwareFeature software_feature) {
base::RunLoop run_loop;
FakeSoftwareFeatureManager* manager = fake_software_feature_manager();
......@@ -1207,6 +1280,17 @@ class DeviceSyncServiceTest
std::move(quit_closure).Run();
}
void OnSetFeatureStatusCompleted(mojom::NetworkRequestResult result_code) {
set_feature_status_results_.push_back(result_code);
}
void OnSetFeatureStatusCompletedSynchronously(
base::OnceClosure quit_closure,
mojom::NetworkRequestResult result_code) {
OnSetFeatureStatusCompleted(result_code);
std::move(quit_closure).Run();
}
void OnFindEligibleDevicesCompleted(
mojom::NetworkRequestResult result_code,
mojom::FindEligibleDevicesResponsePtr response) {
......@@ -1266,6 +1350,8 @@ class DeviceSyncServiceTest
fake_remote_device_provider_factory_;
std::unique_ptr<FakeSoftwareFeatureManagerFactory>
fake_software_feature_manager_factory_;
std::unique_ptr<FakeCryptAuthFeatureStatusSetterFactory>
fake_feature_status_setter_factory_;
std::unique_ptr<signin::IdentityTestEnvironment> identity_test_environment_;
std::unique_ptr<gcm::FakeGCMDriver> fake_gcm_driver_;
......@@ -1282,6 +1368,7 @@ class DeviceSyncServiceTest
mojom::FindEligibleDevicesResponsePtr>>
last_find_eligible_devices_response_;
base::Optional<mojom::DebugInfo> last_debug_info_result_;
std::vector<mojom::NetworkRequestResult> set_feature_status_results_;
std::unique_ptr<FakeDeviceSyncObserver> fake_device_sync_observer_;
std::unique_ptr<DeviceSyncBase> device_sync_;
......@@ -1599,6 +1686,172 @@ TEST_P(DeviceSyncServiceTest, SetSoftwareFeatureState_Error) {
"MultiDevice.DeviceSyncService.SetSoftwareFeatureState.Result", true, 0);
}
TEST_P(DeviceSyncServiceTest, SetFeatureStatus_Success) {
InitializeServiceSuccessfully();
EXPECT_EQ(0u, fake_feature_status_setter()->requests().size());
multidevice::RemoteDevice device_for_test = test_devices()[0];
// Exclusively enable kBetterTogetherHost for the device.
CallSetFeatureStatus(device_for_test.instance_id,
multidevice::SoftwareFeature::kBetterTogetherHost,
FeatureStatusChange::kEnableExclusively);
EXPECT_EQ(1u, fake_feature_status_setter()->requests().size());
EXPECT_EQ(multidevice::SoftwareFeature::kBetterTogetherHost,
fake_feature_status_setter()->requests()[0].feature);
EXPECT_EQ(FeatureStatusChange::kEnableExclusively,
fake_feature_status_setter()->requests()[0].status_change);
EXPECT_TRUE(fake_feature_status_setter()->requests()[0].success_callback);
EXPECT_TRUE(fake_feature_status_setter()->requests()[0].error_callback);
// The DeviceSyncImpl::SetFeatureStatus() callback has not yet been invoked.
EXPECT_TRUE(set_feature_status_results().empty());
// Now, invoke the CryptAuthFeatureStatusSetter success callback.
std::move(fake_feature_status_setter()->requests()[0].success_callback).Run();
// The DeviceSyncImpl::SetFeatureStatus() callback still has not yet been
// invoked since a device sync has not confirmed the feature state change yet.
EXPECT_TRUE(set_feature_status_results().empty());
// Simulate a sync which includes the device with the correct "enabled" state.
device_for_test
.software_features[multidevice::SoftwareFeature::kBetterTogetherHost] =
multidevice::SoftwareFeatureState::kEnabled;
SimulateSync(true /* success */, {device_for_test});
base::RunLoop().RunUntilIdle();
ASSERT_EQ(1u, set_feature_status_results().size());
EXPECT_EQ(mojom::NetworkRequestResult::kSuccess,
set_feature_status_results()[0]);
}
TEST_P(DeviceSyncServiceTest,
SetFeatureStatus_RequestSucceedsButDoesNotTakeEffect) {
InitializeServiceSuccessfully();
// Expected device feature states after SetFeatureStatus() calls:
// * Device 0 has kSmartLockHost disabled.
// * Device 1 has kSmartLockHost disabled.
// * Device 2 has kBetterTogetherHost enabled exclusively.
// * Device 3 has kInstantTetheringHost enabled.
// * Device 4 has kMessagesForWebHost disabled.
multidevice::RemoteDeviceList expected_remote_devices =
multidevice::CreateRemoteDeviceListForTest(5u);
expected_remote_devices[0]
.software_features[multidevice::SoftwareFeature::kSmartLockHost] =
multidevice::SoftwareFeatureState::kSupported;
expected_remote_devices[1]
.software_features[multidevice::SoftwareFeature::kSmartLockHost] =
multidevice::SoftwareFeatureState::kSupported;
expected_remote_devices[2]
.software_features[multidevice::SoftwareFeature::kBetterTogetherHost] =
multidevice::SoftwareFeatureState::kEnabled;
expected_remote_devices[3]
.software_features[multidevice::SoftwareFeature::kInstantTetheringHost] =
multidevice::SoftwareFeatureState::kEnabled;
expected_remote_devices[4]
.software_features[multidevice::SoftwareFeature::kMessagesForWebHost] =
multidevice::SoftwareFeatureState::kSupported;
CallSetFeatureStatus(expected_remote_devices[0].instance_id,
multidevice::SoftwareFeature::kSmartLockHost,
FeatureStatusChange::kDisable);
CallSetFeatureStatus(expected_remote_devices[1].instance_id,
multidevice::SoftwareFeature::kSmartLockHost,
FeatureStatusChange::kDisable);
CallSetFeatureStatus(expected_remote_devices[2].instance_id,
multidevice::SoftwareFeature::kBetterTogetherHost,
FeatureStatusChange::kEnableExclusively);
CallSetFeatureStatus(expected_remote_devices[3].instance_id,
multidevice::SoftwareFeature::kInstantTetheringHost,
FeatureStatusChange::kEnableNonExclusively);
CallSetFeatureStatus(expected_remote_devices[4].instance_id,
multidevice::SoftwareFeature::kMessagesForWebHost,
FeatureStatusChange::kDisable);
EXPECT_EQ(5u, fake_feature_status_setter()->requests().size());
// The DeviceSyncImpl::SetFeatureStatus() callbacks have not yet been invoked.
EXPECT_TRUE(set_feature_status_results().empty());
// Now, invoke the CryptAuthFeatureStatusSetter success callbacks.
std::move(fake_feature_status_setter()->requests()[0].success_callback).Run();
std::move(fake_feature_status_setter()->requests()[1].success_callback).Run();
std::move(fake_feature_status_setter()->requests()[2].success_callback).Run();
std::move(fake_feature_status_setter()->requests()[3].success_callback).Run();
std::move(fake_feature_status_setter()->requests()[4].success_callback).Run();
// The DeviceSyncImpl::SetFeatureStatus() callbacks still have not been
// invoked since a DeviceSync has not confirmed any of the requested feature
// state changes yet.
EXPECT_TRUE(set_feature_status_results().empty());
// Simulate a DeviceSync which returns unexpected device feature states:
// * Device 0 not in list of devices.
// * Device 1 missing kSmartLockHost entry in the feature list.
// * Device 2 has kBetterTogetherHost enabled but not exclusively since device
// 1 also has it enabled.
// * Device 3 does not have kInstantTetheringHost enabled.
// * Device 4 does not have kMessagesForWebHost disabled.
multidevice::RemoteDeviceList remote_devices_from_first_sync =
multidevice::CreateRemoteDeviceListForTest(5u);
remote_devices_from_first_sync[1]
.software_features[multidevice::SoftwareFeature::kBetterTogetherHost] =
multidevice::SoftwareFeatureState::kEnabled;
remote_devices_from_first_sync[2]
.software_features[multidevice::SoftwareFeature::kBetterTogetherHost] =
multidevice::SoftwareFeatureState::kEnabled;
remote_devices_from_first_sync[3]
.software_features[multidevice::SoftwareFeature::kInstantTetheringHost] =
multidevice::SoftwareFeatureState::kSupported;
remote_devices_from_first_sync[4]
.software_features[multidevice::SoftwareFeature::kMessagesForWebHost] =
multidevice::SoftwareFeatureState::kEnabled;
remote_devices_from_first_sync.erase(remote_devices_from_first_sync.begin());
SimulateSync(true /* success */, remote_devices_from_first_sync);
base::RunLoop().RunUntilIdle();
// The DeviceSyncImpl::SetFeatureStatus() callbacks still have not yet been
// invoked since the latest DeviceSync did not reflect the requested feature
// state changes.
EXPECT_EQ(0u, set_feature_status_results().size());
// Simulate a DeviceSync which returns the expected device feature states:
EXPECT_TRUE(CallForceSyncNow());
SimulateSync(true /* success */, expected_remote_devices);
base::RunLoop().RunUntilIdle();
ASSERT_EQ(5u, set_feature_status_results().size());
for (mojom::NetworkRequestResult result : set_feature_status_results())
EXPECT_EQ(mojom::NetworkRequestResult::kSuccess, result);
}
TEST_P(DeviceSyncServiceTest, SetFeatureStatus_Error) {
InitializeServiceSuccessfully();
multidevice::RemoteDevice device_for_test = test_devices()[0];
// Attempt to exclusively enable kBetterTogetherHost for the device.
CallSetFeatureStatus(device_for_test.instance_id,
multidevice::SoftwareFeature::kBetterTogetherHost,
FeatureStatusChange::kEnableExclusively);
// The DeviceSyncImpl::SetFeatureStatus() callback has not yet been invoked.
EXPECT_TRUE(set_feature_status_results().empty());
// Now, invoke the CryptAuthFeatureStatusSetter error callback.
std::move(fake_feature_status_setter()->requests()[0].error_callback)
.Run(NetworkRequestError::kBadRequest);
// The DeviceSyncImpl::SetFeatureStatus() callback is invoked with the same
// error code.
ASSERT_EQ(1u, set_feature_status_results().size());
EXPECT_EQ(mojom::NetworkRequestResult::kBadRequest,
set_feature_status_results()[0]);
}
TEST_P(DeviceSyncServiceTest, FindEligibleDevices) {
InitializeServiceSuccessfully();
......
......@@ -39,6 +39,12 @@ void FakeDeviceSync::InvokePendingSetSoftwareFeatureStateCallback(
set_software_feature_state_callback_queue_.pop();
}
void FakeDeviceSync::InvokePendingSetFeatureStatusCallback(
mojom::NetworkRequestResult result_code) {
std::move(set_feature_status_callback_queue_.front()).Run(result_code);
set_feature_status_callback_queue_.pop();
}
void FakeDeviceSync::InvokePendingFindEligibleDevicesCallback(
mojom::NetworkRequestResult result_code,
mojom::FindEligibleDevicesResponsePtr find_eligible_devices_response_ptr) {
......@@ -89,6 +95,13 @@ void FakeDeviceSync::SetSoftwareFeatureState(
set_software_feature_state_callback_queue_.push(std::move(callback));
}
void FakeDeviceSync::SetFeatureStatus(const std::string& device_instance_id,
multidevice::SoftwareFeature feature,
FeatureStatusChange status_change,
SetFeatureStatusCallback callback) {
set_feature_status_callback_queue_.push(std::move(callback));
}
void FakeDeviceSync::FindEligibleDevices(
multidevice::SoftwareFeature software_feature,
FindEligibleDevicesCallback callback) {
......
......@@ -41,6 +41,8 @@ class FakeDeviceSync : public DeviceSyncBase {
remote_devices);
void InvokePendingSetSoftwareFeatureStateCallback(
mojom::NetworkRequestResult result_code);
void InvokePendingSetFeatureStatusCallback(
mojom::NetworkRequestResult result_code);
void InvokePendingFindEligibleDevicesCallback(
mojom::NetworkRequestResult result_code,
mojom::FindEligibleDevicesResponsePtr find_eligible_devices_response_ptr);
......@@ -62,6 +64,10 @@ class FakeDeviceSync : public DeviceSyncBase {
bool enabled,
bool is_exclusive,
SetSoftwareFeatureStateCallback callback) override;
void SetFeatureStatus(const std::string& device_instance_id,
multidevice::SoftwareFeature feature,
FeatureStatusChange status_change,
SetFeatureStatusCallback callback) override;
void FindEligibleDevices(multidevice::SoftwareFeature software_feature,
FindEligibleDevicesCallback callback) override;
void GetDebugInfo(GetDebugInfoCallback callback) override;
......@@ -77,6 +83,7 @@ class FakeDeviceSync : public DeviceSyncBase {
std::queue<GetSyncedDevicesCallback> get_synced_devices_callback_queue_;
std::queue<SetSoftwareFeatureStateCallback>
set_software_feature_state_callback_queue_;
std::queue<SetFeatureStatusCallback> set_feature_status_callback_queue_;
std::queue<FindEligibleDevicesCallback> find_eligible_devices_callback_queue_;
std::queue<GetDevicesActivityStatusCallback>
get_devices_activity_status_callback_queue_;
......
......@@ -147,12 +147,26 @@ interface DeviceSync {
// SoftwareFeature::EASY_UNLOCK_HOST and |enabled| = false, |public_key| is
// ignored, because that combination of arguments disables EASY_UNLOCK_HOST on
// all of the user's devices.
//
// TODO(https://crbug.com/1019206): Remove this function when v1 DeviceSync
// is deprecated.
SetSoftwareFeatureState(
string device_public_key,
chromeos.multidevice.mojom.SoftwareFeature software_feature,
bool enabled,
bool is_exclusive) => (NetworkRequestResult result_code);
// Enables or disables |feature| for the device with Instance ID
// |device_instance_id|.
//
// This function can only affect devices using CryptAuth v2 DeviceSync since
// it requires an Instance ID. See SetSoftwareFeatureState() for the v1
// DeviceSync analog.
SetFeatureStatus(
string device_instance_id,
chromeos.multidevice.mojom.SoftwareFeature feature,
FeatureStatusChange status_change) => (NetworkRequestResult result_code);
// Finds devices which are eligible for the given feature. When this function
// is invoked, a network request will be sent to each eligible device which
// instructs that device to enable BLE advertising; thus, this function can be
......@@ -161,6 +175,9 @@ interface DeviceSync {
// On success, this function returns a null error_code with a valid response
// to the callback; on error, it returns a valid error_code string indicating
// the reason for failure along with a null response.
//
// TODO(https://crbug.com/1019206): Remove this function when v1 DeviceSync
// is deprecated.
FindEligibleDevices(
chromeos.multidevice.mojom.SoftwareFeature software_feature) =>
(NetworkRequestResult result_code,
......
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