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 "chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h"
6 
7 #include <memory>
8 
9 #include "base/macros.h"
10 #include "chrome/browser/chromeos/android_sms/android_sms_urls.h"
11 #include "chrome/browser/chromeos/android_sms/fake_android_sms_app_manager.h"
12 #include "chromeos/components/multidevice/remote_device_test_util.h"
13 #include "chromeos/services/multidevice_setup/public/cpp/fake_android_sms_pairing_state_tracker.h"
14 #include "chromeos/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
15 #include "components/content_settings/core/common/content_settings_pattern.h"
16 #include "components/prefs/testing_pref_service.h"
17 #include "content/public/test/test_web_ui.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 
20 namespace chromeos {
21 
22 namespace settings {
23 
24 namespace {
25 
26 class TestMultideviceHandler : public MultideviceHandler {
27  public:
TestMultideviceHandler(PrefService * prefs,multidevice_setup::MultiDeviceSetupClient * multidevice_setup_client,multidevice_setup::AndroidSmsPairingStateTracker * android_sms_pairing_state_tracker,android_sms::AndroidSmsAppManager * android_sms_app_manager)28   TestMultideviceHandler(
29       PrefService* prefs,
30       multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
31       multidevice_setup::AndroidSmsPairingStateTracker*
32           android_sms_pairing_state_tracker,
33       android_sms::AndroidSmsAppManager* android_sms_app_manager)
34       : MultideviceHandler(prefs,
35                            multidevice_setup_client,
36                            android_sms_pairing_state_tracker,
37                            android_sms_app_manager) {}
38   ~TestMultideviceHandler() override = default;
39 
40   // Make public for testing.
41   using MultideviceHandler::AllowJavascript;
42   using MultideviceHandler::RegisterMessages;
43   using MultideviceHandler::set_web_ui;
44 };
45 
46 multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap
GenerateDefaultFeatureStatesMap()47 GenerateDefaultFeatureStatesMap() {
48   return multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap{
49       {multidevice_setup::mojom::Feature::kBetterTogetherSuite,
50        multidevice_setup::mojom::FeatureState::kUnavailableNoVerifiedHost},
51       {multidevice_setup::mojom::Feature::kInstantTethering,
52        multidevice_setup::mojom::FeatureState::kUnavailableNoVerifiedHost},
53       {multidevice_setup::mojom::Feature::kMessages,
54        multidevice_setup::mojom::FeatureState::kUnavailableNoVerifiedHost},
55       {multidevice_setup::mojom::Feature::kSmartLock,
56        multidevice_setup::mojom::FeatureState::kUnavailableNoVerifiedHost}};
57 }
58 
VerifyPageContentDict(const base::Value * value,multidevice_setup::mojom::HostStatus expected_host_status,const base::Optional<multidevice::RemoteDeviceRef> & expected_host_device,const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap & feature_states_map)59 void VerifyPageContentDict(
60     const base::Value* value,
61     multidevice_setup::mojom::HostStatus expected_host_status,
62     const base::Optional<multidevice::RemoteDeviceRef>& expected_host_device,
63     const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
64         feature_states_map) {
65   const base::DictionaryValue* page_content_dict;
66   EXPECT_TRUE(value->GetAsDictionary(&page_content_dict));
67 
68   int mode;
69   EXPECT_TRUE(page_content_dict->GetInteger("mode", &mode));
70   EXPECT_EQ(static_cast<int>(expected_host_status), mode);
71 
72   int better_together_state;
73   EXPECT_TRUE(page_content_dict->GetInteger("betterTogetherState",
74                                             &better_together_state));
75   auto it = feature_states_map.find(
76       multidevice_setup::mojom::Feature::kBetterTogetherSuite);
77   EXPECT_EQ(static_cast<int>(it->second), better_together_state);
78 
79   int instant_tethering_state;
80   EXPECT_TRUE(page_content_dict->GetInteger("instantTetheringState",
81                                             &instant_tethering_state));
82   it = feature_states_map.find(
83       multidevice_setup::mojom::Feature::kInstantTethering);
84   EXPECT_EQ(static_cast<int>(it->second), instant_tethering_state);
85 
86   int messages_state;
87   EXPECT_TRUE(page_content_dict->GetInteger("messagesState", &messages_state));
88   it = feature_states_map.find(multidevice_setup::mojom::Feature::kMessages);
89   EXPECT_EQ(static_cast<int>(it->second), messages_state);
90 
91   int smart_lock_state;
92   EXPECT_TRUE(
93       page_content_dict->GetInteger("smartLockState", &smart_lock_state));
94   it = feature_states_map.find(multidevice_setup::mojom::Feature::kSmartLock);
95   EXPECT_EQ(static_cast<int>(it->second), smart_lock_state);
96 
97   std::string host_device_name;
98   if (expected_host_device) {
99     EXPECT_TRUE(
100         page_content_dict->GetString("hostDeviceName", &host_device_name));
101     EXPECT_EQ(expected_host_device->name(), host_device_name);
102   } else {
103     EXPECT_FALSE(
104         page_content_dict->GetString("hostDeviceName", &host_device_name));
105   }
106 }
107 
108 }  // namespace
109 
110 class MultideviceHandlerTest : public testing::Test {
111  protected:
MultideviceHandlerTest()112   MultideviceHandlerTest()
113       : test_device_(multidevice::CreateRemoteDeviceRefForTest()) {}
114   ~MultideviceHandlerTest() override = default;
115 
116   // testing::Test:
SetUp()117   void SetUp() override {
118     test_web_ui_ = std::make_unique<content::TestWebUI>();
119 
120     fake_multidevice_setup_client_ =
121         std::make_unique<multidevice_setup::FakeMultiDeviceSetupClient>();
122     fake_android_sms_pairing_state_tracker_ = std::make_unique<
123         multidevice_setup::FakeAndroidSmsPairingStateTracker>();
124     fake_android_sms_app_manager_ =
125         std::make_unique<android_sms::FakeAndroidSmsAppManager>();
126 
127     prefs_ = std::make_unique<TestingPrefServiceSimple>();
128 
129     handler_ = std::make_unique<TestMultideviceHandler>(
130         prefs_.get(), fake_multidevice_setup_client_.get(),
131         fake_android_sms_pairing_state_tracker_.get(),
132         fake_android_sms_app_manager_.get());
133     handler_->set_web_ui(test_web_ui_.get());
134     handler_->RegisterMessages();
135     handler_->AllowJavascript();
136   }
137 
CallGetPageContentData()138   void CallGetPageContentData() {
139     size_t call_data_count_before_call = test_web_ui()->call_data().size();
140 
141     base::ListValue args;
142     args.AppendString("handlerFunctionName");
143     test_web_ui()->HandleReceivedMessage("getPageContentData", &args);
144 
145     EXPECT_EQ(call_data_count_before_call + 1u,
146               test_web_ui()->call_data().size());
147 
148     const content::TestWebUI::CallData& call_data =
149         CallDataAtIndex(call_data_count_before_call);
150     EXPECT_EQ("cr.webUIResponse", call_data.function_name());
151     EXPECT_EQ("handlerFunctionName", call_data.arg1()->GetString());
152     EXPECT_TRUE(call_data.arg2()->GetBool());
153     VerifyPageContent(call_data.arg3());
154   }
155 
CallRemoveHostDevice()156   void CallRemoveHostDevice() {
157     size_t num_remote_host_device_calls_before_call =
158         fake_multidevice_setup_client()->num_remove_host_device_called();
159     base::ListValue empty_args;
160     test_web_ui()->HandleReceivedMessage("removeHostDevice", &empty_args);
161     EXPECT_EQ(num_remote_host_device_calls_before_call + 1u,
162               fake_multidevice_setup_client()->num_remove_host_device_called());
163   }
164 
CallGetAndroidSmsInfo(bool expected_enabled,const GURL & expected_url)165   void CallGetAndroidSmsInfo(bool expected_enabled, const GURL& expected_url) {
166     size_t call_data_count_before_call = test_web_ui()->call_data().size();
167 
168     base::ListValue args;
169     args.AppendString("handlerFunctionName");
170     test_web_ui()->HandleReceivedMessage("getAndroidSmsInfo", &args);
171 
172     ASSERT_EQ(call_data_count_before_call + 1u,
173               test_web_ui()->call_data().size());
174     const content::TestWebUI::CallData& call_data =
175         CallDataAtIndex(call_data_count_before_call);
176     EXPECT_EQ("cr.webUIResponse", call_data.function_name());
177     EXPECT_EQ("handlerFunctionName", call_data.arg1()->GetString());
178     ASSERT_TRUE(call_data.arg2()->GetBool());
179     EXPECT_EQ(
180         ContentSettingsPattern::FromURLNoWildcard(expected_url).ToString(),
181         call_data.arg3()->FindKey("origin")->GetString());
182     EXPECT_EQ(expected_enabled,
183               call_data.arg3()->FindKey("enabled")->GetBool());
184   }
185 
SimulateHostStatusUpdate(multidevice_setup::mojom::HostStatus host_status,const base::Optional<multidevice::RemoteDeviceRef> & host_device)186   void SimulateHostStatusUpdate(
187       multidevice_setup::mojom::HostStatus host_status,
188       const base::Optional<multidevice::RemoteDeviceRef>& host_device) {
189     size_t call_data_count_before_call = test_web_ui()->call_data().size();
190 
191     fake_multidevice_setup_client_->SetHostStatusWithDevice(
192         std::make_pair(host_status, host_device));
193     EXPECT_EQ(call_data_count_before_call + 2u,
194               test_web_ui()->call_data().size());
195 
196     const content::TestWebUI::CallData& call_data =
197         CallDataAtIndex(call_data_count_before_call);
198     EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
199     EXPECT_EQ("settings.updateMultidevicePageContentData",
200               call_data.arg1()->GetString());
201     VerifyPageContent(call_data.arg2());
202   }
203 
SimulateFeatureStatesUpdate(const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap & feature_states_map)204   void SimulateFeatureStatesUpdate(
205       const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
206           feature_states_map) {
207     size_t call_data_count_before_call = test_web_ui()->call_data().size();
208 
209     fake_multidevice_setup_client_->SetFeatureStates(feature_states_map);
210     EXPECT_EQ(call_data_count_before_call + 2u,
211               test_web_ui()->call_data().size());
212 
213     const content::TestWebUI::CallData& call_data =
214         CallDataAtIndex(call_data_count_before_call);
215     EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
216     EXPECT_EQ("settings.updateMultidevicePageContentData",
217               call_data.arg1()->GetString());
218     VerifyPageContent(call_data.arg2());
219   }
220 
SimulatePairingStateUpdate(bool is_android_sms_pairing_complete)221   void SimulatePairingStateUpdate(bool is_android_sms_pairing_complete) {
222     size_t call_data_count_before_call = test_web_ui()->call_data().size();
223 
224     fake_android_sms_pairing_state_tracker_->SetPairingComplete(
225         is_android_sms_pairing_complete);
226     EXPECT_EQ(call_data_count_before_call + 2u,
227               test_web_ui()->call_data().size());
228 
229     const content::TestWebUI::CallData& call_data =
230         CallDataAtIndex(call_data_count_before_call);
231     EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
232     EXPECT_EQ("settings.updateMultidevicePageContentData",
233               call_data.arg1()->GetString());
234     VerifyPageContent(call_data.arg2());
235   }
236 
CallRetryPendingHostSetup(bool success)237   void CallRetryPendingHostSetup(bool success) {
238     base::ListValue empty_args;
239     test_web_ui()->HandleReceivedMessage("retryPendingHostSetup", &empty_args);
240     fake_multidevice_setup_client()->InvokePendingRetrySetHostNowCallback(
241         success);
242   }
243 
CallSetUpAndroidSms()244   void CallSetUpAndroidSms() {
245     base::ListValue empty_args;
246     test_web_ui()->HandleReceivedMessage("setUpAndroidSms", &empty_args);
247   }
248 
CallSetFeatureEnabledState(multidevice_setup::mojom::Feature feature,bool enabled,const base::Optional<std::string> & auth_token,bool success)249   void CallSetFeatureEnabledState(multidevice_setup::mojom::Feature feature,
250                                   bool enabled,
251                                   const base::Optional<std::string>& auth_token,
252                                   bool success) {
253     size_t call_data_count_before_call = test_web_ui()->call_data().size();
254 
255     base::ListValue args;
256     args.AppendString("handlerFunctionName");
257     args.AppendInteger(static_cast<int>(feature));
258     args.AppendBoolean(enabled);
259     if (auth_token)
260       args.AppendString(*auth_token);
261 
262     base::ListValue empty_args;
263     test_web_ui()->HandleReceivedMessage("setFeatureEnabledState", &args);
264     fake_multidevice_setup_client()
265         ->InvokePendingSetFeatureEnabledStateCallback(
266             feature /* expected_feature */, enabled /* expected_enabled */,
267             auth_token /* expected_auth_token */, success);
268 
269     EXPECT_EQ(call_data_count_before_call + 1u,
270               test_web_ui()->call_data().size());
271     const content::TestWebUI::CallData& call_data =
272         CallDataAtIndex(call_data_count_before_call);
273     EXPECT_EQ("cr.webUIResponse", call_data.function_name());
274     EXPECT_EQ("handlerFunctionName", call_data.arg1()->GetString());
275     EXPECT_TRUE(call_data.arg2()->GetBool());
276     EXPECT_EQ(success, call_data.arg3()->GetBool());
277   }
278 
CallDataAtIndex(size_t index)279   const content::TestWebUI::CallData& CallDataAtIndex(size_t index) {
280     return *test_web_ui_->call_data()[index];
281   }
282 
test_web_ui()283   content::TestWebUI* test_web_ui() { return test_web_ui_.get(); }
284 
285   multidevice_setup::FakeMultiDeviceSetupClient*
fake_multidevice_setup_client()286   fake_multidevice_setup_client() {
287     return fake_multidevice_setup_client_.get();
288   }
289 
fake_android_sms_app_manager()290   android_sms::FakeAndroidSmsAppManager* fake_android_sms_app_manager() {
291     return fake_android_sms_app_manager_.get();
292   }
293 
294   const multidevice::RemoteDeviceRef test_device_;
295 
296  private:
VerifyPageContent(const base::Value * value)297   void VerifyPageContent(const base::Value* value) {
298     VerifyPageContentDict(
299         value, fake_multidevice_setup_client_->GetHostStatus().first,
300         fake_multidevice_setup_client_->GetHostStatus().second,
301         fake_multidevice_setup_client_->GetFeatureStates());
302   }
303 
304   std::unique_ptr<TestingPrefServiceSimple> prefs_;
305   std::unique_ptr<content::TestWebUI> test_web_ui_;
306   std::unique_ptr<multidevice_setup::FakeMultiDeviceSetupClient>
307       fake_multidevice_setup_client_;
308   std::unique_ptr<multidevice_setup::FakeAndroidSmsPairingStateTracker>
309       fake_android_sms_pairing_state_tracker_;
310 
311   multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice
312       host_status_with_device_;
313   multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap
314       feature_states_map_;
315   std::unique_ptr<android_sms::FakeAndroidSmsAppManager>
316       fake_android_sms_app_manager_;
317 
318   std::unique_ptr<TestMultideviceHandler> handler_;
319 
320   DISALLOW_COPY_AND_ASSIGN(MultideviceHandlerTest);
321 };
322 
TEST_F(MultideviceHandlerTest,PageContentData)323 TEST_F(MultideviceHandlerTest, PageContentData) {
324   CallGetPageContentData();
325   CallGetPageContentData();
326 
327   SimulateHostStatusUpdate(
328       multidevice_setup::mojom::HostStatus::kEligibleHostExistsButNoHostSet,
329       base::nullopt /* host_device */);
330   SimulateHostStatusUpdate(multidevice_setup::mojom::HostStatus::
331                                kHostSetLocallyButWaitingForBackendConfirmation,
332                            test_device_);
333   SimulateHostStatusUpdate(
334       multidevice_setup::mojom::HostStatus::kHostSetButNotYetVerified,
335       test_device_);
336   SimulateHostStatusUpdate(multidevice_setup::mojom::HostStatus::kHostVerified,
337                            test_device_);
338 
339   multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap
340       feature_states_map = GenerateDefaultFeatureStatesMap();
341   feature_states_map[multidevice_setup::mojom::Feature::kBetterTogetherSuite] =
342       multidevice_setup::mojom::FeatureState::kEnabledByUser;
343   SimulateFeatureStatesUpdate(feature_states_map);
344 
345   feature_states_map[multidevice_setup::mojom::Feature::kBetterTogetherSuite] =
346       multidevice_setup::mojom::FeatureState::kDisabledByUser;
347   SimulateFeatureStatesUpdate(feature_states_map);
348 
349   SimulatePairingStateUpdate(/*is_android_sms_pairing_complete=*/true);
350 }
351 
TEST_F(MultideviceHandlerTest,RetryPendingHostSetup)352 TEST_F(MultideviceHandlerTest, RetryPendingHostSetup) {
353   CallRetryPendingHostSetup(true /* success */);
354   CallRetryPendingHostSetup(false /* success */);
355 }
356 
TEST_F(MultideviceHandlerTest,SetUpAndroidSms)357 TEST_F(MultideviceHandlerTest, SetUpAndroidSms) {
358   EXPECT_FALSE(fake_android_sms_app_manager()->has_installed_app());
359   EXPECT_FALSE(fake_android_sms_app_manager()->has_launched_app());
360   CallSetUpAndroidSms();
361   EXPECT_TRUE(fake_android_sms_app_manager()->has_installed_app());
362   EXPECT_TRUE(fake_android_sms_app_manager()->has_launched_app());
363 }
364 
TEST_F(MultideviceHandlerTest,SetFeatureEnabledState)365 TEST_F(MultideviceHandlerTest, SetFeatureEnabledState) {
366   CallSetFeatureEnabledState(
367       multidevice_setup::mojom::Feature::kBetterTogetherSuite,
368       true /* enabled */, "authToken" /* auth_token */, true /* success */);
369   CallSetFeatureEnabledState(
370       multidevice_setup::mojom::Feature::kBetterTogetherSuite,
371       false /* enabled */, "authToken" /* auth_token */, false /* success */);
372   CallSetFeatureEnabledState(
373       multidevice_setup::mojom::Feature::kBetterTogetherSuite,
374       false /* enabled */, "authToken" /* auth_token */, true /* success */);
375 }
376 
TEST_F(MultideviceHandlerTest,RemoveHostDevice)377 TEST_F(MultideviceHandlerTest, RemoveHostDevice) {
378   CallRemoveHostDevice();
379   CallRemoveHostDevice();
380   CallRemoveHostDevice();
381 }
382 
TEST_F(MultideviceHandlerTest,GetAndroidSmsInfo)383 TEST_F(MultideviceHandlerTest, GetAndroidSmsInfo) {
384   // Check that getAndroidSmsInfo returns correct value.
385   CallGetAndroidSmsInfo(false /* expected_enabled */,
386                         android_sms::GetAndroidMessagesURL(
387                             true /* use_install_url */) /* expected_url */);
388 
389   // Change messages feature state and assert that the change
390   // callback is fired.
391   multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap
392       feature_states_map = GenerateDefaultFeatureStatesMap();
393   feature_states_map[multidevice_setup::mojom::Feature::kMessages] =
394       multidevice_setup::mojom::FeatureState::kEnabledByUser;
395 
396   size_t call_data_count_before_call = test_web_ui()->call_data().size();
397   SimulateFeatureStatesUpdate(feature_states_map);
398   const content::TestWebUI::CallData& call_data_1 =
399       CallDataAtIndex(call_data_count_before_call + 1);
400   EXPECT_EQ("cr.webUIListenerCallback", call_data_1.function_name());
401   EXPECT_EQ("settings.onAndroidSmsInfoChange", call_data_1.arg1()->GetString());
402 
403   // Check that getAndroidSmsInfo returns update value.
404   CallGetAndroidSmsInfo(true /* enabled */, android_sms::GetAndroidMessagesURL(
405                                                 true) /* expected_url */);
406 
407   // Now, update the installed URL. This should have resulted in another call.
408   fake_android_sms_app_manager()->SetInstalledAppUrl(
409       android_sms::GetAndroidMessagesURL(true /* use_install_url */,
410                                          android_sms::PwaDomain::kStaging));
411   const content::TestWebUI::CallData& call_data_2 =
412       CallDataAtIndex(call_data_count_before_call + 4);
413   EXPECT_EQ("cr.webUIListenerCallback", call_data_2.function_name());
414   EXPECT_EQ("settings.onAndroidSmsInfoChange", call_data_2.arg1()->GetString());
415   CallGetAndroidSmsInfo(
416       true /* enabled */,
417       android_sms::GetAndroidMessagesURL(
418           true /* use_install_url */,
419           android_sms::PwaDomain::kStaging) /* expected_url */);
420 }
421 
422 }  // namespace settings
423 
424 }  // namespace chromeos
425