1//
2// Copyright (C) 2014 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16#include "update_engine/update_manager/real_shill_provider.h"
17
18#include <memory>
19#include <utility>
20
21#include <base/memory/ptr_util.h>
22#include <base/time/time.h>
23#include <brillo/message_loops/fake_message_loop.h>
24#include <gmock/gmock.h>
25#include <gtest/gtest.h>
26#include <shill/dbus-constants.h>
27#include <shill/dbus-proxies.h>
28#include <shill/dbus-proxy-mocks.h>
29
30#include "update_engine/common/fake_clock.h"
31#include "update_engine/common/test_utils.h"
32#include "update_engine/dbus_test_utils.h"
33#include "update_engine/fake_shill_proxy.h"
34#include "update_engine/update_manager/umtest_utils.h"
35
36using base::Time;
37using base::TimeDelta;
38using chromeos_update_engine::ConnectionTethering;
39using chromeos_update_engine::ConnectionType;
40using chromeos_update_engine::FakeClock;
41using org::chromium::flimflam::ManagerProxyMock;
42using org::chromium::flimflam::ServiceProxyMock;
43using std::unique_ptr;
44using testing::Mock;
45using testing::Return;
46using testing::SetArgPointee;
47using testing::_;
48
49namespace {
50
51// Fake service paths.
52const char* const kFakeEthernetServicePath = "/fake/ethernet/service";
53const char* const kFakeWifiServicePath = "/fake/wifi/service";
54const char* const kFakeWimaxServicePath = "/fake/wimax/service";
55const char* const kFakeBluetoothServicePath = "/fake/bluetooth/service";
56const char* const kFakeCellularServicePath = "/fake/cellular/service";
57const char* const kFakeVpnServicePath = "/fake/vpn/service";
58const char* const kFakeUnknownServicePath = "/fake/unknown/service";
59
60}  // namespace
61
62namespace chromeos_update_manager {
63
64class UmRealShillProviderTest : public ::testing::Test {
65 protected:
66  // Initialize the RealShillProvider under test.
67  void SetUp() override {
68    fake_clock_.SetWallclockTime(InitTime());
69    loop_.SetAsCurrent();
70    fake_shill_proxy_ = new chromeos_update_engine::FakeShillProxy();
71    provider_.reset(new RealShillProvider(fake_shill_proxy_, &fake_clock_));
72
73    ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_->GetManagerProxy();
74
75    // The PropertyChanged signal should be subscribed to.
76    MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER(
77        manager_property_changed_, *manager_proxy_mock, PropertyChanged);
78  }
79
80  void TearDown() override {
81    provider_.reset();
82    // Check for leaked callbacks on the main loop.
83    EXPECT_FALSE(loop_.PendingTasks());
84  }
85
86  // These methods generate fixed timestamps for use in faking the current time.
87  Time InitTime() {
88    Time::Exploded now_exp;
89    now_exp.year = 2014;
90    now_exp.month = 3;
91    now_exp.day_of_week = 2;
92    now_exp.day_of_month = 18;
93    now_exp.hour = 8;
94    now_exp.minute = 5;
95    now_exp.second = 33;
96    now_exp.millisecond = 675;
97    Time time;
98    ignore_result(Time::FromLocalExploded(now_exp, &time));
99    return time;
100  }
101
102  Time ConnChangedTime() {
103    return InitTime() + TimeDelta::FromSeconds(10);
104  }
105
106  // Sets the default_service object path in the response from the
107  // ManagerProxyMock instance.
108  void SetManagerReply(const char* default_service, bool reply_succeeds);
109
110  // Sets the |service_type|, |physical_technology| and |service_tethering|
111  // properties in the mocked service |service_path|. If any of the three
112  // const char* is a nullptr, the corresponding property will not be included
113  // in the response.
114  // Returns the mock object pointer, owned by the |fake_shill_proxy_|.
115  ServiceProxyMock* SetServiceReply(const std::string& service_path,
116                                    const char* service_type,
117                                    const char* physical_technology,
118                                    const char* service_tethering);
119
120  void InitWithDefaultService(const char* default_service) {
121    SetManagerReply(default_service, true);
122    // Check that provider initializes correctly.
123    EXPECT_TRUE(provider_->Init());
124    // RunOnce to notify the signal handler was connected properly.
125    EXPECT_TRUE(loop_.RunOnce(false));
126  }
127
128  // Sends a signal informing the provider about a default connection
129  // |service_path|. Sets the fake connection change time in
130  // |conn_change_time_p| if provided.
131  void SendDefaultServiceSignal(const std::string& service_path,
132                                Time* conn_change_time_p) {
133    const Time conn_change_time = ConnChangedTime();
134    fake_clock_.SetWallclockTime(conn_change_time);
135    ASSERT_TRUE(manager_property_changed_.IsHandlerRegistered());
136    manager_property_changed_.signal_callback().Run(
137        shill::kDefaultServiceProperty, dbus::ObjectPath(service_path));
138    fake_clock_.SetWallclockTime(conn_change_time + TimeDelta::FromSeconds(5));
139    if (conn_change_time_p)
140      *conn_change_time_p = conn_change_time;
141  }
142
143  // Sets up expectations for detection of a connection |service_path| with type
144  // |shill_type_str| and tethering mode |shill_tethering_str|. Ensures that the
145  // new connection status and change time are properly detected by the
146  // provider. Writes the fake connection change time to |conn_change_time_p|,
147  // if provided.
148  void SetupConnectionAndAttrs(const std::string& service_path,
149                               const char* shill_type,
150                               const char* shill_tethering,
151                               Time* conn_change_time_p) {
152    SetServiceReply(service_path, shill_type, nullptr, shill_tethering);
153    // Note: We don't setup this |service_path| as the default service path but
154    // we instead send a signal notifying the change since the code won't call
155    // GetProperties on the Manager object at this point.
156
157    // Send a signal about a new default service.
158    Time conn_change_time;
159    SendDefaultServiceSignal(service_path, &conn_change_time);
160
161    // Query the connection status, ensure last change time reported correctly.
162    UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected());
163    UmTestUtils::ExpectVariableHasValue(conn_change_time,
164                                        provider_->var_conn_last_changed());
165
166    // Write the connection change time to the output argument.
167    if (conn_change_time_p)
168      *conn_change_time_p = conn_change_time;
169  }
170
171  // Sets up a connection and tests that its type is being properly detected by
172  // the provider.
173  void SetupConnectionAndTestType(const char* service_path,
174                                  const char* shill_type,
175                                  ConnectionType expected_conn_type) {
176    // Set up and test the connection, record the change time.
177    Time conn_change_time;
178    SetupConnectionAndAttrs(service_path,
179                            shill_type,
180                            shill::kTetheringNotDetectedState,
181                            &conn_change_time);
182
183    // Query the connection type, ensure last change time did not change.
184    UmTestUtils::ExpectVariableHasValue(expected_conn_type,
185                                        provider_->var_conn_type());
186    UmTestUtils::ExpectVariableHasValue(conn_change_time,
187                                        provider_->var_conn_last_changed());
188  }
189
190  // Sets up a connection and tests that its tethering mode is being properly
191  // detected by the provider.
192  void SetupConnectionAndTestTethering(
193      const char* service_path,
194      const char* shill_tethering,
195      ConnectionTethering expected_conn_tethering) {
196    // Set up and test the connection, record the change time.
197    Time conn_change_time;
198    SetupConnectionAndAttrs(
199        service_path, shill::kTypeEthernet, shill_tethering, &conn_change_time);
200
201    // Query the connection tethering, ensure last change time did not change.
202    UmTestUtils::ExpectVariableHasValue(expected_conn_tethering,
203                                        provider_->var_conn_tethering());
204    UmTestUtils::ExpectVariableHasValue(conn_change_time,
205                                        provider_->var_conn_last_changed());
206  }
207
208  brillo::FakeMessageLoop loop_{nullptr};
209  FakeClock fake_clock_;
210  chromeos_update_engine::FakeShillProxy* fake_shill_proxy_;
211
212  // The registered signal handler for the signal Manager.PropertyChanged.
213  chromeos_update_engine::dbus_test_utils::MockSignalHandler<
214      void(const std::string&, const brillo::Any&)> manager_property_changed_;
215
216  unique_ptr<RealShillProvider> provider_;
217};
218
219void UmRealShillProviderTest::SetManagerReply(const char* default_service,
220                                              bool reply_succeeds) {
221  ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_->GetManagerProxy();
222  if (!reply_succeeds) {
223    EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _))
224        .WillOnce(Return(false));
225    return;
226  }
227
228  // Create a dictionary of properties and optionally include the default
229  // service.
230  brillo::VariantDictionary reply_dict;
231  reply_dict["SomeOtherProperty"] = 0xC0FFEE;
232
233  if (default_service) {
234    reply_dict[shill::kDefaultServiceProperty] =
235        dbus::ObjectPath(default_service);
236  }
237  EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _))
238      .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true)));
239}
240
241ServiceProxyMock* UmRealShillProviderTest::SetServiceReply(
242    const std::string& service_path,
243    const char* service_type,
244    const char* physical_technology,
245    const char* service_tethering) {
246  brillo::VariantDictionary reply_dict;
247  reply_dict["SomeOtherProperty"] = 0xC0FFEE;
248
249  if (service_type)
250    reply_dict[shill::kTypeProperty] = std::string(service_type);
251
252  if (physical_technology) {
253    reply_dict[shill::kPhysicalTechnologyProperty] =
254        std::string(physical_technology);
255  }
256
257  if (service_tethering)
258    reply_dict[shill::kTetheringProperty] = std::string(service_tethering);
259
260  ServiceProxyMock* service_proxy_mock = new ServiceProxyMock();
261
262  // Plumb return value into mock object.
263  EXPECT_CALL(*service_proxy_mock, GetProperties(_, _, _))
264      .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true)));
265
266  fake_shill_proxy_->SetServiceForPath(dbus::ObjectPath(service_path),
267                                       base::WrapUnique(service_proxy_mock));
268
269  return service_proxy_mock;
270}
271
272
273// Query the connection status, type and time last changed, as they were set
274// during initialization (no signals).
275TEST_F(UmRealShillProviderTest, ReadBaseValues) {
276  InitWithDefaultService("/");
277  // Query the provider variables.
278  UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_connected());
279  UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type());
280  UmTestUtils::ExpectVariableHasValue(InitTime(),
281                                      provider_->var_conn_last_changed());
282}
283
284// Ensure that invalid DBus paths are ignored.
285TEST_F(UmRealShillProviderTest, InvalidServicePath) {
286  InitWithDefaultService("invalid");
287  UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_connected());
288  UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type());
289  UmTestUtils::ExpectVariableHasValue(InitTime(),
290                                      provider_->var_conn_last_changed());
291}
292
293// Ensure that a service path property including a different type is ignored.
294TEST_F(UmRealShillProviderTest, InvalidServicePathType) {
295  ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_->GetManagerProxy();
296  brillo::VariantDictionary reply_dict;
297  reply_dict[shill::kDefaultServiceProperty] = "/not/an/object/path";
298  EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _))
299      .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true)));
300
301  EXPECT_TRUE(provider_->Init());
302  EXPECT_TRUE(loop_.RunOnce(false));
303
304  UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_connected());
305}
306
307// Test that Ethernet connection is identified correctly.
308TEST_F(UmRealShillProviderTest, ReadConnTypeEthernet) {
309  InitWithDefaultService("/");
310  SetupConnectionAndTestType(kFakeEthernetServicePath,
311                             shill::kTypeEthernet,
312                             ConnectionType::kEthernet);
313}
314
315// Test that Wifi connection is identified correctly.
316TEST_F(UmRealShillProviderTest, ReadConnTypeWifi) {
317  InitWithDefaultService("/");
318  SetupConnectionAndTestType(kFakeWifiServicePath,
319                             shill::kTypeWifi,
320                             ConnectionType::kWifi);
321}
322
323// Test that Wimax connection is identified correctly.
324TEST_F(UmRealShillProviderTest, ReadConnTypeWimax) {
325  InitWithDefaultService("/");
326  SetupConnectionAndTestType(kFakeWimaxServicePath,
327                             shill::kTypeWimax,
328                             ConnectionType::kWimax);
329}
330
331// Test that Bluetooth connection is identified correctly.
332TEST_F(UmRealShillProviderTest, ReadConnTypeBluetooth) {
333  InitWithDefaultService("/");
334  SetupConnectionAndTestType(kFakeBluetoothServicePath,
335                             shill::kTypeBluetooth,
336                             ConnectionType::kBluetooth);
337}
338
339// Test that Cellular connection is identified correctly.
340TEST_F(UmRealShillProviderTest, ReadConnTypeCellular) {
341  InitWithDefaultService("/");
342  SetupConnectionAndTestType(kFakeCellularServicePath,
343                             shill::kTypeCellular,
344                             ConnectionType::kCellular);
345}
346
347// Test that an unknown connection is identified as such.
348TEST_F(UmRealShillProviderTest, ReadConnTypeUnknown) {
349  InitWithDefaultService("/");
350  SetupConnectionAndTestType(kFakeUnknownServicePath,
351                             "FooConnectionType",
352                             ConnectionType::kUnknown);
353}
354
355// Tests that VPN connection is identified correctly.
356TEST_F(UmRealShillProviderTest, ReadConnTypeVpn) {
357  InitWithDefaultService("/");
358  // Mock logic for returning a default service path and its type.
359  SetServiceReply(kFakeVpnServicePath,
360                  shill::kTypeVPN,
361                  shill::kTypeWifi,
362                  shill::kTetheringNotDetectedState);
363
364  // Send a signal about a new default service.
365  Time conn_change_time;
366  SendDefaultServiceSignal(kFakeVpnServicePath, &conn_change_time);
367
368  // Query the connection type, ensure last change time reported correctly.
369  UmTestUtils::ExpectVariableHasValue(ConnectionType::kWifi,
370                                      provider_->var_conn_type());
371  UmTestUtils::ExpectVariableHasValue(conn_change_time,
372                                      provider_->var_conn_last_changed());
373}
374
375// Ensure that the connection type is properly cached in the provider through
376// subsequent variable readings.
377TEST_F(UmRealShillProviderTest, ConnTypeCacheUsed) {
378  InitWithDefaultService("/");
379  SetupConnectionAndTestType(kFakeEthernetServicePath,
380                             shill::kTypeEthernet,
381                             ConnectionType::kEthernet);
382
383  UmTestUtils::ExpectVariableHasValue(ConnectionType::kEthernet,
384                                      provider_->var_conn_type());
385}
386
387// Ensure that the cached connection type remains valid even when a default
388// connection signal occurs but the connection is not changed.
389TEST_F(UmRealShillProviderTest, ConnTypeCacheRemainsValid) {
390  InitWithDefaultService("/");
391  SetupConnectionAndTestType(kFakeEthernetServicePath,
392                             shill::kTypeEthernet,
393                             ConnectionType::kEthernet);
394
395  SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr);
396
397  UmTestUtils::ExpectVariableHasValue(ConnectionType::kEthernet,
398                                      provider_->var_conn_type());
399}
400
401// Ensure that the cached connection type is invalidated and re-read when the
402// default connection changes.
403TEST_F(UmRealShillProviderTest, ConnTypeCacheInvalidated) {
404  InitWithDefaultService("/");
405  SetupConnectionAndTestType(kFakeEthernetServicePath,
406                             shill::kTypeEthernet,
407                             ConnectionType::kEthernet);
408
409  SetupConnectionAndTestType(kFakeWifiServicePath,
410                             shill::kTypeWifi,
411                             ConnectionType::kWifi);
412}
413
414// Test that a non-tethering mode is identified correctly.
415TEST_F(UmRealShillProviderTest, ReadConnTetheringNotDetected) {
416  InitWithDefaultService("/");
417  SetupConnectionAndTestTethering(kFakeWifiServicePath,
418                                  shill::kTetheringNotDetectedState,
419                                  ConnectionTethering::kNotDetected);
420}
421
422// Test that a suspected tethering mode is identified correctly.
423TEST_F(UmRealShillProviderTest, ReadConnTetheringSuspected) {
424  InitWithDefaultService("/");
425  SetupConnectionAndTestTethering(kFakeWifiServicePath,
426                                  shill::kTetheringSuspectedState,
427                                  ConnectionTethering::kSuspected);
428}
429
430// Test that a confirmed tethering mode is identified correctly.
431TEST_F(UmRealShillProviderTest, ReadConnTetheringConfirmed) {
432  InitWithDefaultService("/");
433  SetupConnectionAndTestTethering(kFakeWifiServicePath,
434                                  shill::kTetheringConfirmedState,
435                                  ConnectionTethering::kConfirmed);
436}
437
438// Test that an unknown tethering mode is identified as such.
439TEST_F(UmRealShillProviderTest, ReadConnTetheringUnknown) {
440  InitWithDefaultService("/");
441  SetupConnectionAndTestTethering(kFakeWifiServicePath,
442                                  "FooConnTethering",
443                                  ConnectionTethering::kUnknown);
444}
445
446// Ensure that the connection tethering mode is properly cached in the provider.
447TEST_F(UmRealShillProviderTest, ConnTetheringCacheUsed) {
448  InitWithDefaultService("/");
449  SetupConnectionAndTestTethering(kFakeEthernetServicePath,
450                                  shill::kTetheringNotDetectedState,
451                                  ConnectionTethering::kNotDetected);
452
453  UmTestUtils::ExpectVariableHasValue(ConnectionTethering::kNotDetected,
454                                      provider_->var_conn_tethering());
455}
456
457// Ensure that the cached connection tethering mode remains valid even when a
458// default connection signal occurs but the connection is not changed.
459TEST_F(UmRealShillProviderTest, ConnTetheringCacheRemainsValid) {
460  InitWithDefaultService("/");
461  SetupConnectionAndTestTethering(kFakeEthernetServicePath,
462                                  shill::kTetheringNotDetectedState,
463                                  ConnectionTethering::kNotDetected);
464
465  SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr);
466
467  UmTestUtils::ExpectVariableHasValue(ConnectionTethering::kNotDetected,
468                                      provider_->var_conn_tethering());
469}
470
471// Ensure that the cached connection tethering mode is invalidated and re-read
472// when the default connection changes.
473TEST_F(UmRealShillProviderTest, ConnTetheringCacheInvalidated) {
474  InitWithDefaultService("/");
475  SetupConnectionAndTestTethering(kFakeEthernetServicePath,
476                                  shill::kTetheringNotDetectedState,
477                                  ConnectionTethering::kNotDetected);
478
479  SetupConnectionAndTestTethering(kFakeWifiServicePath,
480                                  shill::kTetheringConfirmedState,
481                                  ConnectionTethering::kConfirmed);
482}
483
484// Fake two DBus signals prompting a default connection change, but otherwise
485// give the same service path. Check connection status and the time it was last
486// changed, making sure that it is the time when the first signal was sent (and
487// not the second).
488TEST_F(UmRealShillProviderTest, ReadLastChangedTimeTwoSignals) {
489  InitWithDefaultService("/");
490  // Send a default service signal twice, advancing the clock in between.
491  Time conn_change_time;
492  SetupConnectionAndAttrs(kFakeEthernetServicePath,
493                          shill::kTypeEthernet,
494                          shill::kTetheringNotDetectedState,
495                          &conn_change_time);
496  // This will set the service path to the same value, so it should not call
497  // GetProperties() again.
498  SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr);
499
500  // Query the connection status, ensure last change time reported as the first
501  // time the signal was sent.
502  UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected());
503  UmTestUtils::ExpectVariableHasValue(conn_change_time,
504                                      provider_->var_conn_last_changed());
505}
506
507// Make sure that the provider initializes correctly even if shill is not
508// responding, that variables can be obtained, and that they all return a null
509// value (indicating that the underlying values were not set).
510TEST_F(UmRealShillProviderTest, NoInitConnStatusReadBaseValues) {
511  // Initialize the provider, no initial connection status response.
512  SetManagerReply(nullptr, false);
513  EXPECT_TRUE(provider_->Init());
514  EXPECT_TRUE(loop_.RunOnce(false));
515  UmTestUtils::ExpectVariableNotSet(provider_->var_is_connected());
516  UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type());
517  UmTestUtils::ExpectVariableNotSet(provider_->var_conn_last_changed());
518}
519
520// Test that, once a signal is received, the connection status and other info
521// can be read correctly.
522TEST_F(UmRealShillProviderTest, NoInitConnStatusReadConnTypeEthernet) {
523  // Initialize the provider with no initial connection status response.
524  SetManagerReply(nullptr, false);
525  EXPECT_TRUE(provider_->Init());
526  EXPECT_TRUE(loop_.RunOnce(false));
527
528  SetupConnectionAndAttrs(kFakeEthernetServicePath,
529                          shill::kTypeEthernet,
530                          shill::kTetheringNotDetectedState,
531                          nullptr);
532  UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected());
533}
534
535}  // namespace chromeos_update_manager
536