grandfathered_easy_unlock_host_disabler_unittest.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  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. #include "ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler.h"
  5. #include <memory>
  6. #include "ash/components/multidevice/remote_device_test_util.h"
  7. #include "ash/constants/ash_features.h"
  8. #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
  9. #include "ash/services/multidevice_setup/fake_host_backend_delegate.h"
  10. #include "base/timer/mock_timer.h"
  11. #include "components/sync_preferences/testing_pref_service_syncable.h"
  12. #include "testing/gtest/include/gtest/gtest.h"
  13. namespace ash {
  14. namespace multidevice_setup {
  15. namespace {
  16. const char kEasyUnlockHostIdToDisablePrefName[] =
  17. "multidevice_setup.easy_unlock_host_id_to_disable";
  18. const char kEasyUnlockHostInstanceIdToDisablePrefName[] =
  19. "multidevice_setup.easy_unlock_host_instance_id_to_disable";
  20. const char kNoDevice[] = "";
  21. const size_t kNumTestDevices = 2;
  22. } // namespace
  23. class MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest
  24. : public ::testing::Test {
  25. public:
  26. MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest(
  27. const MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest&) = delete;
  28. MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest& operator=(
  29. const MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest&) = delete;
  30. protected:
  31. MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest()
  32. : test_devices_(
  33. multidevice::CreateRemoteDeviceRefListForTest(kNumTestDevices)) {}
  34. ~MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest() override = default;
  35. // testing::Test:
  36. void SetUp() override {
  37. for (auto& device : test_devices_) {
  38. // Don't rely on a legacy device ID if not using v1 DeviceSync, even
  39. // though we almost always expect one in practice.
  40. if (!features::ShouldUseV1DeviceSync())
  41. GetMutableRemoteDevice(device)->public_key.clear();
  42. }
  43. fake_host_backend_delegate_ = std::make_unique<FakeHostBackendDelegate>();
  44. fake_device_sync_client_ =
  45. std::make_unique<device_sync::FakeDeviceSyncClient>();
  46. fake_device_sync_client_->set_synced_devices(test_devices_);
  47. test_pref_service_ =
  48. std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
  49. GrandfatheredEasyUnlockHostDisabler::RegisterPrefs(
  50. test_pref_service_->registry());
  51. }
  52. void SetHost(const absl::optional<multidevice::RemoteDeviceRef>& host_device,
  53. multidevice::SoftwareFeature host_type) {
  54. if (host_type != multidevice::SoftwareFeature::kBetterTogetherHost &&
  55. host_type != multidevice::SoftwareFeature::kSmartLockHost)
  56. return;
  57. for (const auto& remote_device : test_devices_) {
  58. bool should_be_host =
  59. host_device != absl::nullopt &&
  60. host_device->GetDeviceId() == remote_device.GetDeviceId() &&
  61. host_device->instance_id() == remote_device.instance_id();
  62. GetMutableRemoteDevice(remote_device)->software_features[host_type] =
  63. should_be_host ? multidevice::SoftwareFeatureState::kEnabled
  64. : multidevice::SoftwareFeatureState::kSupported;
  65. }
  66. if (host_type == multidevice::SoftwareFeature::kBetterTogetherHost)
  67. fake_host_backend_delegate_->NotifyHostChangedOnBackend(host_device);
  68. }
  69. void InitializeTest(
  70. absl::optional<multidevice::RemoteDeviceRef> initial_device_in_prefs,
  71. absl::optional<multidevice::RemoteDeviceRef> initial_better_together_host,
  72. absl::optional<multidevice::RemoteDeviceRef> initial_easy_unlock_host) {
  73. test_pref_service_->SetString(kEasyUnlockHostIdToDisablePrefName,
  74. initial_device_in_prefs
  75. ? initial_device_in_prefs->GetDeviceId()
  76. : kNoDevice);
  77. test_pref_service_->SetString(kEasyUnlockHostInstanceIdToDisablePrefName,
  78. initial_device_in_prefs
  79. ? initial_device_in_prefs->instance_id()
  80. : kNoDevice);
  81. SetHost(initial_better_together_host,
  82. multidevice::SoftwareFeature::kBetterTogetherHost);
  83. SetHost(initial_easy_unlock_host,
  84. multidevice::SoftwareFeature::kSmartLockHost);
  85. auto mock_timer = std::make_unique<base::MockOneShotTimer>();
  86. mock_timer_ = mock_timer.get();
  87. grandfathered_easy_unlock_host_disabler_ =
  88. GrandfatheredEasyUnlockHostDisabler::Factory::Create(
  89. fake_host_backend_delegate_.get(), fake_device_sync_client_.get(),
  90. test_pref_service_.get(), std::move(mock_timer));
  91. }
  92. // Verify that the IDs for |expected_device| are stored in prefs. If
  93. // |expected_device| is null, prefs should have value |kNoDevice|.
  94. void VerifyDeviceInPrefs(
  95. const absl::optional<multidevice::RemoteDeviceRef>& expected_device) {
  96. if (!expected_device) {
  97. EXPECT_EQ(kNoDevice, test_pref_service_->GetString(
  98. kEasyUnlockHostIdToDisablePrefName));
  99. EXPECT_EQ(kNoDevice, test_pref_service_->GetString(
  100. kEasyUnlockHostInstanceIdToDisablePrefName));
  101. return;
  102. }
  103. EXPECT_EQ(
  104. expected_device->GetDeviceId().empty() ? kNoDevice
  105. : expected_device->GetDeviceId(),
  106. test_pref_service_->GetString(kEasyUnlockHostIdToDisablePrefName));
  107. EXPECT_EQ(expected_device->instance_id().empty()
  108. ? kNoDevice
  109. : expected_device->instance_id(),
  110. test_pref_service_->GetString(
  111. kEasyUnlockHostInstanceIdToDisablePrefName));
  112. }
  113. void VerifyEasyUnlockHostDisableRequest(
  114. int expected_queue_size,
  115. const absl::optional<multidevice::RemoteDeviceRef>& expected_host) {
  116. EXPECT_EQ(
  117. expected_queue_size,
  118. features::ShouldUseV1DeviceSync()
  119. ? fake_device_sync_client_
  120. ->GetSetSoftwareFeatureStateInputsQueueSize()
  121. : fake_device_sync_client_->GetSetFeatureStatusInputsQueueSize());
  122. if (expected_queue_size > 0) {
  123. ASSERT_TRUE(expected_host);
  124. VerifyLatestEasyUnlockHostDisableRequest(*expected_host);
  125. }
  126. }
  127. void InvokePendingEasyUnlockHostDisableRequestCallback(
  128. device_sync::mojom::NetworkRequestResult result_code) {
  129. if (features::ShouldUseV1DeviceSync()) {
  130. fake_device_sync_client_->InvokePendingSetSoftwareFeatureStateCallback(
  131. result_code);
  132. } else {
  133. fake_device_sync_client_->InvokePendingSetFeatureStatusCallback(
  134. result_code);
  135. }
  136. }
  137. const multidevice::RemoteDeviceRefList& test_devices() const {
  138. return test_devices_;
  139. }
  140. device_sync::FakeDeviceSyncClient* fake_device_sync_client() const {
  141. return fake_device_sync_client_.get();
  142. }
  143. base::MockOneShotTimer* mock_timer() const { return mock_timer_; }
  144. private:
  145. void VerifyLatestEasyUnlockHostDisableRequest(
  146. const multidevice::RemoteDeviceRef& expected_host) {
  147. // Verify inputs to SetSoftwareFeatureState().
  148. if (features::ShouldUseV1DeviceSync()) {
  149. ASSERT_FALSE(
  150. fake_device_sync_client_->set_software_feature_state_inputs_queue()
  151. .empty());
  152. const device_sync::FakeDeviceSyncClient::SetSoftwareFeatureStateInputs&
  153. inputs = fake_device_sync_client_
  154. ->set_software_feature_state_inputs_queue()
  155. .back();
  156. EXPECT_EQ(expected_host.public_key(), inputs.public_key);
  157. EXPECT_EQ(multidevice::SoftwareFeature::kSmartLockHost,
  158. inputs.software_feature);
  159. EXPECT_FALSE(inputs.enabled);
  160. EXPECT_FALSE(inputs.is_exclusive);
  161. return;
  162. }
  163. // Verify inputs to SetFeatureStatus().
  164. ASSERT_FALSE(
  165. fake_device_sync_client_->set_feature_status_inputs_queue().empty());
  166. const device_sync::FakeDeviceSyncClient::SetFeatureStatusInputs& inputs =
  167. fake_device_sync_client_->set_feature_status_inputs_queue().back();
  168. EXPECT_EQ(expected_host.instance_id(), inputs.device_instance_id);
  169. EXPECT_EQ(multidevice::SoftwareFeature::kSmartLockHost, inputs.feature);
  170. EXPECT_EQ(device_sync::FeatureStatusChange::kDisable, inputs.status_change);
  171. }
  172. multidevice::RemoteDeviceRefList test_devices_;
  173. std::unique_ptr<FakeHostBackendDelegate> fake_host_backend_delegate_;
  174. std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_;
  175. std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
  176. test_pref_service_;
  177. base::MockOneShotTimer* mock_timer_ = nullptr;
  178. std::unique_ptr<GrandfatheredEasyUnlockHostDisabler>
  179. grandfathered_easy_unlock_host_disabler_;
  180. };
  181. // Situation #1:
  182. // BTH = BETTER_TOGETHER_HOST, 0 = disabled, A = devices[0]
  183. // EUH = EASY_UNLOCK_HOST, 1 = enabled, B = devices[1]
  184. //
  185. // | A | B | | A | B |
  186. // ---+---+---+ ---+---+---+
  187. // BTH| 1 | 0 | BTH| 0 | 0 |
  188. // ---+---+---+ ---> ---+---+---+
  189. // EUH| 1 | 0 | EUH| 1 | 0 |
  190. //
  191. // Grandfathering prevents EUH from being disabled automatically. This class
  192. // disables EUH manually.
  193. TEST_F(
  194. MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
  195. IfBetterTogetherHostChangedFromOneDeviceToNoDeviceThenDisableEasyUnlock) {
  196. InitializeTest(absl::nullopt /* initial_device_in_prefs */,
  197. test_devices()[0] /* initial_better_together_host */,
  198. test_devices()[0] /* initial_easy_unlock_host */);
  199. SetHost(absl::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
  200. VerifyDeviceInPrefs(test_devices()[0]);
  201. VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
  202. test_devices()[0]);
  203. InvokePendingEasyUnlockHostDisableRequestCallback(
  204. device_sync::mojom::NetworkRequestResult::kSuccess);
  205. VerifyDeviceInPrefs(absl::nullopt /* expected_device */);
  206. EXPECT_FALSE(mock_timer()->IsRunning());
  207. }
  208. // Situation #2:
  209. // BTH = BETTER_TOGETHER_HOST, 0 = disabled, A = devices[0]
  210. // EUH = EASY_UNLOCK_HOST, 1 = enabled, B = devices[1]
  211. //
  212. // | A | B | | A | B |
  213. // ---+---+---+ ---+---+---+
  214. // BTH| 0 | 0 | BTH| 0 | 1 |
  215. // ---+---+---+ ---> ---+---+---+
  216. // EUH| 1 | 0 | EUH| 0 | 1 |
  217. //
  218. // The CryptAuth backend (via GmsCore) disables EUH on device A when BTH is
  219. // enabled (exclusively) on another device, B. No action necessary from this
  220. // class.
  221. TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
  222. IfBetterTogetherHostChangedFromNoDeviceToADeviceThenDoNothing) {
  223. InitializeTest(absl::nullopt /* initial_device_in_prefs */,
  224. absl::nullopt /* initial_better_together_host */,
  225. test_devices()[0] /* initial_easy_unlock_host */);
  226. SetHost(test_devices()[1], multidevice::SoftwareFeature::kBetterTogetherHost);
  227. VerifyDeviceInPrefs(absl::nullopt /* expected_device */);
  228. VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
  229. absl::nullopt /* expected_host */);
  230. }
  231. // Situation #3:
  232. // BTH = BETTER_TOGETHER_HOST, 0 = disabled, A = devices[0]
  233. // EUH = EASY_UNLOCK_HOST, 1 = enabled, B = devices[1]
  234. //
  235. // | A | B | | A | B |
  236. // ---+---+---+ ---+---+---+
  237. // BTH| 1 | 0 | BTH| 0 | 1 |
  238. // ---+---+---+ ---> ---+---+---+
  239. // EUH| 1 | 0 | EUH| 0 | 1 |
  240. //
  241. // The CryptAuth backend (via GmsCore) disables EUH on device A when BTH is
  242. // enabled (exclusively) on another device, B. We still attempt to disable
  243. // EUH in this case to be safe.
  244. TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
  245. IfBetterTogetherHostChangedFromOneDeviceToAnotherThenDisableEasyUnlock) {
  246. InitializeTest(absl::nullopt /* initial_device_in_prefs */,
  247. test_devices()[0] /* initial_better_together_host */,
  248. test_devices()[0] /* initial_easy_unlock_host */);
  249. SetHost(test_devices()[1], multidevice::SoftwareFeature::kBetterTogetherHost);
  250. VerifyDeviceInPrefs(test_devices()[0]);
  251. VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
  252. test_devices()[0]);
  253. InvokePendingEasyUnlockHostDisableRequestCallback(
  254. device_sync::mojom::NetworkRequestResult::kSuccess);
  255. VerifyDeviceInPrefs(absl::nullopt /* expected_device */);
  256. EXPECT_FALSE(mock_timer()->IsRunning());
  257. }
  258. TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
  259. IfDisablePendingThenConstructorAttemptsToDisableEasyUnlock) {
  260. InitializeTest(test_devices()[0] /* initial_device_in_prefs */,
  261. absl::nullopt /* initial_better_together_host */,
  262. test_devices()[0] /* initial_easy_unlock_host */);
  263. VerifyDeviceInPrefs(test_devices()[0]);
  264. VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
  265. test_devices()[0]);
  266. InvokePendingEasyUnlockHostDisableRequestCallback(
  267. device_sync::mojom::NetworkRequestResult::kSuccess);
  268. }
  269. // Situation #1 where device A is removed from list of synced devices:
  270. //
  271. // | A | B | | A | B |
  272. // ---+---+---+ ---+---+---+
  273. // BTH| 1 | 0 | BTH| 0 | 0 |
  274. // ---+---+---+ ---> ---+---+---+
  275. // EUH| 1 | 0 | EUH| 1 | 0 |
  276. TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
  277. IfHostToDisableIsNotInListOfSyncedDevicesThenClearPref) {
  278. InitializeTest(absl::nullopt /* initial_device_in_prefs */,
  279. test_devices()[0] /* initial_better_together_host */,
  280. test_devices()[0] /* initial_easy_unlock_host */);
  281. // Remove device[0] from list
  282. fake_device_sync_client()->set_synced_devices({test_devices()[1]});
  283. SetHost(absl::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
  284. VerifyDeviceInPrefs(absl::nullopt /* expected_device */);
  285. VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
  286. absl::nullopt /* expected_host */);
  287. }
  288. // Situation #1 with failure:
  289. //
  290. // | A | B | | A | B |
  291. // ---+---+---+ ---+---+---+
  292. // BTH| 1 | 0 | BTH| 0 | 0 |
  293. // ---+---+---+ ---> ---+---+---+
  294. // EUH| 1 | 0 | EUH| 1 | 0 |
  295. TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
  296. IfEasyUnlockDisableUnsuccessfulThenScheduleRetry) {
  297. InitializeTest(absl::nullopt /* initial_device_in_prefs */,
  298. test_devices()[0] /* initial_better_together_host */,
  299. test_devices()[0] /* initial_easy_unlock_host */);
  300. SetHost(absl::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
  301. VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
  302. test_devices()[0]);
  303. InvokePendingEasyUnlockHostDisableRequestCallback(
  304. device_sync::mojom::NetworkRequestResult::kInternalServerError);
  305. VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
  306. absl::nullopt /* expected_host */);
  307. VerifyDeviceInPrefs(test_devices()[0]);
  308. EXPECT_TRUE(mock_timer()->IsRunning());
  309. mock_timer()->Fire();
  310. VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
  311. test_devices()[0]);
  312. }
  313. TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
  314. IfNoDisablePendingThenConstructorDoesNothing) {
  315. InitializeTest(absl::nullopt /* initial_device_in_prefs */,
  316. absl::nullopt /* initial_better_together_host */,
  317. test_devices()[0] /* initial_easy_unlock_host */);
  318. VerifyDeviceInPrefs(absl::nullopt /* expected_device */);
  319. VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
  320. absl::nullopt /* expected_host */);
  321. }
  322. TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
  323. IfDisablePendingButIsNotCurrentEasyUnlockHostThenClearPref) {
  324. InitializeTest(test_devices()[0] /* initial_device_in_prefs */,
  325. test_devices()[1] /* initial_better_together_host */,
  326. test_devices()[1] /* initial_easy_unlock_host */);
  327. VerifyDeviceInPrefs(absl::nullopt /* expected_device */);
  328. VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
  329. absl::nullopt /* expected_host */);
  330. }
  331. TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
  332. IfDisablePendingButIsCurrentBetterTogetherHostThenClearPref) {
  333. InitializeTest(test_devices()[0] /* initial_device_in_prefs */,
  334. test_devices()[0] /* initial_better_together_host */,
  335. test_devices()[0] /* initial_easy_unlock_host */);
  336. VerifyDeviceInPrefs(absl::nullopt /* expected_device */);
  337. VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
  338. absl::nullopt /* expected_host */);
  339. }
  340. // Simulate:
  341. // - Disable BETTER_TOGETHER_HOST on device 0
  342. // - GrandfatheredEasyUnlockHostDisabler tries to disable EASY_UNLOCK_HOST on
  343. // device 0 but fails
  344. // - Timer is running while we wait to retry
  345. // - Re-enable BETTER_TOGETHER_HOST on device 0
  346. TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
  347. IfHostChangesWhileRetryTimerIsRunningThenCancelTimerAndClearPref) {
  348. InitializeTest(absl::nullopt /* initial_device_in_prefs */,
  349. test_devices()[0] /* initial_better_together_host */,
  350. test_devices()[0] /* initial_easy_unlock_host */);
  351. SetHost(absl::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
  352. VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
  353. test_devices()[0]);
  354. InvokePendingEasyUnlockHostDisableRequestCallback(
  355. device_sync::mojom::NetworkRequestResult::kInternalServerError);
  356. EXPECT_TRUE(mock_timer()->IsRunning());
  357. SetHost(test_devices()[0], multidevice::SoftwareFeature::kBetterTogetherHost);
  358. VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
  359. absl::nullopt /* expected_host */);
  360. EXPECT_FALSE(mock_timer()->IsRunning());
  361. VerifyDeviceInPrefs(absl::nullopt /* expected_device */);
  362. }
  363. // Simulate:
  364. // - Set device 0 as host
  365. // - Disable host
  366. // - Set device 1 as host
  367. // - Disable host
  368. // - SetSoftwareFeatureState callback for device 0 is called
  369. TEST_F(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
  370. IfDifferentHostDisabledBeforeFirstCallbackThenFirstCallbackDoesNothing) {
  371. InitializeTest(absl::nullopt /* initial_device_in_prefs */,
  372. test_devices()[0] /* initial_better_together_host */,
  373. test_devices()[0] /* initial_easy_unlock_host */);
  374. SetHost(absl::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
  375. VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
  376. test_devices()[0]);
  377. SetHost(test_devices()[1], multidevice::SoftwareFeature::kBetterTogetherHost);
  378. SetHost(test_devices()[1], multidevice::SoftwareFeature::kSmartLockHost);
  379. SetHost(absl::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
  380. VerifyEasyUnlockHostDisableRequest(2 /* expected_queue_size */,
  381. test_devices()[1]);
  382. VerifyDeviceInPrefs(test_devices()[1]);
  383. InvokePendingEasyUnlockHostDisableRequestCallback(
  384. device_sync::mojom::NetworkRequestResult::kSuccess);
  385. VerifyDeviceInPrefs(test_devices()[1]);
  386. }
  387. } // namespace multidevice_setup
  388. } // namespace ash