1 //
2 // Copyright (C) 2012 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 
17 #include "shill/cellular/modem.h"
18 
19 #include <vector>
20 
21 #include <base/strings/stringprintf.h>
22 #include <gmock/gmock.h>
23 #include <gtest/gtest.h>
24 #include <mm/mm-modem.h>
25 #include <net/if.h>
26 #include <sys/ioctl.h>
27 
28 #include "shill/cellular/cellular.h"
29 #include "shill/cellular/cellular_capability_gsm.h"
30 #include "shill/cellular/mock_cellular.h"
31 #include "shill/cellular/mock_modem.h"
32 #include "shill/cellular/mock_modem_info.h"
33 #include "shill/manager.h"
34 #include "shill/mock_device_info.h"
35 #include "shill/net/mock_rtnl_handler.h"
36 #include "shill/net/rtnl_handler.h"
37 #include "shill/test_event_dispatcher.h"
38 
39 using std::string;
40 using std::vector;
41 using testing::_;
42 using testing::AnyNumber;
43 using testing::DoAll;
44 using testing::Return;
45 using testing::SetArgumentPointee;
46 using testing::StrEq;
47 using testing::StrictMock;
48 using testing::Test;
49 
50 namespace shill {
51 
52 namespace {
53 
54 const int kTestInterfaceIndex = 5;
55 const char kLinkName[] = "usb0";
56 const char kService[] = "org.chromium.ModemManager";
57 const char kPath[] = "/org/chromium/ModemManager/Gobi/0";
58 const unsigned char kAddress[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
59 const char kAddressAsString[] = "000102030405";
60 
61 }  // namespace
62 
63 class ModemTest : public Test {
64  public:
ModemTest()65   ModemTest()
66       : modem_info_(nullptr, &dispatcher_, nullptr, nullptr),
67         device_info_(modem_info_.control_interface(), modem_info_.dispatcher(),
68                      modem_info_.metrics(), modem_info_.manager()),
69         modem_(
70             new StrictModem(
71                 kService,
72                 kPath,
73                 &modem_info_,
74                 nullptr)) {}
75   virtual void SetUp();
76   virtual void TearDown();
77 
ReplaceSingletons()78   void ReplaceSingletons() {
79     modem_->rtnl_handler_ = &rtnl_handler_;
80   }
81 
82  protected:
83   EventDispatcherForTest dispatcher_;
84   MockModemInfo modem_info_;
85   MockDeviceInfo device_info_;
86   std::unique_ptr<StrictModem> modem_;
87   MockRTNLHandler rtnl_handler_;
88   ByteString expected_address_;
89 };
90 
SetUp()91 void ModemTest::SetUp() {
92   EXPECT_EQ(kService, modem_->service_);
93   EXPECT_EQ(kPath, modem_->path_);
94   ReplaceSingletons();
95   expected_address_ = ByteString(kAddress, arraysize(kAddress));
96 
97   EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(kLinkName)).
98       WillRepeatedly(Return(kTestInterfaceIndex));
99 
100   EXPECT_CALL(*modem_info_.mock_manager(), device_info())
101       .WillRepeatedly(Return(&device_info_));
102 }
103 
TearDown()104 void ModemTest::TearDown() {
105   modem_.reset();
106 }
107 
108 MATCHER_P2(HasPropertyWithValueU32, key, value, "") {
109   return arg.ContainsUint(key) && value == arg.GetUint(key);
110 }
111 
TEST_F(ModemTest,PendingDevicePropertiesAndCreate)112 TEST_F(ModemTest, PendingDevicePropertiesAndCreate) {
113   static const char kSentinel[] = "sentinel";
114   static const uint32_t kSentinelValue = 17;
115 
116   InterfaceToProperties properties;
117   properties[MM_MODEM_INTERFACE].SetUint(kSentinel, kSentinelValue);
118 
119   EXPECT_CALL(*modem_, GetLinkName(_, _)).WillRepeatedly(DoAll(
120       SetArgumentPointee<1>(string(kLinkName)),
121       Return(true)));
122   EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(StrEq(kLinkName))).
123       WillRepeatedly(Return(kTestInterfaceIndex));
124 
125   // The first time we call CreateDeviceFromModemProperties,
126   // GetMACAddress will fail.
127   EXPECT_CALL(device_info_, GetMACAddress(kTestInterfaceIndex, _)).
128       WillOnce(Return(false));
129   EXPECT_CALL(*modem_, GetModemInterface()).
130       WillRepeatedly(Return(MM_MODEM_INTERFACE));
131   modem_->CreateDeviceFromModemProperties(properties);
132   EXPECT_FALSE(modem_->device_.get());
133 
134   // On the second time, we allow GetMACAddress to succeed.  Now we
135   // expect a device to be built
136   EXPECT_CALL(device_info_, GetMACAddress(kTestInterfaceIndex, _)).
137       WillOnce(DoAll(SetArgumentPointee<1>(expected_address_),
138                      Return(true)));
139 
140   // modem will take ownership
141   MockCellular* cellular = new MockCellular(
142       &modem_info_,
143       kLinkName,
144       kAddressAsString,
145       kTestInterfaceIndex,
146       Cellular::kTypeCDMA,
147       kService,
148       kPath);
149 
150   EXPECT_CALL(*modem_,
151               ConstructCellular(StrEq(kLinkName),
152                                 StrEq(kAddressAsString),
153                                 kTestInterfaceIndex)).
154       WillOnce(Return(cellular));
155 
156   EXPECT_CALL(*cellular, OnPropertiesChanged(
157       _,
158       HasPropertyWithValueU32(kSentinel, kSentinelValue),
159       _));
160   EXPECT_CALL(device_info_, RegisterDevice(_));
161   modem_->OnDeviceInfoAvailable(kLinkName);
162 
163   EXPECT_TRUE(modem_->device_.get());
164 
165   // Add expectations for the eventual |modem_| destruction.
166   EXPECT_CALL(*cellular, DestroyService());
167   EXPECT_CALL(device_info_, DeregisterDevice(_));
168 }
169 
TEST_F(ModemTest,EarlyDeviceProperties)170 TEST_F(ModemTest, EarlyDeviceProperties) {
171   // OnDeviceInfoAvailable called before
172   // CreateDeviceFromModemProperties: Do nothing
173   modem_->OnDeviceInfoAvailable(kLinkName);
174   EXPECT_FALSE(modem_->device_.get());
175 }
176 
TEST_F(ModemTest,CreateDeviceEarlyFailures)177 TEST_F(ModemTest, CreateDeviceEarlyFailures) {
178   InterfaceToProperties properties;
179 
180   EXPECT_CALL(*modem_, ConstructCellular(_, _, _)).Times(0);
181   EXPECT_CALL(*modem_, GetModemInterface()).
182       WillRepeatedly(Return(MM_MODEM_INTERFACE));
183 
184   // No modem interface properties:  no device created
185   modem_->CreateDeviceFromModemProperties(properties);
186   EXPECT_FALSE(modem_->device_.get());
187 
188   properties[MM_MODEM_INTERFACE] = KeyValueStore();
189 
190   // Link name, but no ifindex: no device created
191   EXPECT_CALL(*modem_, GetLinkName(_, _)).WillOnce(DoAll(
192       SetArgumentPointee<1>(string(kLinkName)),
193       Return(true)));
194   EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(StrEq(kLinkName))).WillOnce(
195       Return(-1));
196   modem_->CreateDeviceFromModemProperties(properties);
197   EXPECT_FALSE(modem_->device_.get());
198 
199   // The params are good, but the device is blacklisted.
200   EXPECT_CALL(*modem_, GetLinkName(_, _)).WillOnce(DoAll(
201       SetArgumentPointee<1>(string(kLinkName)),
202       Return(true)));
203   EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(StrEq(kLinkName)))
204       .WillOnce(Return(kTestInterfaceIndex));
205   EXPECT_CALL(device_info_, GetMACAddress(kTestInterfaceIndex, _))
206       .WillOnce(DoAll(SetArgumentPointee<1>(expected_address_),
207                       Return(true)));
208   EXPECT_CALL(device_info_, IsDeviceBlackListed(kLinkName))
209       .WillRepeatedly(Return(true));
210   modem_->CreateDeviceFromModemProperties(properties);
211   EXPECT_FALSE(modem_->device_.get());
212 
213   // No link name: see CreateDevicePPP.
214 }
215 
TEST_F(ModemTest,CreateDevicePPP)216 TEST_F(ModemTest, CreateDevicePPP) {
217   InterfaceToProperties properties;
218   properties[MM_MODEM_INTERFACE] = KeyValueStore();
219 
220   string dev_name(
221       base::StringPrintf(Modem::kFakeDevNameFormat, Modem::fake_dev_serial_));
222 
223   // |modem_| will take ownership.
224   MockCellular* cellular = new MockCellular(
225       &modem_info_,
226       dev_name,
227       Modem::kFakeDevAddress,
228       Modem::kFakeDevInterfaceIndex,
229       Cellular::kTypeUniversal,
230       kService,
231       kPath);
232 
233   EXPECT_CALL(*modem_, GetModemInterface()).
234       WillRepeatedly(Return(MM_MODEM_INTERFACE));
235   // No link name: assumed to be a PPP dongle.
236   EXPECT_CALL(*modem_, GetLinkName(_, _)).WillOnce(Return(false));
237   EXPECT_CALL(*modem_,
238               ConstructCellular(dev_name,
239                                 StrEq(Modem::kFakeDevAddress),
240                                 Modem::kFakeDevInterfaceIndex)).
241       WillOnce(Return(cellular));
242   EXPECT_CALL(device_info_, RegisterDevice(_));
243 
244   modem_->CreateDeviceFromModemProperties(properties);
245   EXPECT_TRUE(modem_->device_.get());
246 
247   // Add expectations for the eventual |modem_| destruction.
248   EXPECT_CALL(*cellular, DestroyService());
249   EXPECT_CALL(device_info_, DeregisterDevice(_));
250 }
251 
TEST_F(ModemTest,GetDeviceParams)252 TEST_F(ModemTest, GetDeviceParams) {
253   string mac_address;
254   int interface_index = 2;
255   EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(_)).WillOnce(Return(-1));
256   EXPECT_CALL(device_info_, GetMACAddress(_, _)).Times(AnyNumber())
257       .WillRepeatedly(Return(false));
258   EXPECT_FALSE(modem_->GetDeviceParams(&mac_address, &interface_index));
259   EXPECT_EQ(-1, interface_index);
260 
261   EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(_)).WillOnce(Return(-2));
262   EXPECT_CALL(device_info_, GetMACAddress(_, _)).Times(AnyNumber())
263       .WillRepeatedly(Return(false));
264   EXPECT_FALSE(modem_->GetDeviceParams(&mac_address, &interface_index));
265   EXPECT_EQ(-2, interface_index);
266 
267   EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(_)).WillOnce(Return(1));
268   EXPECT_CALL(device_info_, GetMACAddress(_, _)).WillOnce(Return(false));
269   EXPECT_FALSE(modem_->GetDeviceParams(&mac_address, &interface_index));
270   EXPECT_EQ(1, interface_index);
271 
272   EXPECT_CALL(rtnl_handler_, GetInterfaceIndex(_)).WillOnce(Return(2));
273   EXPECT_CALL(device_info_, GetMACAddress(2, _)).
274       WillOnce(DoAll(SetArgumentPointee<1>(expected_address_),
275                      Return(true)));
276   EXPECT_TRUE(modem_->GetDeviceParams(&mac_address, &interface_index));
277   EXPECT_EQ(2, interface_index);
278   EXPECT_EQ(kAddressAsString, mac_address);
279 }
280 
TEST_F(ModemTest,RejectPPPModem)281 TEST_F(ModemTest, RejectPPPModem) {
282   // TODO(rochberg):  Port this to ModemClassic
283 }
284 
285 }  // namespace shill
286