1 // Copyright (c) 2015 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/chromeos/policy/heartbeat_scheduler.h"
6 
7 #include <stdint.h>
8 
9 #include <vector>
10 
11 #include "base/macros.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/test/gmock_move_support.h"
14 #include "base/test/metrics/histogram_tester.h"
15 #include "base/test/test_simple_task_runner.h"
16 #include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
17 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
18 #include "chromeos/settings/cros_settings_names.h"
19 #include "components/gcm_driver/common/gcm_message.h"
20 #include "components/gcm_driver/fake_gcm_driver.h"
21 #include "components/policy/core/common/cloud/cloud_policy_client.h"
22 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
23 #include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
24 #include "content/public/test/browser_task_environment.h"
25 #include "content/public/test/test_utils.h"
26 #include "net/base/ip_endpoint.h"
27 #include "testing/gmock/include/gmock/gmock.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 
30 using ::testing::_;
31 using ::testing::AnyNumber;
32 using ::testing::AtLeast;
33 using ::testing::Contains;
34 using ::testing::Field;
35 using ::testing::Key;
36 using ::testing::Matches;
37 using ::testing::Pair;
38 using ::testing::SaveArg;
39 
40 namespace {
41 const char* const kFakeCustomerId = "fake_customer_id";
42 const char* const kFakeDeviceId = "fake_device_id";
43 const char* const kHeartbeatGCMAppID = "com.google.chromeos.monitoring";
44 const char* const kRegistrationId = "registration_id";
45 const char* const kDMToken = "fake_dm_token";
46 
47 MATCHER(IsHeartbeatMsg, "") {
48   return Matches(
49       Field(&gcm::OutgoingMessage::data, Contains(Pair("type", "hb"))))(arg);
50 }
51 
52 MATCHER(IsUpstreamNotificationMsg, "") {
53   return Matches(Field(&gcm::OutgoingMessage::data, Contains(Key("notify"))))(
54       arg);
55 }
56 
57 class MockGCMDriver : public testing::StrictMock<gcm::FakeGCMDriver> {
58  public:
MockGCMDriver()59   MockGCMDriver() { IgnoreDefaultHeartbeatsInterval(); }
60 
~MockGCMDriver()61   ~MockGCMDriver() override {
62   }
63 
64   MOCK_METHOD2(RegisterImpl,
65                void(const std::string&, const std::vector<std::string>&));
66   MOCK_METHOD3(SendImpl,
67                void(const std::string&,
68                     const std::string&,
69                     const gcm::OutgoingMessage& message));
70 
71   MOCK_METHOD2(AddHeartbeatInterval,
72                void(const std::string& scope, int interval_ms));
73 
74   // Helper function to complete a registration previously started by
75   // Register().
CompleteRegistration(const std::string & app_id,gcm::GCMClient::Result result)76   void CompleteRegistration(const std::string& app_id,
77                             gcm::GCMClient::Result result) {
78     RegisterFinished(app_id, kRegistrationId, result);
79   }
80 
81   // Helper function to complete a send operation previously started by
82   // Send().
CompleteSend(const std::string & app_id,const std::string & message_id,gcm::GCMClient::Result result)83   void CompleteSend(const std::string& app_id,
84                     const std::string& message_id,
85                     gcm::GCMClient::Result result) {
86     SendFinished(app_id, message_id, result);
87   }
88 
AddConnectionObserver(gcm::GCMConnectionObserver * observer)89   void AddConnectionObserver(gcm::GCMConnectionObserver* observer) override {
90     EXPECT_FALSE(observer_);
91     observer_ = observer;
92   }
93 
RemoveConnectionObserver(gcm::GCMConnectionObserver * observer)94   void RemoveConnectionObserver(gcm::GCMConnectionObserver* observer) override {
95     EXPECT_TRUE(observer_);
96     observer_ = nullptr;
97   }
98 
NotifyConnected()99   void NotifyConnected() {
100     ASSERT_TRUE(observer_);
101     observer_->OnConnected(net::IPEndPoint());
102   }
103 
IgnoreDefaultHeartbeatsInterval()104   void IgnoreDefaultHeartbeatsInterval() {
105     // Ignore default setting for heartbeats interval.
106     EXPECT_CALL(*this, AddHeartbeatInterval(_, 2 * 60 * 1000))
107         .Times(AnyNumber());
108   }
109 
ResetStore()110   void ResetStore() {
111     // Defensive copy in case OnStoreReset calls Add/RemoveAppHandler.
112     std::vector<gcm::GCMAppHandler*> app_handler_values;
113     for (const auto& key_value : app_handlers())
114       app_handler_values.push_back(key_value.second);
115     for (gcm::GCMAppHandler* app_handler : app_handler_values) {
116       app_handler->OnStoreReset();
117       // app_handler might now have been deleted.
118     }
119   }
120 
121  private:
122   gcm::GCMConnectionObserver* observer_ = nullptr;
123 
124   DISALLOW_COPY_AND_ASSIGN(MockGCMDriver);
125 };
126 
127 class HeartbeatSchedulerTest : public testing::Test {
128  public:
HeartbeatSchedulerTest()129   HeartbeatSchedulerTest()
130       : task_runner_(new base::TestSimpleTaskRunner()),
131         scheduler_(&gcm_driver_,
132                    &cloud_policy_client_,
133                    &cloud_policy_store_,
134                    kFakeDeviceId,
135                    task_runner_) {}
136 
SetUp()137   void SetUp() {
138     cloud_policy_store_.InitPolicyData();
139     cloud_policy_store_.GetMutablePolicyData()->set_obfuscated_customer_id(
140         kFakeCustomerId);
141   }
142 
TearDown()143   void TearDown() override { content::RunAllTasksUntilIdle(); }
144 
CheckPendingTaskDelay(base::Time last_heartbeat,base::TimeDelta expected_delay)145   void CheckPendingTaskDelay(base::Time last_heartbeat,
146                              base::TimeDelta expected_delay) {
147     EXPECT_FALSE(last_heartbeat.is_null());
148     base::Time now = base::Time::NowFromSystemTime();
149     EXPECT_GE(now, last_heartbeat);
150     base::TimeDelta actual_delay = task_runner_->NextPendingTaskDelay();
151 
152     // NextPendingTaskDelay() returns the exact original delay value the task
153     // was posted with. The heartbeat task would have been calculated to fire at
154     // |last_heartbeat| + |expected_delay|, but we don't know the exact time
155     // when the task was posted (if it was a couple of milliseconds after
156     // |last_heartbeat|, then |actual_delay| would be a couple of milliseconds
157     // smaller than |expected_delay|.
158     //
159     // We do know that the task was posted sometime between |last_heartbeat|
160     // and |now|, so we know that 0 <= |expected_delay| - |actual_delay| <=
161     // |now| - |last_heartbeat|.
162     base::TimeDelta delta = expected_delay - actual_delay;
163     EXPECT_LE(base::TimeDelta(), delta);
164     EXPECT_GE(now - last_heartbeat, delta);
165   }
166 
IgnoreUpstreamNotificationMsg()167   void IgnoreUpstreamNotificationMsg() {
168     EXPECT_CALL(gcm_driver_,
169                 SendImpl(kHeartbeatGCMAppID, _, IsUpstreamNotificationMsg()))
170         .Times(AnyNumber());
171   }
172 
173   content::BrowserTaskEnvironment task_environment_;
174   MockGCMDriver gcm_driver_;
175   chromeos::ScopedTestingCrosSettings scoped_testing_cros_settings_;
176   testing::NiceMock<policy::MockCloudPolicyClient> cloud_policy_client_;
177   testing::NiceMock<policy::MockCloudPolicyStore> cloud_policy_store_;
178 
179   // TaskRunner used to run individual tests.
180   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
181 
182   // The HeartbeatScheduler instance under test.
183   policy::HeartbeatScheduler scheduler_;
184 
185   base::HistogramTester histogram_tester_;
186 };
187 
TEST_F(HeartbeatSchedulerTest,Basic)188 TEST_F(HeartbeatSchedulerTest, Basic) {
189   // Just makes sure we can spin up and shutdown the scheduler with
190   // heartbeats disabled.
191   scoped_testing_cros_settings_.device_settings()->SetBoolean(
192       chromeos::kHeartbeatEnabled, false);
193   ASSERT_FALSE(task_runner_->HasPendingTask());
194 }
195 
TEST_F(HeartbeatSchedulerTest,PermanentlyFailedGCMRegistration)196 TEST_F(HeartbeatSchedulerTest, PermanentlyFailedGCMRegistration) {
197   // If heartbeats are enabled, we should register with GCMDriver.
198   EXPECT_CALL(gcm_driver_, RegisterImpl(kHeartbeatGCMAppID, _));
199   scoped_testing_cros_settings_.device_settings()->SetBoolean(
200       chromeos::kHeartbeatEnabled, true);
201   gcm_driver_.CompleteRegistration(
202       kHeartbeatGCMAppID, gcm::GCMClient::GCM_DISABLED);
203 
204   // There should be no heartbeat tasks pending, because registration failed.
205   ASSERT_FALSE(task_runner_->HasPendingTask());
206 }
207 
TEST_F(HeartbeatSchedulerTest,TemporarilyFailedGCMRegistration)208 TEST_F(HeartbeatSchedulerTest, TemporarilyFailedGCMRegistration) {
209   IgnoreUpstreamNotificationMsg();
210 
211   EXPECT_CALL(gcm_driver_, RegisterImpl(kHeartbeatGCMAppID, _));
212   scoped_testing_cros_settings_.device_settings()->SetBoolean(
213       chromeos::kHeartbeatEnabled, true);
214   gcm_driver_.CompleteRegistration(
215       kHeartbeatGCMAppID, gcm::GCMClient::SERVER_ERROR);
216   testing::Mock::VerifyAndClearExpectations(&gcm_driver_);
217 
218   IgnoreUpstreamNotificationMsg();
219   gcm_driver_.IgnoreDefaultHeartbeatsInterval();
220 
221   // Should have a pending task to try registering again.
222   ASSERT_TRUE(task_runner_->HasPendingTask());
223   EXPECT_CALL(gcm_driver_, RegisterImpl(kHeartbeatGCMAppID, _));
224   task_runner_->RunPendingTasks();
225   testing::Mock::VerifyAndClearExpectations(&gcm_driver_);
226 
227   IgnoreUpstreamNotificationMsg();
228   gcm_driver_.IgnoreDefaultHeartbeatsInterval();
229 
230   // Once we have successfully registered, we should send a heartbeat.
231   EXPECT_CALL(gcm_driver_, SendImpl(kHeartbeatGCMAppID, _, IsHeartbeatMsg()));
232   gcm_driver_.CompleteRegistration(
233       kHeartbeatGCMAppID, gcm::GCMClient::SUCCESS);
234   task_runner_->RunPendingTasks();
235 }
236 
TEST_F(HeartbeatSchedulerTest,StoreResetDuringRegistration)237 TEST_F(HeartbeatSchedulerTest, StoreResetDuringRegistration) {
238   IgnoreUpstreamNotificationMsg();
239 
240   EXPECT_CALL(gcm_driver_, RegisterImpl(kHeartbeatGCMAppID, _));
241   scoped_testing_cros_settings_.device_settings()->SetBoolean(
242       chromeos::kHeartbeatEnabled, true);
243   testing::Mock::VerifyAndClearExpectations(&gcm_driver_);
244 
245   gcm_driver_.ResetStore();
246 
247   IgnoreUpstreamNotificationMsg();
248 
249   // Successful registration handled ok despite store reset.
250   EXPECT_FALSE(task_runner_->HasPendingTask());
251   EXPECT_CALL(gcm_driver_, SendImpl(kHeartbeatGCMAppID, _, IsHeartbeatMsg()));
252   gcm_driver_.CompleteRegistration(
253       kHeartbeatGCMAppID, gcm::GCMClient::SUCCESS);
254   EXPECT_EQ(1U, task_runner_->NumPendingTasks());
255   task_runner_->RunPendingTasks();
256   testing::Mock::VerifyAndClearExpectations(&gcm_driver_);
257   histogram_tester_.ExpectTotalCount(
258       policy::HeartbeatScheduler::kHeartbeatSignalHistogram, 0);
259 }
260 
TEST_F(HeartbeatSchedulerTest,StoreResetAfterRegistration)261 TEST_F(HeartbeatSchedulerTest, StoreResetAfterRegistration) {
262   IgnoreUpstreamNotificationMsg();
263 
264   // Start from a successful registration.
265   EXPECT_CALL(gcm_driver_, RegisterImpl(kHeartbeatGCMAppID, _));
266   scoped_testing_cros_settings_.device_settings()->SetBoolean(
267       chromeos::kHeartbeatEnabled, true);
268   EXPECT_CALL(gcm_driver_, SendImpl(kHeartbeatGCMAppID, _, IsHeartbeatMsg()));
269   gcm_driver_.CompleteRegistration(
270       kHeartbeatGCMAppID, gcm::GCMClient::SUCCESS);
271   EXPECT_EQ(1U, task_runner_->NumPendingTasks());
272   task_runner_->RunPendingTasks();
273   testing::Mock::VerifyAndClearExpectations(&gcm_driver_);
274 
275   gcm_driver_.IgnoreDefaultHeartbeatsInterval();
276 
277   // Reseting the store should trigger re-registration.
278   EXPECT_CALL(gcm_driver_, RegisterImpl(kHeartbeatGCMAppID, _));
279   gcm_driver_.ResetStore();
280   testing::Mock::VerifyAndClearExpectations(&gcm_driver_);
281 
282   IgnoreUpstreamNotificationMsg();
283 
284   // Once we have successfully re-registered, we should send a heartbeat.
285   EXPECT_FALSE(task_runner_->HasPendingTask());
286   EXPECT_CALL(gcm_driver_, SendImpl(kHeartbeatGCMAppID, _, IsHeartbeatMsg()));
287   gcm_driver_.CompleteRegistration(
288       kHeartbeatGCMAppID, gcm::GCMClient::SUCCESS);
289   EXPECT_EQ(1U, task_runner_->NumPendingTasks());
290   task_runner_->RunPendingTasks();
291   testing::Mock::VerifyAndClearExpectations(&gcm_driver_);
292 }
293 
TEST_F(HeartbeatSchedulerTest,ChangeHeartbeatFrequency)294 TEST_F(HeartbeatSchedulerTest, ChangeHeartbeatFrequency) {
295   IgnoreUpstreamNotificationMsg();
296 
297   EXPECT_CALL(gcm_driver_, RegisterImpl(kHeartbeatGCMAppID, _));
298   scoped_testing_cros_settings_.device_settings()->SetBoolean(
299       chromeos::kHeartbeatEnabled, true);
300   gcm_driver_.CompleteRegistration(
301       kHeartbeatGCMAppID, gcm::GCMClient::SUCCESS);
302 
303   EXPECT_EQ(1U, task_runner_->NumPendingTasks());
304   // Should have a heartbeat task posted with zero delay on startup.
305   EXPECT_EQ(base::TimeDelta(), task_runner_->NextPendingTaskDelay());
306   testing::Mock::VerifyAndClearExpectations(&gcm_driver_);
307 
308   IgnoreUpstreamNotificationMsg();
309   gcm_driver_.IgnoreDefaultHeartbeatsInterval();
310 
311   const int new_delay = 1234*1000;  // 1234 seconds.
312   EXPECT_CALL(gcm_driver_, AddHeartbeatInterval(_, new_delay));
313   scoped_testing_cros_settings_.device_settings()->SetInteger(
314       chromeos::kHeartbeatFrequency, new_delay);
315   // Now run pending heartbeat task, should send a heartbeat.
316   gcm::OutgoingMessage message;
317   EXPECT_CALL(gcm_driver_, SendImpl(kHeartbeatGCMAppID, _, IsHeartbeatMsg()))
318       .WillOnce(SaveArg<2>(&message));
319   task_runner_->RunPendingTasks();
320   EXPECT_FALSE(task_runner_->HasPendingTask());
321 
322   // Complete sending a message - we should queue up the next heartbeat
323   // even if the previous attempt failed.
324   gcm_driver_.CompleteSend(
325       kHeartbeatGCMAppID, message.id, gcm::GCMClient::SERVER_ERROR);
326   EXPECT_EQ(1U, task_runner_->NumPendingTasks());
327   histogram_tester_.ExpectUniqueSample(
328       policy::HeartbeatScheduler::kHeartbeatSignalHistogram, /*failure*/ false,
329       /*amount*/ 1);
330   CheckPendingTaskDelay(scheduler_.last_heartbeat(),
331                         base::TimeDelta::FromMilliseconds(new_delay));
332 }
333 
TEST_F(HeartbeatSchedulerTest,DisableHeartbeats)334 TEST_F(HeartbeatSchedulerTest, DisableHeartbeats) {
335   IgnoreUpstreamNotificationMsg();
336 
337   // Makes sure that we can disable heartbeats on the fly.
338   EXPECT_CALL(gcm_driver_, RegisterImpl(kHeartbeatGCMAppID, _));
339   scoped_testing_cros_settings_.device_settings()->SetBoolean(
340       chromeos::kHeartbeatEnabled, true);
341   gcm::OutgoingMessage message;
342   EXPECT_CALL(gcm_driver_, SendImpl(kHeartbeatGCMAppID, _, IsHeartbeatMsg()))
343       .WillOnce(SaveArg<2>(&message));
344   gcm_driver_.CompleteRegistration(
345       kHeartbeatGCMAppID, gcm::GCMClient::SUCCESS);
346   // Should have a heartbeat task posted.
347   EXPECT_EQ(1U, task_runner_->NumPendingTasks());
348   task_runner_->RunPendingTasks();
349 
350   // Complete sending a message - we should queue up the next heartbeat.
351   gcm_driver_.CompleteSend(
352       kHeartbeatGCMAppID, message.id, gcm::GCMClient::SUCCESS);
353   histogram_tester_.ExpectUniqueSample(
354       policy::HeartbeatScheduler::kHeartbeatSignalHistogram, /*success*/ true,
355       /*amount*/ 1);
356 
357   // Should have a new heartbeat task posted.
358   ASSERT_EQ(1U, task_runner_->NumPendingTasks());
359   CheckPendingTaskDelay(scheduler_.last_heartbeat(),
360                         policy::HeartbeatScheduler::kDefaultHeartbeatInterval);
361   testing::Mock::VerifyAndClearExpectations(&gcm_driver_);
362 
363   IgnoreUpstreamNotificationMsg();
364   gcm_driver_.IgnoreDefaultHeartbeatsInterval();
365 
366   // Now disable heartbeats. Should get no more heartbeats sent.
367   scoped_testing_cros_settings_.device_settings()->SetBoolean(
368       chromeos::kHeartbeatEnabled, false);
369   task_runner_->RunPendingTasks();
370   EXPECT_FALSE(task_runner_->HasPendingTask());
371 }
372 
TEST_F(HeartbeatSchedulerTest,CheckMessageContents)373 TEST_F(HeartbeatSchedulerTest, CheckMessageContents) {
374   IgnoreUpstreamNotificationMsg();
375 
376   gcm::OutgoingMessage message;
377   EXPECT_CALL(gcm_driver_, RegisterImpl(kHeartbeatGCMAppID, _));
378   EXPECT_CALL(gcm_driver_, SendImpl(kHeartbeatGCMAppID, _, IsHeartbeatMsg()))
379       .WillOnce(SaveArg<2>(&message));
380   scoped_testing_cros_settings_.device_settings()->SetBoolean(
381       chromeos::kHeartbeatEnabled, true);
382   gcm_driver_.CompleteRegistration(
383       kHeartbeatGCMAppID, gcm::GCMClient::SUCCESS);
384   task_runner_->RunPendingTasks();
385 
386   // Heartbeats should have a time-to-live equivalent to the heartbeat frequency
387   // so we don't have more than one heartbeat queued at a time.
388   EXPECT_EQ(policy::HeartbeatScheduler::kDefaultHeartbeatInterval,
389             base::TimeDelta::FromSeconds(message.time_to_live));
390 
391   // Check the values in the message payload.
392   EXPECT_EQ("hb", message.data["type"]);
393   int64_t timestamp;
394   EXPECT_TRUE(base::StringToInt64(message.data["timestamp"], &timestamp));
395   EXPECT_EQ(kFakeCustomerId, message.data["customer_id"]);
396   EXPECT_EQ(kFakeDeviceId, message.data["device_id"]);
397 }
398 
TEST_F(HeartbeatSchedulerTest,SendGcmIdUpdate)399 TEST_F(HeartbeatSchedulerTest, SendGcmIdUpdate) {
400   // Verifies that GCM id update request was sent after GCM registration.
401   cloud_policy_client_.SetDMToken(kDMToken);
402   policy::CloudPolicyClient::StatusCallback callback;
403   EXPECT_CALL(cloud_policy_client_, UpdateGcmId_(kRegistrationId, _))
404       .WillOnce(MoveArg<1>(&callback));
405 
406   // Enable heartbeats.
407   EXPECT_CALL(gcm_driver_, RegisterImpl(kHeartbeatGCMAppID, _));
408   EXPECT_CALL(gcm_driver_, SendImpl(kHeartbeatGCMAppID, _, _))
409       .Times(AtLeast(1));
410   scoped_testing_cros_settings_.device_settings()->SetBoolean(
411       chromeos::kHeartbeatEnabled, true);
412   gcm_driver_.CompleteRegistration(kHeartbeatGCMAppID, gcm::GCMClient::SUCCESS);
413   task_runner_->RunPendingTasks();
414 
415   // Verifies that CloudPolicyClient got the update request, with a valid
416   // callback.
417   testing::Mock::VerifyAndClearExpectations(&cloud_policy_client_);
418   EXPECT_FALSE(callback.is_null());
419   std::move(callback).Run(true);
420 }
421 
TEST_F(HeartbeatSchedulerTest,GcmUpstreamNotificationSignup)422 TEST_F(HeartbeatSchedulerTest, GcmUpstreamNotificationSignup) {
423   // Verifies that upstream notification works as expected.
424   cloud_policy_client_.SetDMToken(kDMToken);
425   EXPECT_CALL(gcm_driver_, RegisterImpl(kHeartbeatGCMAppID, _))
426       .Times(AnyNumber());
427   EXPECT_CALL(cloud_policy_client_, UpdateGcmId_(kRegistrationId, _));
428 
429   // GCM connected event before the registration should be ignored.
430   scoped_testing_cros_settings_.device_settings()->SetBoolean(
431       chromeos::kHeartbeatEnabled, true);
432   gcm_driver_.NotifyConnected();
433   task_runner_->RunPendingTasks();
434   testing::Mock::VerifyAndClearExpectations(&gcm_driver_);
435 
436   // Ignore unintested calls.
437   EXPECT_CALL(gcm_driver_, RegisterImpl(kHeartbeatGCMAppID, _))
438       .Times(AnyNumber());
439   EXPECT_CALL(gcm_driver_, SendImpl(kHeartbeatGCMAppID, _, IsHeartbeatMsg()))
440       .Times(AnyNumber());
441   gcm_driver_.IgnoreDefaultHeartbeatsInterval();
442 
443   EXPECT_CALL(gcm_driver_,
444               SendImpl(kHeartbeatGCMAppID, _, IsUpstreamNotificationMsg()))
445       .Times(AtLeast(1));
446   gcm_driver_.CompleteRegistration(kHeartbeatGCMAppID, gcm::GCMClient::SUCCESS);
447 
448   gcm_driver_.NotifyConnected();
449 }
450 
451 }  // namespace
452