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/cellular_capability_classic.h"
18 
19 #include <base/bind.h>
20 #if defined(__ANDROID__)
21 #include <dbus/service_constants.h>
22 #else
23 #include <chromeos/dbus/service_constants.h>
24 #endif  // __ANDROID__
25 
26 #include "shill/cellular/cellular.h"
27 #include "shill/cellular/modem_gobi_proxy_interface.h"
28 #include "shill/control_interface.h"
29 #include "shill/error.h"
30 #include "shill/logging.h"
31 #include "shill/property_accessor.h"
32 
33 using base::Bind;
34 using base::Callback;
35 using base::Closure;
36 using std::string;
37 
38 namespace shill {
39 
40 namespace Logging {
41 static auto kModuleLogScope = ScopeLogger::kCellular;
ObjectID(CellularCapabilityClassic * c)42 static string ObjectID(CellularCapabilityClassic* c) {
43   return c->cellular()->GetRpcIdentifier();
44 }
45 }
46 
47 const char CellularCapabilityClassic::kConnectPropertyApn[] = "apn";
48 const char CellularCapabilityClassic::kConnectPropertyApnUsername[] =
49     "username";
50 const char CellularCapabilityClassic::kConnectPropertyApnPassword[] =
51     "password";
52 const char CellularCapabilityClassic::kConnectPropertyHomeOnly[] = "home_only";
53 const char CellularCapabilityClassic::kConnectPropertyPhoneNumber[] = "number";
54 const char CellularCapabilityClassic::kModemPropertyEnabled[] = "Enabled";
55 const int CellularCapabilityClassic::kTimeoutSetCarrierMilliseconds = 120000;
56 
ConvertClassicToModemState(uint32_t classic_state)57 static Cellular::ModemState ConvertClassicToModemState(uint32_t classic_state) {
58   ModemClassicState cstate = static_cast<ModemClassicState>(classic_state);
59   switch (cstate) {
60     case kModemClassicStateUnknown:
61       return Cellular::kModemStateUnknown;
62     case kModemClassicStateDisabled:
63       return Cellular::kModemStateDisabled;
64     case kModemClassicStateDisabling:
65       return Cellular::kModemStateDisabling;
66     case kModemClassicStateEnabling:
67       return Cellular::kModemStateEnabling;
68     case kModemClassicStateEnabled:
69       return Cellular::kModemStateEnabled;
70     case kModemClassicStateSearching:
71       return Cellular::kModemStateSearching;
72     case kModemClassicStateRegistered:
73       return Cellular::kModemStateRegistered;
74     case kModemClassicStateDisconnecting:
75       return Cellular::kModemStateDisconnecting;
76     case kModemClassicStateConnecting:
77       return Cellular::kModemStateConnecting;
78     case kModemClassicStateConnected:
79       return Cellular::kModemStateConnected;
80     default:
81       return Cellular::kModemStateUnknown;
82   }
83 }
84 
CellularCapabilityClassic(Cellular * cellular,ControlInterface * control_interface,ModemInfo * modem_info)85 CellularCapabilityClassic::CellularCapabilityClassic(
86     Cellular* cellular,
87     ControlInterface* control_interface,
88     ModemInfo* modem_info)
89     : CellularCapability(cellular, control_interface, modem_info),
90       weak_ptr_factory_(this) {
91   // This class is currently instantiated only for Gobi modems so setup the
92   // supported carriers list appropriately and expose it over RPC.
93   cellular->set_supported_carriers({kCarrierGenericUMTS,
94                                     kCarrierSprint,
95                                     kCarrierVerizon});
96 }
97 
~CellularCapabilityClassic()98 CellularCapabilityClassic::~CellularCapabilityClassic() {}
99 
InitProxies()100 void CellularCapabilityClassic::InitProxies() {
101   SLOG(this, 2) << __func__;
102   proxy_.reset(control_interface()->CreateModemProxy(
103       cellular()->dbus_path(), cellular()->dbus_service()));
104   simple_proxy_.reset(control_interface()->CreateModemSimpleProxy(
105       cellular()->dbus_path(), cellular()->dbus_service()));
106   proxy_->set_state_changed_callback(
107       Bind(&CellularCapabilityClassic::OnModemStateChangedSignal,
108            weak_ptr_factory_.GetWeakPtr()));
109 }
110 
ReleaseProxies()111 void CellularCapabilityClassic::ReleaseProxies() {
112   SLOG(this, 2) << __func__;
113   proxy_.reset();
114   simple_proxy_.reset();
115   gobi_proxy_.reset();
116 }
117 
AreProxiesInitialized() const118 bool CellularCapabilityClassic::AreProxiesInitialized() const {
119   return (proxy_.get() && simple_proxy_.get() && gobi_proxy_.get());
120 }
121 
FinishEnable(const ResultCallback & callback)122 void CellularCapabilityClassic::FinishEnable(const ResultCallback& callback) {
123   // Normally, running the callback is the last thing done in a method.
124   // In this case, we do it first, because we want to make sure that
125   // the device is marked as Enabled before the registration state is
126   // handled. See comment in Cellular::HandleNewRegistrationState.
127   callback.Run(Error());
128   GetRegistrationState();
129   GetSignalQuality();
130   // We expect the modem to start scanning after it has been enabled.
131   // Change this if this behavior is no longer the case in the future.
132   modem_info()->metrics()->NotifyDeviceEnableFinished(
133       cellular()->interface_index());
134   modem_info()->metrics()->NotifyDeviceScanStarted(
135       cellular()->interface_index());
136 }
137 
FinishDisable(const ResultCallback & callback)138 void CellularCapabilityClassic::FinishDisable(const ResultCallback& callback) {
139   modem_info()->metrics()->NotifyDeviceDisableFinished(
140       cellular()->interface_index());
141   ReleaseProxies();
142   callback.Run(Error());
143 }
144 
RunNextStep(CellularTaskList * tasks)145 void CellularCapabilityClassic::RunNextStep(CellularTaskList* tasks) {
146   CHECK(!tasks->empty());
147   SLOG(this, 2) << __func__ << ": " << tasks->size() << " remaining tasks";
148   Closure task = (*tasks)[0];
149   tasks->erase(tasks->begin());
150   cellular()->dispatcher()->PostTask(task);
151 }
152 
StepCompletedCallback(const ResultCallback & callback,bool ignore_error,CellularTaskList * tasks,const Error & error)153 void CellularCapabilityClassic::StepCompletedCallback(
154     const ResultCallback& callback,
155     bool ignore_error,
156     CellularTaskList* tasks,
157     const Error& error) {
158   if ((ignore_error || error.IsSuccess()) && !tasks->empty()) {
159     RunNextStep(tasks);
160     return;
161   }
162   delete tasks;
163   if (!callback.is_null())
164     callback.Run(error);
165 }
166 
167 // always called from an async context
EnableModem(const ResultCallback & callback)168 void CellularCapabilityClassic::EnableModem(const ResultCallback& callback) {
169   SLOG(this, 2) << __func__;
170   CHECK(!callback.is_null());
171   Error error;
172   modem_info()->metrics()->NotifyDeviceEnableStarted(
173       cellular()->interface_index());
174   proxy_->Enable(true, &error, callback, kTimeoutEnable);
175   if (error.IsFailure())
176     callback.Run(error);
177 }
178 
179 // always called from an async context
DisableModem(const ResultCallback & callback)180 void CellularCapabilityClassic::DisableModem(const ResultCallback& callback) {
181   SLOG(this, 2) << __func__;
182   CHECK(!callback.is_null());
183   Error error;
184   modem_info()->metrics()->NotifyDeviceDisableStarted(
185       cellular()->interface_index());
186   proxy_->Enable(false, &error, callback, kTimeoutEnable);
187   if (error.IsFailure())
188       callback.Run(error);
189 }
190 
191 // always called from an async context
GetModemStatus(const ResultCallback & callback)192 void CellularCapabilityClassic::GetModemStatus(const ResultCallback& callback) {
193   SLOG(this, 2) << __func__;
194   CHECK(!callback.is_null());
195   KeyValueStoreCallback cb = Bind(
196       &CellularCapabilityClassic::OnGetModemStatusReply,
197       weak_ptr_factory_.GetWeakPtr(), callback);
198   Error error;
199   simple_proxy_->GetModemStatus(&error, cb, kTimeoutDefault);
200   if (error.IsFailure())
201       callback.Run(error);
202 }
203 
204 // always called from an async context
GetModemInfo(const ResultCallback & callback)205 void CellularCapabilityClassic::GetModemInfo(const ResultCallback& callback) {
206   SLOG(this, 2) << __func__;
207   CHECK(!callback.is_null());
208   ModemInfoCallback cb = Bind(&CellularCapabilityClassic::OnGetModemInfoReply,
209                               weak_ptr_factory_.GetWeakPtr(), callback);
210   Error error;
211   proxy_->GetModemInfo(&error, cb, kTimeoutDefault);
212   if (error.IsFailure())
213       callback.Run(error);
214 }
215 
StopModem(Error * error,const ResultCallback & callback)216 void CellularCapabilityClassic::StopModem(Error* error,
217                                           const ResultCallback& callback) {
218   SLOG(this, 2) << __func__;
219 
220   CellularTaskList* tasks = new CellularTaskList();
221   ResultCallback cb =
222       Bind(&CellularCapabilityClassic::StepCompletedCallback,
223            weak_ptr_factory_.GetWeakPtr(), callback, false, tasks);
224   ResultCallback cb_ignore_error =
225       Bind(&CellularCapabilityClassic::StepCompletedCallback,
226            weak_ptr_factory_.GetWeakPtr(), callback, true, tasks);
227   // TODO(ers): We can skip the call to Disconnect if the modem has
228   // told us that the modem state is Disabled or Registered.
229   tasks->push_back(Bind(&CellularCapabilityClassic::Disconnect,
230                         weak_ptr_factory_.GetWeakPtr(), nullptr,
231                         cb_ignore_error));
232   // TODO(ers): We can skip the call to Disable if the modem has
233   // told us that the modem state is Disabled.
234   tasks->push_back(Bind(&CellularCapabilityClassic::DisableModem,
235                         weak_ptr_factory_.GetWeakPtr(), cb));
236   tasks->push_back(Bind(&CellularCapabilityClassic::FinishDisable,
237                         weak_ptr_factory_.GetWeakPtr(), cb));
238 
239   RunNextStep(tasks);
240 }
241 
Connect(const KeyValueStore & properties,Error * error,const ResultCallback & callback)242 void CellularCapabilityClassic::Connect(const KeyValueStore& properties,
243                                         Error* error,
244                                         const ResultCallback& callback) {
245   SLOG(this, 2) << __func__;
246   simple_proxy_->Connect(properties, error, callback, kTimeoutConnect);
247 }
248 
Disconnect(Error * error,const ResultCallback & callback)249 void CellularCapabilityClassic::Disconnect(Error* error,
250                                            const ResultCallback& callback) {
251   SLOG(this, 2) << __func__;
252   if (proxy_.get())
253     proxy_->Disconnect(error, callback, kTimeoutDisconnect);
254   else
255     LOG(ERROR) << "No proxy found in disconnect.";
256 }
257 
SetCarrier(const string & carrier,Error * error,const ResultCallback & callback)258 void CellularCapabilityClassic::SetCarrier(const string& carrier,
259                                            Error* error,
260                                            const ResultCallback& callback) {
261   LOG(INFO) << __func__ << "(" << carrier << ")";
262   if (!gobi_proxy_.get()) {
263     gobi_proxy_.reset(control_interface()->CreateModemGobiProxy(
264         cellular()->dbus_path(), cellular()->dbus_service()));
265   }
266   CHECK(error);
267   gobi_proxy_->SetCarrier(carrier, error, callback,
268                           kTimeoutSetCarrierMilliseconds);
269 }
270 
OnPropertiesChanged(const std::string & interface,const KeyValueStore & changed_properties,const std::vector<std::string> & invalidated_properties)271 void CellularCapabilityClassic::OnPropertiesChanged(
272     const std::string& interface,
273     const KeyValueStore& changed_properties,
274     const std::vector<std::string>& invalidated_properties) {
275   SLOG(this, 2) << __func__;
276   // This solves a bootstrapping problem: If the modem is not yet
277   // enabled, there are no proxy objects associated with the capability
278   // object, so modem signals like StateChanged aren't seen. By monitoring
279   // changes to the Enabled property via the ModemManager, we're able to
280   // get the initialization process started, which will result in the
281   // creation of the proxy objects.
282   //
283   // We handle all state changes to ENABLED from a disabled state (including,
284   // UNKNOWN) through Cellular::OnModemStateChanged. This will try to enable
285   // the device regardless of whether it has been registered with the Manager.
286   //
287   // All other state changes are handled from OnModemStateChangedSignal.
288   if (changed_properties.ContainsBool(kModemPropertyEnabled)) {
289     bool enabled = changed_properties.GetBool(kModemPropertyEnabled);
290     SLOG(this, 2) << "Property \"Enabled\" changed: " << enabled;
291     Cellular::ModemState prev_modem_state = cellular()->modem_state();
292     if (!Cellular::IsEnabledModemState(prev_modem_state)) {
293       cellular()->OnModemStateChanged(
294           enabled ? Cellular::kModemStateEnabled :
295                     Cellular::kModemStateDisabled);
296     }
297   }
298 }
299 
OnGetModemStatusReply(const ResultCallback & callback,const KeyValueStore & props,const Error & error)300 void CellularCapabilityClassic::OnGetModemStatusReply(
301     const ResultCallback& callback,
302     const KeyValueStore& props,
303     const Error& error) {
304   SLOG(this, 2) << __func__ << " error " << error;
305   if (error.IsSuccess()) {
306     if (props.ContainsString("carrier")) {
307       string carrier = props.GetString("carrier");
308       cellular()->set_carrier(carrier);
309       cellular()->home_provider_info()->UpdateOperatorName(carrier);
310     }
311     if (props.ContainsString("meid")) {
312       cellular()->set_meid(props.GetString("meid"));
313     }
314     if (props.ContainsString("imei")) {
315      cellular()->set_imei(props.GetString("imei"));
316     }
317     if (props.ContainsString(kModemPropertyIMSI)) {
318       string imsi = props.GetString(kModemPropertyIMSI);
319       cellular()->set_imsi(imsi);
320       cellular()->home_provider_info()->UpdateIMSI(imsi);
321       // We do not currently obtain the IMSI OTA at all. Provide the IMSI from
322       // the SIM to the serving operator as well to aid in MVNO identification.
323       cellular()->serving_operator_info()->UpdateIMSI(imsi);
324     }
325     if (props.ContainsString("esn")) {
326       cellular()->set_esn(props.GetString("esn"));
327     }
328     if (props.ContainsString("mdn")) {
329       cellular()->set_mdn(props.GetString("mdn"));
330     }
331     if (props.ContainsString("min")) {
332       cellular()->set_min(props.GetString("min"));
333     }
334     if (props.ContainsString("firmware_revision")) {
335       cellular()->set_firmware_revision(props.GetString("firmware_revision"));
336     }
337     UpdateStatus(props);
338   }
339   callback.Run(error);
340 }
341 
UpdateStatus(const KeyValueStore & properties)342 void CellularCapabilityClassic::UpdateStatus(
343     const KeyValueStore& properties) {
344   SLOG(this, 3) << __func__;
345 }
346 
OnGetModemInfoReply(const ResultCallback & callback,const std::string & manufacturer,const std::string & modem,const std::string & version,const Error & error)347 void CellularCapabilityClassic::OnGetModemInfoReply(
348     const ResultCallback& callback,
349     const std::string& manufacturer,
350     const std::string& modem,
351     const std::string& version,
352     const Error& error) {
353   SLOG(this, 2) << __func__ << "(" << error << ")";
354   if (error.IsSuccess()) {
355     cellular()->set_manufacturer(manufacturer);
356     cellular()->set_model_id(modem);
357     cellular()->set_hardware_revision(version);
358     SLOG(this, 2) << __func__ << ": " << manufacturer << ", " << modem << ", "
359                   << version;
360   }
361   callback.Run(error);
362 }
363 
OnModemStateChangedSignal(uint32_t old_state,uint32_t new_state,uint32_t reason)364 void CellularCapabilityClassic::OnModemStateChangedSignal(
365     uint32_t old_state, uint32_t new_state, uint32_t reason) {
366   SLOG(this, 2) << __func__ << "(" << old_state << ", " << new_state << ", "
367                 << reason << ")";
368   cellular()->OnModemStateChanged(ConvertClassicToModemState(new_state));
369 }
370 
371 }  // namespace shill
372