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