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"], ×tamp));
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