1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chromeos/services/multidevice_setup/grandfathered_easy_unlock_host_disabler.h"
6 
7 #include <memory>
8 
9 #include "base/macros.h"
10 #include "base/timer/mock_timer.h"
11 #include "chromeos/components/multidevice/remote_device_test_util.h"
12 #include "chromeos/constants/chromeos_features.h"
13 #include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h"
14 #include "chromeos/services/multidevice_setup/fake_host_backend_delegate.h"
15 #include "components/sync_preferences/testing_pref_service_syncable.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 
18 namespace chromeos {
19 
20 namespace multidevice_setup {
21 
22 namespace {
23 
24 // Parameterized test types, indicating the following test scenarios:
25 enum class TestType {
26   // Use v1 DeviceSync and host does not have an Instance ID.
27   kYesV1NoInstanceId,
28   // Use v1 DeviceSync and host has an Instance ID.
29   kYesV1YesInstanceId,
30   // Do not use v1 DeviceSync and host has an Instance ID.
31   kNoV1YesInstanceId
32 };
33 
34 const char kEasyUnlockHostIdToDisablePrefName[] =
35     "multidevice_setup.easy_unlock_host_id_to_disable";
36 const char kEasyUnlockHostInstanceIdToDisablePrefName[] =
37     "multidevice_setup.easy_unlock_host_instance_id_to_disable";
38 
39 const char kNoDevice[] = "";
40 
41 const size_t kNumTestDevices = 2;
42 
43 }  // namespace
44 
45 class MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest
46     : public ::testing::TestWithParam<TestType> {
47  protected:
MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest()48   MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest()
49       : test_devices_(
50             multidevice::CreateRemoteDeviceRefListForTest(kNumTestDevices)) {}
51   ~MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest() override = default;
52 
53   // testing::Test:
SetUp()54   void SetUp() override {
55     SetDeviceSyncFeatureFlags();
56 
57     for (auto& device : test_devices_) {
58       if (!HasInstanceId())
59         GetMutableRemoteDevice(device)->instance_id.clear();
60 
61       // Don't rely on a legacy device ID if not using v1 DeviceSync, even
62       // though we almost always expect one in practice.
63       if (!features::ShouldUseV1DeviceSync())
64         GetMutableRemoteDevice(device)->public_key.clear();
65     }
66 
67     fake_host_backend_delegate_ = std::make_unique<FakeHostBackendDelegate>();
68 
69     fake_device_sync_client_ =
70         std::make_unique<device_sync::FakeDeviceSyncClient>();
71     fake_device_sync_client_->set_synced_devices(test_devices_);
72 
73     test_pref_service_ =
74         std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
75     GrandfatheredEasyUnlockHostDisabler::RegisterPrefs(
76         test_pref_service_->registry());
77   }
78 
SetHost(const base::Optional<multidevice::RemoteDeviceRef> & host_device,multidevice::SoftwareFeature host_type)79   void SetHost(const base::Optional<multidevice::RemoteDeviceRef>& host_device,
80                multidevice::SoftwareFeature host_type) {
81     if (host_type != multidevice::SoftwareFeature::kBetterTogetherHost &&
82         host_type != multidevice::SoftwareFeature::kSmartLockHost)
83       return;
84 
85     for (const auto& remote_device : test_devices_) {
86       bool should_be_host =
87           host_device != base::nullopt &&
88           host_device->GetDeviceId() == remote_device.GetDeviceId() &&
89           host_device->instance_id() == remote_device.instance_id();
90 
91       GetMutableRemoteDevice(remote_device)->software_features[host_type] =
92           should_be_host ? multidevice::SoftwareFeatureState::kEnabled
93                          : multidevice::SoftwareFeatureState::kSupported;
94     }
95 
96     if (host_type == multidevice::SoftwareFeature::kBetterTogetherHost)
97       fake_host_backend_delegate_->NotifyHostChangedOnBackend(host_device);
98   }
99 
InitializeTest(base::Optional<multidevice::RemoteDeviceRef> initial_device_in_prefs,base::Optional<multidevice::RemoteDeviceRef> initial_better_together_host,base::Optional<multidevice::RemoteDeviceRef> initial_easy_unlock_host)100   void InitializeTest(
101       base::Optional<multidevice::RemoteDeviceRef> initial_device_in_prefs,
102       base::Optional<multidevice::RemoteDeviceRef> initial_better_together_host,
103       base::Optional<multidevice::RemoteDeviceRef> initial_easy_unlock_host) {
104     test_pref_service_->SetString(kEasyUnlockHostIdToDisablePrefName,
105                                   initial_device_in_prefs
106                                       ? initial_device_in_prefs->GetDeviceId()
107                                       : kNoDevice);
108     test_pref_service_->SetString(kEasyUnlockHostInstanceIdToDisablePrefName,
109                                   initial_device_in_prefs
110                                       ? initial_device_in_prefs->instance_id()
111                                       : kNoDevice);
112 
113     SetHost(initial_better_together_host,
114             multidevice::SoftwareFeature::kBetterTogetherHost);
115     SetHost(initial_easy_unlock_host,
116             multidevice::SoftwareFeature::kSmartLockHost);
117 
118     auto mock_timer = std::make_unique<base::MockOneShotTimer>();
119     mock_timer_ = mock_timer.get();
120 
121     grandfathered_easy_unlock_host_disabler_ =
122         GrandfatheredEasyUnlockHostDisabler::Factory::Create(
123             fake_host_backend_delegate_.get(), fake_device_sync_client_.get(),
124             test_pref_service_.get(), std::move(mock_timer));
125   }
126 
127   // Verify that the IDs for |expected_device| are stored in prefs. If
128   // |expected_device| is null, prefs should have value |kNoDevice|.
VerifyDeviceInPrefs(const base::Optional<multidevice::RemoteDeviceRef> & expected_device)129   void VerifyDeviceInPrefs(
130       const base::Optional<multidevice::RemoteDeviceRef>& expected_device) {
131     if (!expected_device) {
132       EXPECT_EQ(kNoDevice, test_pref_service_->GetString(
133                                kEasyUnlockHostIdToDisablePrefName));
134       EXPECT_EQ(kNoDevice, test_pref_service_->GetString(
135                                kEasyUnlockHostInstanceIdToDisablePrefName));
136       return;
137     }
138 
139     EXPECT_EQ(
140         expected_device->GetDeviceId().empty() ? kNoDevice
141                                                : expected_device->GetDeviceId(),
142         test_pref_service_->GetString(kEasyUnlockHostIdToDisablePrefName));
143     EXPECT_EQ(expected_device->instance_id().empty()
144                   ? kNoDevice
145                   : expected_device->instance_id(),
146               test_pref_service_->GetString(
147                   kEasyUnlockHostInstanceIdToDisablePrefName));
148   }
149 
VerifyEasyUnlockHostDisableRequest(int expected_queue_size,const base::Optional<multidevice::RemoteDeviceRef> & expected_host)150   void VerifyEasyUnlockHostDisableRequest(
151       int expected_queue_size,
152       const base::Optional<multidevice::RemoteDeviceRef>& expected_host) {
153     EXPECT_EQ(
154         expected_queue_size,
155         features::ShouldUseV1DeviceSync()
156             ? fake_device_sync_client_
157                   ->GetSetSoftwareFeatureStateInputsQueueSize()
158             : fake_device_sync_client_->GetSetFeatureStatusInputsQueueSize());
159     if (expected_queue_size > 0) {
160       ASSERT_TRUE(expected_host);
161       VerifyLatestEasyUnlockHostDisableRequest(*expected_host);
162     }
163   }
164 
InvokePendingEasyUnlockHostDisableRequestCallback(device_sync::mojom::NetworkRequestResult result_code)165   void InvokePendingEasyUnlockHostDisableRequestCallback(
166       device_sync::mojom::NetworkRequestResult result_code) {
167     if (features::ShouldUseV1DeviceSync()) {
168       fake_device_sync_client_->InvokePendingSetSoftwareFeatureStateCallback(
169           result_code);
170     } else {
171       fake_device_sync_client_->InvokePendingSetFeatureStatusCallback(
172           result_code);
173     }
174   }
175 
test_devices() const176   const multidevice::RemoteDeviceRefList& test_devices() const {
177     return test_devices_;
178   }
179 
fake_device_sync_client() const180   device_sync::FakeDeviceSyncClient* fake_device_sync_client() const {
181     return fake_device_sync_client_.get();
182   }
183 
mock_timer() const184   base::MockOneShotTimer* mock_timer() const { return mock_timer_; }
185 
186  private:
HasInstanceId()187   bool HasInstanceId() {
188     switch (GetParam()) {
189       case TestType::kYesV1YesInstanceId:
190         FALLTHROUGH;
191       case TestType::kNoV1YesInstanceId:
192         return true;
193       case TestType::kYesV1NoInstanceId:
194         return false;
195     }
196   }
197 
SetDeviceSyncFeatureFlags()198   void SetDeviceSyncFeatureFlags() {
199     bool use_v1;
200     switch (GetParam()) {
201       case TestType::kYesV1YesInstanceId:
202         FALLTHROUGH;
203       case TestType::kYesV1NoInstanceId:
204         use_v1 = true;
205         break;
206       case TestType::kNoV1YesInstanceId:
207         use_v1 = false;
208         break;
209     }
210   }
211 
VerifyLatestEasyUnlockHostDisableRequest(const multidevice::RemoteDeviceRef & expected_host)212   void VerifyLatestEasyUnlockHostDisableRequest(
213       const multidevice::RemoteDeviceRef& expected_host) {
214     // Verify inputs to SetSoftwareFeatureState().
215     if (features::ShouldUseV1DeviceSync()) {
216       ASSERT_FALSE(
217           fake_device_sync_client_->set_software_feature_state_inputs_queue()
218               .empty());
219       const device_sync::FakeDeviceSyncClient::SetSoftwareFeatureStateInputs&
220           inputs = fake_device_sync_client_
221                        ->set_software_feature_state_inputs_queue()
222                        .back();
223       EXPECT_EQ(expected_host.public_key(), inputs.public_key);
224       EXPECT_EQ(multidevice::SoftwareFeature::kSmartLockHost,
225                 inputs.software_feature);
226       EXPECT_FALSE(inputs.enabled);
227       EXPECT_FALSE(inputs.is_exclusive);
228       return;
229     }
230 
231     // Verify inputs to SetFeatureStatus().
232     ASSERT_FALSE(
233         fake_device_sync_client_->set_feature_status_inputs_queue().empty());
234     const device_sync::FakeDeviceSyncClient::SetFeatureStatusInputs& inputs =
235         fake_device_sync_client_->set_feature_status_inputs_queue().back();
236     EXPECT_EQ(expected_host.instance_id(), inputs.device_instance_id);
237     EXPECT_EQ(multidevice::SoftwareFeature::kSmartLockHost, inputs.feature);
238     EXPECT_EQ(device_sync::FeatureStatusChange::kDisable, inputs.status_change);
239   }
240 
241   multidevice::RemoteDeviceRefList test_devices_;
242 
243   std::unique_ptr<FakeHostBackendDelegate> fake_host_backend_delegate_;
244   std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_;
245   std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
246       test_pref_service_;
247   base::MockOneShotTimer* mock_timer_ = nullptr;
248 
249   std::unique_ptr<GrandfatheredEasyUnlockHostDisabler>
250       grandfathered_easy_unlock_host_disabler_;
251 
252   DISALLOW_COPY_AND_ASSIGN(
253       MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest);
254 };
255 
256 // Situation #1:
257 //   BTH = BETTER_TOGETHER_HOST, 0 = disabled, A = devices[0]
258 //   EUH = EASY_UNLOCK_HOST,     1 = enabled,  B = devices[1]
259 //
260 //    | A | B |           | A | B |
261 // ---+---+---+        ---+---+---+
262 // BTH| 1 | 0 |        BTH| 0 | 0 |
263 // ---+---+---+  --->  ---+---+---+
264 // EUH| 1 | 0 |        EUH| 1 | 0 |
265 //
266 // Grandfathering prevents EUH from being disabled automatically. This class
267 // disables EUH manually.
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfBetterTogetherHostChangedFromOneDeviceToNoDeviceThenDisableEasyUnlock)268 TEST_P(
269     MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
270     IfBetterTogetherHostChangedFromOneDeviceToNoDeviceThenDisableEasyUnlock) {
271   InitializeTest(base::nullopt /* initial_device_in_prefs */,
272                  test_devices()[0] /* initial_better_together_host */,
273                  test_devices()[0] /* initial_easy_unlock_host */);
274 
275   SetHost(base::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
276 
277   VerifyDeviceInPrefs(test_devices()[0]);
278   VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
279                                      test_devices()[0]);
280   InvokePendingEasyUnlockHostDisableRequestCallback(
281       device_sync::mojom::NetworkRequestResult::kSuccess);
282 
283   VerifyDeviceInPrefs(base::nullopt /* expected_device */);
284   EXPECT_FALSE(mock_timer()->IsRunning());
285 }
286 
287 // Situation #2:
288 //   BTH = BETTER_TOGETHER_HOST, 0 = disabled, A = devices[0]
289 //   EUH = EASY_UNLOCK_HOST,     1 = enabled,  B = devices[1]
290 //
291 //    | A | B |           | A | B |
292 // ---+---+---+        ---+---+---+
293 // BTH| 0 | 0 |        BTH| 0 | 1 |
294 // ---+---+---+  --->  ---+---+---+
295 // EUH| 1 | 0 |        EUH| 0 | 1 |
296 //
297 // The CryptAuth backend (via GmsCore) disables EUH on device A when BTH is
298 // enabled (exclusively) on another device, B. No action necessary from this
299 // class.
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfBetterTogetherHostChangedFromNoDeviceToADeviceThenDoNothing)300 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
301        IfBetterTogetherHostChangedFromNoDeviceToADeviceThenDoNothing) {
302   InitializeTest(base::nullopt /* initial_device_in_prefs */,
303                  base::nullopt /* initial_better_together_host */,
304                  test_devices()[0] /* initial_easy_unlock_host */);
305 
306   SetHost(test_devices()[1], multidevice::SoftwareFeature::kBetterTogetherHost);
307 
308   VerifyDeviceInPrefs(base::nullopt /* expected_device */);
309   VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
310                                      base::nullopt /* expected_host */);
311 }
312 
313 // Situation #3:
314 //   BTH = BETTER_TOGETHER_HOST, 0 = disabled, A = devices[0]
315 //   EUH = EASY_UNLOCK_HOST,     1 = enabled,  B = devices[1]
316 //
317 //    | A | B |           | A | B |
318 // ---+---+---+        ---+---+---+
319 // BTH| 1 | 0 |        BTH| 0 | 1 |
320 // ---+---+---+  --->  ---+---+---+
321 // EUH| 1 | 0 |        EUH| 0 | 1 |
322 //
323 // The CryptAuth backend (via GmsCore) disables EUH on device A when BTH is
324 // enabled (exclusively) on another device, B. We still attempt to disable
325 // EUH in this case to be safe.
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfBetterTogetherHostChangedFromOneDeviceToAnotherThenDisableEasyUnlock)326 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
327        IfBetterTogetherHostChangedFromOneDeviceToAnotherThenDisableEasyUnlock) {
328   InitializeTest(base::nullopt /* initial_device_in_prefs */,
329                  test_devices()[0] /* initial_better_together_host */,
330                  test_devices()[0] /* initial_easy_unlock_host */);
331 
332   SetHost(test_devices()[1], multidevice::SoftwareFeature::kBetterTogetherHost);
333 
334   VerifyDeviceInPrefs(test_devices()[0]);
335   VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
336                                      test_devices()[0]);
337   InvokePendingEasyUnlockHostDisableRequestCallback(
338       device_sync::mojom::NetworkRequestResult::kSuccess);
339 
340   VerifyDeviceInPrefs(base::nullopt /* expected_device */);
341   EXPECT_FALSE(mock_timer()->IsRunning());
342 }
343 
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfDisablePendingThenConstructorAttemptsToDisableEasyUnlock)344 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
345        IfDisablePendingThenConstructorAttemptsToDisableEasyUnlock) {
346   InitializeTest(test_devices()[0] /* initial_device_in_prefs */,
347                  base::nullopt /* initial_better_together_host */,
348                  test_devices()[0] /* initial_easy_unlock_host */);
349 
350   VerifyDeviceInPrefs(test_devices()[0]);
351   VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
352                                      test_devices()[0]);
353   InvokePendingEasyUnlockHostDisableRequestCallback(
354       device_sync::mojom::NetworkRequestResult::kSuccess);
355 }
356 
357 // Situation #1 where device A is removed from list of synced devices:
358 //
359 //    | A | B |           | A | B |
360 // ---+---+---+        ---+---+---+
361 // BTH| 1 | 0 |        BTH| 0 | 0 |
362 // ---+---+---+  --->  ---+---+---+
363 // EUH| 1 | 0 |        EUH| 1 | 0 |
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfHostToDisableIsNotInListOfSyncedDevicesThenClearPref)364 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
365        IfHostToDisableIsNotInListOfSyncedDevicesThenClearPref) {
366   InitializeTest(base::nullopt /* initial_device_in_prefs */,
367                  test_devices()[0] /* initial_better_together_host */,
368                  test_devices()[0] /* initial_easy_unlock_host */);
369 
370   // Remove device[0] from list
371   fake_device_sync_client()->set_synced_devices({test_devices()[1]});
372 
373   SetHost(base::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
374 
375   VerifyDeviceInPrefs(base::nullopt /* expected_device */);
376   VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
377                                      base::nullopt /* expected_host */);
378 }
379 
380 // Situation #1 with failure:
381 //
382 //    | A | B |           | A | B |
383 // ---+---+---+        ---+---+---+
384 // BTH| 1 | 0 |        BTH| 0 | 0 |
385 // ---+---+---+  --->  ---+---+---+
386 // EUH| 1 | 0 |        EUH| 1 | 0 |
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfEasyUnlockDisableUnsuccessfulThenScheduleRetry)387 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
388        IfEasyUnlockDisableUnsuccessfulThenScheduleRetry) {
389   InitializeTest(base::nullopt /* initial_device_in_prefs */,
390                  test_devices()[0] /* initial_better_together_host */,
391                  test_devices()[0] /* initial_easy_unlock_host */);
392 
393   SetHost(base::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
394 
395   VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
396                                      test_devices()[0]);
397   InvokePendingEasyUnlockHostDisableRequestCallback(
398       device_sync::mojom::NetworkRequestResult::kInternalServerError);
399 
400   VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
401                                      base::nullopt /* expected_host */);
402 
403   VerifyDeviceInPrefs(test_devices()[0]);
404   EXPECT_TRUE(mock_timer()->IsRunning());
405 
406   mock_timer()->Fire();
407 
408   VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
409                                      test_devices()[0]);
410 }
411 
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfNoDisablePendingThenConstructorDoesNothing)412 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
413        IfNoDisablePendingThenConstructorDoesNothing) {
414   InitializeTest(base::nullopt /* initial_device_in_prefs */,
415                  base::nullopt /* initial_better_together_host */,
416                  test_devices()[0] /* initial_easy_unlock_host */);
417 
418   VerifyDeviceInPrefs(base::nullopt /* expected_device */);
419   VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
420                                      base::nullopt /* expected_host */);
421 }
422 
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfDisablePendingButIsNotCurrentEasyUnlockHostThenClearPref)423 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
424        IfDisablePendingButIsNotCurrentEasyUnlockHostThenClearPref) {
425   InitializeTest(test_devices()[0] /* initial_device_in_prefs */,
426                  test_devices()[1] /* initial_better_together_host */,
427                  test_devices()[1] /* initial_easy_unlock_host */);
428 
429   VerifyDeviceInPrefs(base::nullopt /* expected_device */);
430   VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
431                                      base::nullopt /* expected_host */);
432 }
433 
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfDisablePendingButIsCurrentBetterTogetherHostThenClearPref)434 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
435        IfDisablePendingButIsCurrentBetterTogetherHostThenClearPref) {
436   InitializeTest(test_devices()[0] /* initial_device_in_prefs */,
437                  test_devices()[0] /* initial_better_together_host */,
438                  test_devices()[0] /* initial_easy_unlock_host */);
439 
440   VerifyDeviceInPrefs(base::nullopt /* expected_device */);
441   VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
442                                      base::nullopt /* expected_host */);
443 }
444 
445 // Simulate:
446 //   - Disable BETTER_TOGETHER_HOST on device 0
447 //   - GrandfatheredEasyUnlockHostDisabler tries to disable EASY_UNLOCK_HOST on
448 //     device 0 but fails
449 //   - Timer is running while we wait to retry
450 //   - Re-enable BETTER_TOGETHER_HOST on device 0
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfHostChangesWhileRetryTimerIsRunningThenCancelTimerAndClearPref)451 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
452        IfHostChangesWhileRetryTimerIsRunningThenCancelTimerAndClearPref) {
453   InitializeTest(base::nullopt /* initial_device_in_prefs */,
454                  test_devices()[0] /* initial_better_together_host */,
455                  test_devices()[0] /* initial_easy_unlock_host */);
456 
457   SetHost(base::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
458 
459   VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
460                                      test_devices()[0]);
461   InvokePendingEasyUnlockHostDisableRequestCallback(
462       device_sync::mojom::NetworkRequestResult::kInternalServerError);
463 
464   EXPECT_TRUE(mock_timer()->IsRunning());
465 
466   SetHost(test_devices()[0], multidevice::SoftwareFeature::kBetterTogetherHost);
467 
468   VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
469                                      base::nullopt /* expected_host */);
470 
471   EXPECT_FALSE(mock_timer()->IsRunning());
472   VerifyDeviceInPrefs(base::nullopt /* expected_device */);
473 }
474 
475 // Simulate:
476 //   - Set device 0 as host
477 //   - Disable host
478 //   - Set device 1 as host
479 //   - Disable host
480 //   - SetSoftwareFeatureState callback for device 0 is called
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfDifferentHostDisabledBeforeFirstCallbackThenFirstCallbackDoesNothing)481 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
482        IfDifferentHostDisabledBeforeFirstCallbackThenFirstCallbackDoesNothing) {
483   InitializeTest(base::nullopt /* initial_device_in_prefs */,
484                  test_devices()[0] /* initial_better_together_host */,
485                  test_devices()[0] /* initial_easy_unlock_host */);
486   SetHost(base::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
487   VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
488                                      test_devices()[0]);
489 
490   SetHost(test_devices()[1], multidevice::SoftwareFeature::kBetterTogetherHost);
491   SetHost(test_devices()[1], multidevice::SoftwareFeature::kSmartLockHost);
492   SetHost(base::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
493   VerifyEasyUnlockHostDisableRequest(2 /* expected_queue_size */,
494                                      test_devices()[1]);
495   VerifyDeviceInPrefs(test_devices()[1]);
496 
497   InvokePendingEasyUnlockHostDisableRequestCallback(
498       device_sync::mojom::NetworkRequestResult::kSuccess);
499   VerifyDeviceInPrefs(test_devices()[1]);
500 }
501 // Runs tests for the following scenarios.
502 //   - Use v1 DeviceSync and host does not have an Instance ID.
503 //   - Use v1 DeviceSync and host has an Instance ID.
504 //   - Do not use v1 DeviceSync and host has an Instance ID.
505 // TODO(https://crbug.com/1019206): Remove when v1 DeviceSync is disabled,
506 // when all devices should have an Instance ID.
507 INSTANTIATE_TEST_SUITE_P(
508     All,
509     MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
510     ::testing::Values(TestType::kYesV1NoInstanceId,
511                       TestType::kYesV1YesInstanceId,
512                       TestType::kNoV1YesInstanceId));
513 
514 }  // namespace multidevice_setup
515 
516 }  // namespace chromeos
517