123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- // 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 "ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler.h"
- #include <memory>
- #include "ash/components/multidevice/remote_device_test_util.h"
- #include "ash/constants/ash_features.h"
- #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
- #include "ash/services/multidevice_setup/fake_host_backend_delegate.h"
- #include "base/timer/mock_timer.h"
- #include "components/sync_preferences/testing_pref_service_syncable.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace ash {
- namespace multidevice_setup {
- namespace {
- const char kEasyUnlockHostIdToDisablePrefName[] =
- "multidevice_setup.easy_unlock_host_id_to_disable";
- const char kEasyUnlockHostInstanceIdToDisablePrefName[] =
- "multidevice_setup.easy_unlock_host_instance_id_to_disable";
- const char kNoDevice[] = "";
- const size_t kNumTestDevices = 2;
- } // namespace
- class MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest
- : public ::testing::Test {
- public:
- MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest(
- const MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest&) = delete;
- MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest& operator=(
- const MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest&) = delete;
- protected:
- MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest()
- : test_devices_(
- multidevice::CreateRemoteDeviceRefListForTest(kNumTestDevices)) {}
- ~MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest() override = default;
- // testing::Test:
- void SetUp() override {
- for (auto& device : test_devices_) {
- // Don't rely on a legacy device ID if not using v1 DeviceSync, even
- // though we almost always expect one in practice.
- if (!features::ShouldUseV1DeviceSync())
- GetMutableRemoteDevice(device)->public_key.clear();
- }
- fake_host_backend_delegate_ = std::make_unique<FakeHostBackendDelegate>();
- fake_device_sync_client_ =
- std::make_unique<device_sync::FakeDeviceSyncClient>();
- fake_device_sync_client_->set_synced_devices(test_devices_);
- test_pref_service_ =
- std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
- GrandfatheredEasyUnlockHostDisabler::RegisterPrefs(
- test_pref_service_->registry());
- }
- void SetHost(const absl::optional<multidevice::RemoteDeviceRef>& host_device,
- multidevice::SoftwareFeature host_type) {
- if (host_type != multidevice::SoftwareFeature::kBetterTogetherHost &&
- host_type != multidevice::SoftwareFeature::kSmartLockHost)
- return;
- for (const auto& remote_device : test_devices_) {
- bool should_be_host =
- host_device != absl::nullopt &&
- host_device->GetDeviceId() == remote_device.GetDeviceId() &&
- host_device->instance_id() == remote_device.instance_id();
- GetMutableRemoteDevice(remote_device)->software_features[host_type] =
- should_be_host ? multidevice::SoftwareFeatureState::kEnabled
- : multidevice::SoftwareFeatureState::kSupported;
- }
- if (host_type == multidevice::SoftwareFeature::kBetterTogetherHost)
- fake_host_backend_delegate_->NotifyHostChangedOnBackend(host_device);
- }
- void InitializeTest(
- absl::optional<multidevice::RemoteDeviceRef> initial_device_in_prefs,
- absl::optional<multidevice::RemoteDeviceRef> initial_better_together_host,
- absl::optional<multidevice::RemoteDeviceRef> initial_easy_unlock_host) {
- test_pref_service_->SetString(kEasyUnlockHostIdToDisablePrefName,
- initial_device_in_prefs
- ? initial_device_in_prefs->GetDeviceId()
- : kNoDevice);
- test_pref_service_->SetString(kEasyUnlockHostInstanceIdToDisablePrefName,
- initial_device_in_prefs
- ? initial_device_in_prefs->instance_id()
- : kNoDevice);
- SetHost(initial_better_together_host,
- multidevice::SoftwareFeature::kBetterTogetherHost);
- SetHost(initial_easy_unlock_host,
- multidevice::SoftwareFeature::kSmartLockHost);
- auto mock_timer = std::make_unique<base::MockOneShotTimer>();
- mock_timer_ = mock_timer.get();
- grandfathered_easy_unlock_host_disabler_ =
- GrandfatheredEasyUnlockHostDisabler::Factory::Create(
- fake_host_backend_delegate_.get(), fake_device_sync_client_.get(),
- test_pref_service_.get(), std::move(mock_timer));
- }
- // Verify that the IDs for |expected_device| are stored in prefs. If
- // |expected_device| is null, prefs should have value |kNoDevice|.
- void VerifyDeviceInPrefs(
- const absl::optional<multidevice::RemoteDeviceRef>& expected_device) {
- if (!expected_device) {
- EXPECT_EQ(kNoDevice, test_pref_service_->GetString(
- kEasyUnlockHostIdToDisablePrefName));
- EXPECT_EQ(kNoDevice, test_pref_service_->GetString(
- kEasyUnlockHostInstanceIdToDisablePrefName));
- return;
- }
- EXPECT_EQ(
- expected_device->GetDeviceId().empty() ? kNoDevice
- : expected_device->GetDeviceId(),
- test_pref_service_->GetString(kEasyUnlockHostIdToDisablePrefName));
- EXPECT_EQ(expected_device->instance_id().empty()
- ? kNoDevice
- : expected_device->instance_id(),
- test_pref_service_->GetString(
- kEasyUnlockHostInstanceIdToDisablePrefName));
- }
- void VerifyEasyUnlockHostDisableRequest(
- int expected_queue_size,
- const absl::optional<multidevice::RemoteDeviceRef>& expected_host) {
- EXPECT_EQ(
- expected_queue_size,
- features::ShouldUseV1DeviceSync()
- ? fake_device_sync_client_
- ->GetSetSoftwareFeatureStateInputsQueueSize()
- : fake_device_sync_client_->GetSetFeatureStatusInputsQueueSize());
- if (expected_queue_size > 0) {
- ASSERT_TRUE(expected_host);
- VerifyLatestEasyUnlockHostDisableRequest(*expected_host);
- }
- }
- void InvokePendingEasyUnlockHostDisableRequestCallback(
- device_sync::mojom::NetworkRequestResult result_code) {
- if (features::ShouldUseV1DeviceSync()) {
- fake_device_sync_client_->InvokePendingSetSoftwareFeatureStateCallback(
- result_code);
- } else {
- fake_device_sync_client_->InvokePendingSetFeatureStatusCallback(
- result_code);
- }
- }
- const multidevice::RemoteDeviceRefList& test_devices() const {
- return test_devices_;
- }
- device_sync::FakeDeviceSyncClient* fake_device_sync_client() const {
- return fake_device_sync_client_.get();
- }
- base::MockOneShotTimer* mock_timer() const { return mock_timer_; }
- private:
- void VerifyLatestEasyUnlockHostDisableRequest(
- const multidevice::RemoteDeviceRef& expected_host) {
- // Verify inputs to SetSoftwareFeatureState().
- 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::kSmartLockHost,
- inputs.software_feature);
- EXPECT_FALSE(inputs.enabled);
- EXPECT_FALSE(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::kSmartLockHost, inputs.feature);
- EXPECT_EQ(device_sync::FeatureStatusChange::kDisable, inputs.status_change);
- }
- multidevice::RemoteDeviceRefList test_devices_;
- std::unique_ptr<FakeHostBackendDelegate> fake_host_backend_delegate_;
- std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_;
- std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
- test_pref_service_;
- base::MockOneShotTimer* mock_timer_ = nullptr;
- std::unique_ptr<GrandfatheredEasyUnlockHostDisabler>
- grandfathered_easy_unlock_host_disabler_;
- };
- // Situation #1:
- // BTH = BETTER_TOGETHER_HOST, 0 = disabled, A = devices[0]
- // EUH = EASY_UNLOCK_HOST, 1 = enabled, B = devices[1]
- //
- // | A | B | | A | B |
- // ---+---+---+ ---+---+---+
- // BTH| 1 | 0 | BTH| 0 | 0 |
- // ---+---+---+ ---> ---+---+---+
- // EUH| 1 | 0 | EUH| 1 | 0 |
- //
- // Grandfathering prevents EUH from being disabled automatically. This class
- // disables EUH manually.
- TEST_F(
- MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
- IfBetterTogetherHostChangedFromOneDeviceToNoDeviceThenDisableEasyUnlock) {
- InitializeTest(absl::nullopt /* initial_device_in_prefs */,
- test_devices()[0] /* initial_better_together_host */,
- test_devices()[0] /* initial_easy_unlock_host */);
- SetHost(absl::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
- VerifyDeviceInPrefs(test_devices()[0]);
- VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
- test_devices()[0]);
- InvokePendingEasyUnlockHostDisableRequestCallback(
- device_sync::mojom::NetworkRequestResult::kSuccess);
- VerifyDeviceInPrefs(absl::nullopt /* expected_device */);
- EXPECT_FALSE(mock_timer()->IsRunning());
- }
- // Situation #2:
- // BTH = BETTER_TOGETHER_HOST, 0 = disabled, A = devices[0]
- // EUH = EASY_UNLOCK_HOST, 1 = enabled, B = devices[1]
- //
- // | A | B | | A | B |
- // ---+---+---+ ---+---+---+
- // BTH| 0 | 0 | BTH| 0 | 1 |
- // ---+---+---+ ---> ---+---+---+
- // EUH| 1 | 0 | EUH| 0 | 1 |
- //
- // The CryptAuth backend (via GmsCore) disables EUH on device A when BTH is
- // enabled (exclusively) on another device, B. No action necessary from this
- // class.
- TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
- IfBetterTogetherHostChangedFromNoDeviceToADeviceThenDoNothing) {
- InitializeTest(absl::nullopt /* initial_device_in_prefs */,
- absl::nullopt /* initial_better_together_host */,
- test_devices()[0] /* initial_easy_unlock_host */);
- SetHost(test_devices()[1], multidevice::SoftwareFeature::kBetterTogetherHost);
- VerifyDeviceInPrefs(absl::nullopt /* expected_device */);
- VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
- absl::nullopt /* expected_host */);
- }
- // Situation #3:
- // BTH = BETTER_TOGETHER_HOST, 0 = disabled, A = devices[0]
- // EUH = EASY_UNLOCK_HOST, 1 = enabled, B = devices[1]
- //
- // | A | B | | A | B |
- // ---+---+---+ ---+---+---+
- // BTH| 1 | 0 | BTH| 0 | 1 |
- // ---+---+---+ ---> ---+---+---+
- // EUH| 1 | 0 | EUH| 0 | 1 |
- //
- // The CryptAuth backend (via GmsCore) disables EUH on device A when BTH is
- // enabled (exclusively) on another device, B. We still attempt to disable
- // EUH in this case to be safe.
- TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
- IfBetterTogetherHostChangedFromOneDeviceToAnotherThenDisableEasyUnlock) {
- InitializeTest(absl::nullopt /* initial_device_in_prefs */,
- test_devices()[0] /* initial_better_together_host */,
- test_devices()[0] /* initial_easy_unlock_host */);
- SetHost(test_devices()[1], multidevice::SoftwareFeature::kBetterTogetherHost);
- VerifyDeviceInPrefs(test_devices()[0]);
- VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
- test_devices()[0]);
- InvokePendingEasyUnlockHostDisableRequestCallback(
- device_sync::mojom::NetworkRequestResult::kSuccess);
- VerifyDeviceInPrefs(absl::nullopt /* expected_device */);
- EXPECT_FALSE(mock_timer()->IsRunning());
- }
- TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
- IfDisablePendingThenConstructorAttemptsToDisableEasyUnlock) {
- InitializeTest(test_devices()[0] /* initial_device_in_prefs */,
- absl::nullopt /* initial_better_together_host */,
- test_devices()[0] /* initial_easy_unlock_host */);
- VerifyDeviceInPrefs(test_devices()[0]);
- VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
- test_devices()[0]);
- InvokePendingEasyUnlockHostDisableRequestCallback(
- device_sync::mojom::NetworkRequestResult::kSuccess);
- }
- // Situation #1 where device A is removed from list of synced devices:
- //
- // | A | B | | A | B |
- // ---+---+---+ ---+---+---+
- // BTH| 1 | 0 | BTH| 0 | 0 |
- // ---+---+---+ ---> ---+---+---+
- // EUH| 1 | 0 | EUH| 1 | 0 |
- TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
- IfHostToDisableIsNotInListOfSyncedDevicesThenClearPref) {
- InitializeTest(absl::nullopt /* initial_device_in_prefs */,
- test_devices()[0] /* initial_better_together_host */,
- test_devices()[0] /* initial_easy_unlock_host */);
- // Remove device[0] from list
- fake_device_sync_client()->set_synced_devices({test_devices()[1]});
- SetHost(absl::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
- VerifyDeviceInPrefs(absl::nullopt /* expected_device */);
- VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
- absl::nullopt /* expected_host */);
- }
- // Situation #1 with failure:
- //
- // | A | B | | A | B |
- // ---+---+---+ ---+---+---+
- // BTH| 1 | 0 | BTH| 0 | 0 |
- // ---+---+---+ ---> ---+---+---+
- // EUH| 1 | 0 | EUH| 1 | 0 |
- TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
- IfEasyUnlockDisableUnsuccessfulThenScheduleRetry) {
- InitializeTest(absl::nullopt /* initial_device_in_prefs */,
- test_devices()[0] /* initial_better_together_host */,
- test_devices()[0] /* initial_easy_unlock_host */);
- SetHost(absl::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
- VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
- test_devices()[0]);
- InvokePendingEasyUnlockHostDisableRequestCallback(
- device_sync::mojom::NetworkRequestResult::kInternalServerError);
- VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
- absl::nullopt /* expected_host */);
- VerifyDeviceInPrefs(test_devices()[0]);
- EXPECT_TRUE(mock_timer()->IsRunning());
- mock_timer()->Fire();
- VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
- test_devices()[0]);
- }
- TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
- IfNoDisablePendingThenConstructorDoesNothing) {
- InitializeTest(absl::nullopt /* initial_device_in_prefs */,
- absl::nullopt /* initial_better_together_host */,
- test_devices()[0] /* initial_easy_unlock_host */);
- VerifyDeviceInPrefs(absl::nullopt /* expected_device */);
- VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
- absl::nullopt /* expected_host */);
- }
- TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
- IfDisablePendingButIsNotCurrentEasyUnlockHostThenClearPref) {
- InitializeTest(test_devices()[0] /* initial_device_in_prefs */,
- test_devices()[1] /* initial_better_together_host */,
- test_devices()[1] /* initial_easy_unlock_host */);
- VerifyDeviceInPrefs(absl::nullopt /* expected_device */);
- VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
- absl::nullopt /* expected_host */);
- }
- TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
- IfDisablePendingButIsCurrentBetterTogetherHostThenClearPref) {
- InitializeTest(test_devices()[0] /* initial_device_in_prefs */,
- test_devices()[0] /* initial_better_together_host */,
- test_devices()[0] /* initial_easy_unlock_host */);
- VerifyDeviceInPrefs(absl::nullopt /* expected_device */);
- VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
- absl::nullopt /* expected_host */);
- }
- // Simulate:
- // - Disable BETTER_TOGETHER_HOST on device 0
- // - GrandfatheredEasyUnlockHostDisabler tries to disable EASY_UNLOCK_HOST on
- // device 0 but fails
- // - Timer is running while we wait to retry
- // - Re-enable BETTER_TOGETHER_HOST on device 0
- TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
- IfHostChangesWhileRetryTimerIsRunningThenCancelTimerAndClearPref) {
- InitializeTest(absl::nullopt /* initial_device_in_prefs */,
- test_devices()[0] /* initial_better_together_host */,
- test_devices()[0] /* initial_easy_unlock_host */);
- SetHost(absl::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
- VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
- test_devices()[0]);
- InvokePendingEasyUnlockHostDisableRequestCallback(
- device_sync::mojom::NetworkRequestResult::kInternalServerError);
- EXPECT_TRUE(mock_timer()->IsRunning());
- SetHost(test_devices()[0], multidevice::SoftwareFeature::kBetterTogetherHost);
- VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
- absl::nullopt /* expected_host */);
- EXPECT_FALSE(mock_timer()->IsRunning());
- VerifyDeviceInPrefs(absl::nullopt /* expected_device */);
- }
- // Simulate:
- // - Set device 0 as host
- // - Disable host
- // - Set device 1 as host
- // - Disable host
- // - SetSoftwareFeatureState callback for device 0 is called
- TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
- IfDifferentHostDisabledBeforeFirstCallbackThenFirstCallbackDoesNothing) {
- InitializeTest(absl::nullopt /* initial_device_in_prefs */,
- test_devices()[0] /* initial_better_together_host */,
- test_devices()[0] /* initial_easy_unlock_host */);
- SetHost(absl::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
- VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
- test_devices()[0]);
- SetHost(test_devices()[1], multidevice::SoftwareFeature::kBetterTogetherHost);
- SetHost(test_devices()[1], multidevice::SoftwareFeature::kSmartLockHost);
- SetHost(absl::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
- VerifyEasyUnlockHostDisableRequest(2 /* expected_queue_size */,
- test_devices()[1]);
- VerifyDeviceInPrefs(test_devices()[1]);
- InvokePendingEasyUnlockHostDisableRequestCallback(
- device_sync::mojom::NetworkRequestResult::kSuccess);
- VerifyDeviceInPrefs(test_devices()[1]);
- }
- } // namespace multidevice_setup
- } // namespace ash
|