blob: b553a2bb16818e57cf379d348fff539177c2ce7f [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/services/multidevice_setup/feature_state_manager_impl.h"
#include <memory>
#include "base/macros.h"
#include "base/optional.h"
#include "chromeos/components/multidevice/remote_device_test_util.h"
#include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h"
#include "chromeos/services/multidevice_setup/fake_feature_state_manager.h"
#include "chromeos/services/multidevice_setup/fake_host_status_provider.h"
#include "chromeos/services/multidevice_setup/public/cpp/fake_android_sms_pairing_state_tracker.h"
#include "chromeos/services/multidevice_setup/public/cpp/prefs.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace multidevice_setup {
namespace {
multidevice::RemoteDeviceRef CreateTestHostDevice() {
multidevice::RemoteDeviceRef host_device =
multidevice::CreateRemoteDeviceRefForTest();
// Set all host features to supported.
multidevice::RemoteDevice* raw_device =
multidevice::GetMutableRemoteDevice(host_device);
raw_device
->software_features[multidevice::SoftwareFeature::kBetterTogetherHost] =
multidevice::SoftwareFeatureState::kSupported;
raw_device->software_features[multidevice::SoftwareFeature::kSmartLockHost] =
multidevice::SoftwareFeatureState::kSupported;
raw_device
->software_features[multidevice::SoftwareFeature::kInstantTetheringHost] =
multidevice::SoftwareFeatureState::kSupported;
raw_device
->software_features[multidevice::SoftwareFeature::kMessagesForWebHost] =
multidevice::SoftwareFeatureState::kSupported;
return host_device;
}
} // namespace
class MultiDeviceSetupFeatureStateManagerImplTest : public testing::Test {
protected:
MultiDeviceSetupFeatureStateManagerImplTest()
: test_local_device_(multidevice::CreateRemoteDeviceRefForTest()),
test_host_device_(CreateTestHostDevice()) {}
~MultiDeviceSetupFeatureStateManagerImplTest() override = default;
// testing::Test:
void SetUp() override {
test_pref_service_ =
std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
user_prefs::PrefRegistrySyncable* registry = test_pref_service_->registry();
RegisterFeaturePrefs(registry);
fake_host_status_provider_ = std::make_unique<FakeHostStatusProvider>();
fake_device_sync_client_ =
std::make_unique<device_sync::FakeDeviceSyncClient>();
fake_device_sync_client_->set_synced_devices(
multidevice::RemoteDeviceRefList{test_local_device_,
test_host_device_});
fake_device_sync_client_->set_local_device_metadata(test_local_device_);
auto fake_android_sms_pairing_state_tracker =
std::make_unique<FakeAndroidSmsPairingStateTracker>();
fake_android_sms_pairing_state_tracker->SetPairingComplete(true);
fake_android_sms_pairing_state_tracker_ =
fake_android_sms_pairing_state_tracker.get();
manager_ = FeatureStateManagerImpl::Factory::Get()->BuildInstance(
test_pref_service_.get(), fake_host_status_provider_.get(),
fake_device_sync_client_.get(),
std::move(fake_android_sms_pairing_state_tracker));
fake_observer_ = std::make_unique<FakeFeatureStateManagerObserver>();
manager_->AddObserver(fake_observer_.get());
}
void TryAllUnverifiedHostStatesAndVerifyFeatureState(mojom::Feature feature) {
bool was_previously_verified =
fake_host_status_provider_->GetHostWithStatus().host_status() ==
mojom::HostStatus::kHostVerified;
size_t num_observer_events_before_call =
fake_observer_->feature_state_updates().size();
size_t expected_num_observer_events_after_call =
num_observer_events_before_call + (was_previously_verified ? 1u : 0u);
fake_host_status_provider_->SetHostWithStatus(
mojom::HostStatus::kNoEligibleHosts, base::nullopt /* host_device */);
if (was_previously_verified) {
VerifyFeatureStateChange(num_observer_events_before_call, feature,
mojom::FeatureState::kUnavailableNoVerifiedHost);
}
EXPECT_EQ(mojom::FeatureState::kUnavailableNoVerifiedHost,
manager_->GetFeatureStates()[feature]);
EXPECT_EQ(expected_num_observer_events_after_call,
fake_observer_->feature_state_updates().size());
fake_host_status_provider_->SetHostWithStatus(
mojom::HostStatus::kEligibleHostExistsButNoHostSet,
base::nullopt /* host_device */);
EXPECT_EQ(mojom::FeatureState::kUnavailableNoVerifiedHost,
manager_->GetFeatureStates()[feature]);
EXPECT_EQ(expected_num_observer_events_after_call,
fake_observer_->feature_state_updates().size());
fake_host_status_provider_->SetHostWithStatus(
mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation,
test_host_device_);
EXPECT_EQ(mojom::FeatureState::kUnavailableNoVerifiedHost,
manager_->GetFeatureStates()[feature]);
EXPECT_EQ(expected_num_observer_events_after_call,
fake_observer_->feature_state_updates().size());
fake_host_status_provider_->SetHostWithStatus(
mojom::HostStatus::kHostSetButNotYetVerified, test_host_device_);
EXPECT_EQ(mojom::FeatureState::kUnavailableNoVerifiedHost,
manager_->GetFeatureStates()[feature]);
EXPECT_EQ(expected_num_observer_events_after_call,
fake_observer_->feature_state_updates().size());
}
void SetVerifiedHost() {
// Should not already be verified if we are setting it to be verified.
EXPECT_NE(mojom::HostStatus::kHostVerified,
fake_host_status_provider_->GetHostWithStatus().host_status());
size_t num_observer_events_before_call =
fake_observer_->feature_state_updates().size();
SetSoftwareFeatureState(false /* use_local_device */,
multidevice::SoftwareFeature::kBetterTogetherHost,
multidevice::SoftwareFeatureState::kEnabled);
fake_host_status_provider_->SetHostWithStatus(
mojom::HostStatus::kHostVerified, test_host_device_);
// Since the host is now verified, there should be a feature state update
// for all features.
EXPECT_EQ(num_observer_events_before_call + 1u,
fake_observer_->feature_state_updates().size());
}
void MakeBetterTogetherSuiteDisabledByUser() {
SetSoftwareFeatureState(true /* use_local_device */,
multidevice::SoftwareFeature::kBetterTogetherClient,
multidevice::SoftwareFeatureState::kSupported);
test_pref_service_->SetBoolean(kBetterTogetherSuiteEnabledPrefName, false);
EXPECT_EQ(
mojom::FeatureState::kDisabledByUser,
manager_->GetFeatureStates()[mojom::Feature::kBetterTogetherSuite]);
}
void VerifyFeatureStateChange(size_t expected_index,
mojom::Feature expected_feature,
mojom::FeatureState expected_feature_state) {
const FeatureStateManager::FeatureStatesMap& map =
fake_observer_->feature_state_updates()[expected_index];
const auto it = map.find(expected_feature);
EXPECT_NE(map.end(), it);
EXPECT_EQ(expected_feature_state, it->second);
}
void SetSoftwareFeatureState(
bool use_local_device,
multidevice::SoftwareFeature software_feature,
multidevice::SoftwareFeatureState software_feature_state) {
multidevice::RemoteDeviceRef& device =
use_local_device ? test_local_device_ : test_host_device_;
multidevice::GetMutableRemoteDevice(device)
->software_features[software_feature] = software_feature_state;
fake_device_sync_client_->NotifyNewDevicesSynced();
}
void SetAndroidSmsPairingState(bool is_paired) {
fake_android_sms_pairing_state_tracker_->SetPairingComplete(is_paired);
}
sync_preferences::TestingPrefServiceSyncable* test_pref_service() {
return test_pref_service_.get();
}
FeatureStateManager* manager() { return manager_.get(); }
private:
multidevice::RemoteDeviceRef test_local_device_;
multidevice::RemoteDeviceRef test_host_device_;
std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
test_pref_service_;
std::unique_ptr<FakeHostStatusProvider> fake_host_status_provider_;
std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_;
FakeAndroidSmsPairingStateTracker* fake_android_sms_pairing_state_tracker_;
std::unique_ptr<FakeFeatureStateManagerObserver> fake_observer_;
std::unique_ptr<FeatureStateManager> manager_;
DISALLOW_COPY_AND_ASSIGN(MultiDeviceSetupFeatureStateManagerImplTest);
};
TEST_F(MultiDeviceSetupFeatureStateManagerImplTest, BetterTogetherSuite) {
TryAllUnverifiedHostStatesAndVerifyFeatureState(
mojom::Feature::kBetterTogetherSuite);
SetVerifiedHost();
EXPECT_EQ(
mojom::FeatureState::kNotSupportedByChromebook,
manager()->GetFeatureStates()[mojom::Feature::kBetterTogetherSuite]);
SetSoftwareFeatureState(true /* use_local_device */,
multidevice::SoftwareFeature::kBetterTogetherClient,
multidevice::SoftwareFeatureState::kSupported);
EXPECT_EQ(
mojom::FeatureState::kEnabledByUser,
manager()->GetFeatureStates()[mojom::Feature::kBetterTogetherSuite]);
VerifyFeatureStateChange(1u /* expected_index */,
mojom::Feature::kBetterTogetherSuite,
mojom::FeatureState::kEnabledByUser);
test_pref_service()->SetBoolean(kBetterTogetherSuiteEnabledPrefName, false);
EXPECT_EQ(
mojom::FeatureState::kDisabledByUser,
manager()->GetFeatureStates()[mojom::Feature::kBetterTogetherSuite]);
VerifyFeatureStateChange(2u /* expected_index */,
mojom::Feature::kBetterTogetherSuite,
mojom::FeatureState::kDisabledByUser);
// Set all features to prohibited. This should cause the Better Together suite
// to become prohibited as well.
test_pref_service()->SetBoolean(kInstantTetheringAllowedPrefName, false);
test_pref_service()->SetBoolean(kMessagesAllowedPrefName, false);
test_pref_service()->SetBoolean(kSmartLockAllowedPrefName, false);
EXPECT_EQ(
mojom::FeatureState::kProhibitedByPolicy,
manager()->GetFeatureStates()[mojom::Feature::kBetterTogetherSuite]);
VerifyFeatureStateChange(5u /* expected_index */,
mojom::Feature::kBetterTogetherSuite,
mojom::FeatureState::kProhibitedByPolicy);
}
TEST_F(MultiDeviceSetupFeatureStateManagerImplTest, InstantTethering) {
TryAllUnverifiedHostStatesAndVerifyFeatureState(
mojom::Feature::kInstantTethering);
SetVerifiedHost();
EXPECT_EQ(mojom::FeatureState::kNotSupportedByChromebook,
manager()->GetFeatureStates()[mojom::Feature::kInstantTethering]);
SetSoftwareFeatureState(true /* use_local_device */,
multidevice::SoftwareFeature::kInstantTetheringClient,
multidevice::SoftwareFeatureState::kSupported);
EXPECT_EQ(mojom::FeatureState::kNotSupportedByPhone,
manager()->GetFeatureStates()[mojom::Feature::kInstantTethering]);
VerifyFeatureStateChange(1u /* expected_index */,
mojom::Feature::kInstantTethering,
mojom::FeatureState::kNotSupportedByPhone);
SetSoftwareFeatureState(false /* use_local_device */,
multidevice::SoftwareFeature::kInstantTetheringHost,
multidevice::SoftwareFeatureState::kEnabled);
EXPECT_EQ(mojom::FeatureState::kEnabledByUser,
manager()->GetFeatureStates()[mojom::Feature::kInstantTethering]);
VerifyFeatureStateChange(2u /* expected_index */,
mojom::Feature::kInstantTethering,
mojom::FeatureState::kEnabledByUser);
MakeBetterTogetherSuiteDisabledByUser();
EXPECT_EQ(mojom::FeatureState::kUnavailableSuiteDisabled,
manager()->GetFeatureStates()[mojom::Feature::kInstantTethering]);
VerifyFeatureStateChange(4u /* expected_index */,
mojom::Feature::kInstantTethering,
mojom::FeatureState::kUnavailableSuiteDisabled);
test_pref_service()->SetBoolean(kInstantTetheringEnabledPrefName, false);
EXPECT_EQ(mojom::FeatureState::kDisabledByUser,
manager()->GetFeatureStates()[mojom::Feature::kInstantTethering]);
VerifyFeatureStateChange(5u /* expected_index */,
mojom::Feature::kInstantTethering,
mojom::FeatureState::kDisabledByUser);
test_pref_service()->SetBoolean(kInstantTetheringAllowedPrefName, false);
EXPECT_EQ(mojom::FeatureState::kProhibitedByPolicy,
manager()->GetFeatureStates()[mojom::Feature::kInstantTethering]);
VerifyFeatureStateChange(6u /* expected_index */,
mojom::Feature::kInstantTethering,
mojom::FeatureState::kProhibitedByPolicy);
}
TEST_F(MultiDeviceSetupFeatureStateManagerImplTest, Messages) {
TryAllUnverifiedHostStatesAndVerifyFeatureState(mojom::Feature::kMessages);
SetVerifiedHost();
EXPECT_EQ(mojom::FeatureState::kNotSupportedByChromebook,
manager()->GetFeatureStates()[mojom::Feature::kMessages]);
SetSoftwareFeatureState(true /* use_local_device */,
multidevice::SoftwareFeature::kMessagesForWebClient,
multidevice::SoftwareFeatureState::kSupported);
EXPECT_EQ(mojom::FeatureState::kNotSupportedByPhone,
manager()->GetFeatureStates()[mojom::Feature::kMessages]);
VerifyFeatureStateChange(1u /* expected_index */, mojom::Feature::kMessages,
mojom::FeatureState::kNotSupportedByPhone);
SetSoftwareFeatureState(false /* use_local_device */,
multidevice::SoftwareFeature::kMessagesForWebHost,
multidevice::SoftwareFeatureState::kEnabled);
EXPECT_EQ(mojom::FeatureState::kEnabledByUser,
manager()->GetFeatureStates()[mojom::Feature::kMessages]);
VerifyFeatureStateChange(2u /* expected_index */, mojom::Feature::kMessages,
mojom::FeatureState::kEnabledByUser);
SetAndroidSmsPairingState(false /* is_paired */);
EXPECT_EQ(mojom::FeatureState::kFurtherSetupRequired,
manager()->GetFeatureStates()[mojom::Feature::kMessages]);
VerifyFeatureStateChange(3u /* expected_index */, mojom::Feature::kMessages,
mojom::FeatureState::kFurtherSetupRequired);
SetAndroidSmsPairingState(true /* is_paired */);
EXPECT_EQ(mojom::FeatureState::kEnabledByUser,
manager()->GetFeatureStates()[mojom::Feature::kMessages]);
VerifyFeatureStateChange(4u /* expected_index */, mojom::Feature::kMessages,
mojom::FeatureState::kEnabledByUser);
SetAndroidSmsPairingState(false /* is_paired */);
MakeBetterTogetherSuiteDisabledByUser();
EXPECT_EQ(mojom::FeatureState::kUnavailableSuiteDisabled,
manager()->GetFeatureStates()[mojom::Feature::kMessages]);
VerifyFeatureStateChange(7u /* expected_index */, mojom::Feature::kMessages,
mojom::FeatureState::kUnavailableSuiteDisabled);
SetAndroidSmsPairingState(true /* is_paired */);
EXPECT_EQ(mojom::FeatureState::kUnavailableSuiteDisabled,
manager()->GetFeatureStates()[mojom::Feature::kMessages]);
test_pref_service()->SetBoolean(kMessagesEnabledPrefName, false);
EXPECT_EQ(mojom::FeatureState::kDisabledByUser,
manager()->GetFeatureStates()[mojom::Feature::kMessages]);
VerifyFeatureStateChange(8u /* expected_index */, mojom::Feature::kMessages,
mojom::FeatureState::kDisabledByUser);
test_pref_service()->SetBoolean(kMessagesAllowedPrefName, false);
EXPECT_EQ(mojom::FeatureState::kProhibitedByPolicy,
manager()->GetFeatureStates()[mojom::Feature::kMessages]);
VerifyFeatureStateChange(9u /* expected_index */, mojom::Feature::kMessages,
mojom::FeatureState::kProhibitedByPolicy);
}
TEST_F(MultiDeviceSetupFeatureStateManagerImplTest, SmartLock) {
TryAllUnverifiedHostStatesAndVerifyFeatureState(mojom::Feature::kSmartLock);
SetVerifiedHost();
EXPECT_EQ(mojom::FeatureState::kNotSupportedByChromebook,
manager()->GetFeatureStates()[mojom::Feature::kSmartLock]);
SetSoftwareFeatureState(true /* use_local_device */,
multidevice::SoftwareFeature::kSmartLockClient,
multidevice::SoftwareFeatureState::kSupported);
EXPECT_EQ(mojom::FeatureState::kUnavailableInsufficientSecurity,
manager()->GetFeatureStates()[mojom::Feature::kSmartLock]);
VerifyFeatureStateChange(
1u /* expected_index */, mojom::Feature::kSmartLock,
mojom::FeatureState::kUnavailableInsufficientSecurity);
SetSoftwareFeatureState(false /* use_local_device */,
multidevice::SoftwareFeature::kSmartLockHost,
multidevice::SoftwareFeatureState::kEnabled);
EXPECT_EQ(mojom::FeatureState::kEnabledByUser,
manager()->GetFeatureStates()[mojom::Feature::kSmartLock]);
VerifyFeatureStateChange(2u /* expected_index */, mojom::Feature::kSmartLock,
mojom::FeatureState::kEnabledByUser);
MakeBetterTogetherSuiteDisabledByUser();
EXPECT_EQ(mojom::FeatureState::kUnavailableSuiteDisabled,
manager()->GetFeatureStates()[mojom::Feature::kSmartLock]);
VerifyFeatureStateChange(4u /* expected_index */, mojom::Feature::kSmartLock,
mojom::FeatureState::kUnavailableSuiteDisabled);
test_pref_service()->SetBoolean(kSmartLockEnabledPrefName, false);
EXPECT_EQ(mojom::FeatureState::kDisabledByUser,
manager()->GetFeatureStates()[mojom::Feature::kSmartLock]);
VerifyFeatureStateChange(5u /* expected_index */, mojom::Feature::kSmartLock,
mojom::FeatureState::kDisabledByUser);
test_pref_service()->SetBoolean(kSmartLockAllowedPrefName, false);
EXPECT_EQ(mojom::FeatureState::kProhibitedByPolicy,
manager()->GetFeatureStates()[mojom::Feature::kSmartLock]);
VerifyFeatureStateChange(6u /* expected_index */, mojom::Feature::kSmartLock,
mojom::FeatureState::kProhibitedByPolicy);
}
} // namespace multidevice_setup
} // namespace chromeos