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_gsm.h"
18 
19 #include <string>
20 #include <vector>
21 
22 #include <base/bind.h>
23 #include <base/stl_util.h>
24 #include <base/strings/string_number_conversions.h>
25 #include <base/strings/stringprintf.h>
26 #if defined(__ANDROID__)
27 #include <dbus/service_constants.h>
28 #else
29 #include <chromeos/dbus/service_constants.h>
30 #endif  // __ANDROID__
31 #include <mm/mm-modem.h>
32 
33 #include "shill/adaptor_interfaces.h"
34 #include "shill/cellular/cellular_service.h"
35 #include "shill/control_interface.h"
36 #include "shill/error.h"
37 #include "shill/logging.h"
38 #include "shill/property_accessor.h"
39 
40 using base::Bind;
41 using std::string;
42 using std::vector;
43 
44 namespace shill {
45 
46 namespace Logging {
47 static auto kModuleLogScope = ScopeLogger::kCellular;
ObjectID(CellularCapabilityGSM * c)48 static string ObjectID(CellularCapabilityGSM* c) {
49   return c->cellular()->GetRpcIdentifier();
50 }
51 }
52 
53 // static
54 const char CellularCapabilityGSM::kNetworkPropertyAccessTechnology[] =
55     "access-tech";
56 const char CellularCapabilityGSM::kNetworkPropertyID[] = "operator-num";
57 const char CellularCapabilityGSM::kNetworkPropertyLongName[] = "operator-long";
58 const char CellularCapabilityGSM::kNetworkPropertyShortName[] =
59     "operator-short";
60 const char CellularCapabilityGSM::kNetworkPropertyStatus[] = "status";
61 const char CellularCapabilityGSM::kPhoneNumber[] = "*99#";
62 const char CellularCapabilityGSM::kPropertyAccessTechnology[] =
63     "AccessTechnology";
64 const char CellularCapabilityGSM::kPropertyEnabledFacilityLocks[] =
65     "EnabledFacilityLocks";
66 const char CellularCapabilityGSM::kPropertyUnlockRequired[] = "UnlockRequired";
67 const char CellularCapabilityGSM::kPropertyUnlockRetries[] = "UnlockRetries";
68 
69 const int CellularCapabilityGSM::kGetIMSIRetryLimit = 40;
70 const int64_t CellularCapabilityGSM::kGetIMSIRetryDelayMilliseconds = 500;
71 
72 
CellularCapabilityGSM(Cellular * cellular,ControlInterface * control_interface,ModemInfo * modem_info)73 CellularCapabilityGSM::CellularCapabilityGSM(
74     Cellular* cellular,
75     ControlInterface* control_interface,
76     ModemInfo* modem_info)
77     : CellularCapabilityClassic(cellular, control_interface, modem_info),
78       weak_ptr_factory_(this),
79       mobile_operator_info_(new MobileOperatorInfo(cellular->dispatcher(),
80                                                    "ParseScanResult")),
81       registration_state_(MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN),
82       access_technology_(MM_MODEM_GSM_ACCESS_TECH_UNKNOWN),
83       home_provider_info_(nullptr),
84       get_imsi_retries_(0),
85       get_imsi_retry_delay_milliseconds_(kGetIMSIRetryDelayMilliseconds) {
86   SLOG(this, 2) << "Cellular capability constructed: GSM";
87   mobile_operator_info_->Init();
88   HelpRegisterConstDerivedKeyValueStore(
89       kSIMLockStatusProperty, &CellularCapabilityGSM::SimLockStatusToProperty);
90   this->cellular()->set_scanning_supported(true);
91 
92   // TODO(benchan): This is a hack to initialize the GSM card proxy for GetIMSI
93   // before InitProxies is called. There are side-effects of calling InitProxies
94   // before the device is enabled. It's better to refactor InitProxies such that
95   // proxies can be created when the cellular device/capability is constructed,
96   // but callbacks for DBus signal updates are not set up until the device is
97   // enabled.
98   card_proxy_.reset(
99       control_interface->CreateModemGSMCardProxy(cellular->dbus_path(),
100                                                  cellular->dbus_service()));
101   // TODO(benchan): To allow unit testing using a mock proxy without further
102   // complicating the code, the test proxy factory is set up to return a nullptr
103   // pointer when CellularCapabilityGSM is constructed. Refactor the code to
104   // avoid this hack.
105   if (card_proxy_.get())
106     InitProperties();
107 }
108 
~CellularCapabilityGSM()109 CellularCapabilityGSM::~CellularCapabilityGSM() {}
110 
GetTypeString() const111 string CellularCapabilityGSM::GetTypeString() const {
112   return kTechnologyFamilyGsm;
113 }
114 
SimLockStatusToProperty(Error *)115 KeyValueStore CellularCapabilityGSM::SimLockStatusToProperty(Error* /*error*/) {
116   KeyValueStore status;
117   status.SetBool(kSIMLockEnabledProperty, sim_lock_status_.enabled);
118   status.SetString(kSIMLockTypeProperty, sim_lock_status_.lock_type);
119   status.SetUint(kSIMLockRetriesLeftProperty, sim_lock_status_.retries_left);
120   return status;
121 }
122 
HelpRegisterConstDerivedKeyValueStore(const string & name,KeyValueStore (CellularCapabilityGSM::* get)(Error * error))123 void CellularCapabilityGSM::HelpRegisterConstDerivedKeyValueStore(
124     const string& name,
125     KeyValueStore(CellularCapabilityGSM::*get)(Error* error)) {
126   cellular()->mutable_store()->RegisterDerivedKeyValueStore(
127       name,
128       KeyValueStoreAccessor(
129           new CustomAccessor<CellularCapabilityGSM, KeyValueStore>(
130               this, get, nullptr)));
131 }
132 
InitProxies()133 void CellularCapabilityGSM::InitProxies() {
134   CellularCapabilityClassic::InitProxies();
135   // TODO(benchan): Remove this check after refactoring the proxy
136   // initialization.
137   if (!card_proxy_.get()) {
138     card_proxy_.reset(
139         control_interface()->CreateModemGSMCardProxy(
140             cellular()->dbus_path(), cellular()->dbus_service()));
141   }
142   network_proxy_.reset(
143       control_interface()->CreateModemGSMNetworkProxy(
144           cellular()->dbus_path(), cellular()->dbus_service()));
145   network_proxy_->set_signal_quality_callback(
146       Bind(&CellularCapabilityGSM::OnSignalQualitySignal,
147            weak_ptr_factory_.GetWeakPtr()));
148   network_proxy_->set_network_mode_callback(
149       Bind(&CellularCapabilityGSM::OnNetworkModeSignal,
150            weak_ptr_factory_.GetWeakPtr()));
151   network_proxy_->set_registration_info_callback(
152       Bind(&CellularCapabilityGSM::OnRegistrationInfoSignal,
153            weak_ptr_factory_.GetWeakPtr()));
154 }
155 
InitProperties()156 void CellularCapabilityGSM::InitProperties() {
157   CellularTaskList* tasks = new CellularTaskList();
158   ResultCallback cb_ignore_error =
159       Bind(&CellularCapabilityGSM::StepCompletedCallback,
160            weak_ptr_factory_.GetWeakPtr(), ResultCallback(), true, tasks);
161   // Chrome checks if a SIM is present before allowing the modem to be enabled,
162   // so shill needs to obtain IMSI, as an indicator of SIM presence, even
163   // before the device is enabled.
164   tasks->push_back(Bind(&CellularCapabilityGSM::GetIMSI,
165                         weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
166   RunNextStep(tasks);
167 }
168 
StartModem(Error * error,const ResultCallback & callback)169 void CellularCapabilityGSM::StartModem(Error* error,
170                                        const ResultCallback& callback) {
171   InitProxies();
172 
173   CellularTaskList* tasks = new CellularTaskList();
174   ResultCallback cb =
175       Bind(&CellularCapabilityGSM::StepCompletedCallback,
176            weak_ptr_factory_.GetWeakPtr(), callback, false, tasks);
177   ResultCallback cb_ignore_error =
178         Bind(&CellularCapabilityGSM::StepCompletedCallback,
179                    weak_ptr_factory_.GetWeakPtr(), callback, true, tasks);
180   if (!cellular()->IsUnderlyingDeviceEnabled())
181     tasks->push_back(Bind(&CellularCapabilityGSM::EnableModem,
182                           weak_ptr_factory_.GetWeakPtr(), cb));
183   // If we're within range of the home network, the modem will try to
184   // register once it's enabled, or may be already registered if we
185   // started out enabled.
186   if (!IsUnderlyingDeviceRegistered() &&
187       !cellular()->selected_network().empty())
188     tasks->push_back(Bind(&CellularCapabilityGSM::Register,
189                           weak_ptr_factory_.GetWeakPtr(), cb));
190   tasks->push_back(Bind(&CellularCapabilityGSM::GetIMEI,
191                         weak_ptr_factory_.GetWeakPtr(), cb));
192   get_imsi_retries_ = 0;
193   tasks->push_back(Bind(&CellularCapabilityGSM::GetIMSI,
194                         weak_ptr_factory_.GetWeakPtr(), cb));
195   tasks->push_back(Bind(&CellularCapabilityGSM::GetSPN,
196                         weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
197   tasks->push_back(Bind(&CellularCapabilityGSM::GetMSISDN,
198                         weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
199   tasks->push_back(Bind(&CellularCapabilityGSM::GetProperties,
200                         weak_ptr_factory_.GetWeakPtr(), cb));
201   tasks->push_back(Bind(&CellularCapabilityGSM::GetModemInfo,
202                         weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
203   tasks->push_back(Bind(&CellularCapabilityGSM::FinishEnable,
204                         weak_ptr_factory_.GetWeakPtr(), cb));
205 
206   RunNextStep(tasks);
207 }
208 
IsUnderlyingDeviceRegistered() const209 bool CellularCapabilityGSM::IsUnderlyingDeviceRegistered() const {
210   switch (cellular()->modem_state()) {
211     case Cellular::kModemStateFailed:
212     case Cellular::kModemStateUnknown:
213     case Cellular::kModemStateDisabled:
214     case Cellular::kModemStateInitializing:
215     case Cellular::kModemStateLocked:
216     case Cellular::kModemStateDisabling:
217     case Cellular::kModemStateEnabling:
218     case Cellular::kModemStateEnabled:
219       return false;
220     case Cellular::kModemStateSearching:
221     case Cellular::kModemStateRegistered:
222     case Cellular::kModemStateDisconnecting:
223     case Cellular::kModemStateConnecting:
224     case Cellular::kModemStateConnected:
225       return true;
226   }
227   return false;
228 }
229 
ReleaseProxies()230 void CellularCapabilityGSM::ReleaseProxies() {
231   SLOG(this, 2) << __func__;
232   CellularCapabilityClassic::ReleaseProxies();
233   card_proxy_.reset();
234   network_proxy_.reset();
235 }
236 
AreProxiesInitialized() const237 bool CellularCapabilityGSM::AreProxiesInitialized() const {
238   return (CellularCapabilityClassic::AreProxiesInitialized() &&
239           card_proxy_.get() && network_proxy_.get());
240 }
241 
OnServiceCreated()242 void CellularCapabilityGSM::OnServiceCreated() {
243   cellular()->service()->SetActivationState(kActivationStateActivated);
244 }
245 
246 // Create the list of APNs to try, in the following order:
247 // - last APN that resulted in a successful connection attempt on the
248 //   current network (if any)
249 // - the APN, if any, that was set by the user
250 // - the list of APNs found in the mobile broadband provider DB for the
251 //   home provider associated with the current SIM
252 // - as a last resort, attempt to connect with no APN
SetupApnTryList()253 void CellularCapabilityGSM::SetupApnTryList() {
254   apn_try_list_.clear();
255 
256   DCHECK(cellular()->service().get());
257   const Stringmap* apn_info = cellular()->service()->GetLastGoodApn();
258   if (apn_info)
259     apn_try_list_.push_back(*apn_info);
260 
261   apn_info = cellular()->service()->GetUserSpecifiedApn();
262   if (apn_info)
263     apn_try_list_.push_back(*apn_info);
264 
265   apn_try_list_.insert(apn_try_list_.end(),
266                        cellular()->apn_list().begin(),
267                        cellular()->apn_list().end());
268 }
269 
SetupConnectProperties(KeyValueStore * properties)270 void CellularCapabilityGSM::SetupConnectProperties(
271     KeyValueStore* properties) {
272   SetupApnTryList();
273   FillConnectPropertyMap(properties);
274 }
275 
FillConnectPropertyMap(KeyValueStore * properties)276 void CellularCapabilityGSM::FillConnectPropertyMap(
277     KeyValueStore* properties) {
278   properties->SetString(kConnectPropertyPhoneNumber, kPhoneNumber);
279 
280   if (!AllowRoaming())
281     properties->SetBool(kConnectPropertyHomeOnly, true);
282 
283   if (!apn_try_list_.empty()) {
284     // Leave the APN at the front of the list, so that it can be recorded
285     // if the connect attempt succeeds.
286     Stringmap apn_info = apn_try_list_.front();
287     SLOG(this, 2) << __func__ << ": Using APN " << apn_info[kApnProperty];
288     properties->SetString(kConnectPropertyApn, apn_info[kApnProperty]);
289     if (ContainsKey(apn_info, kApnUsernameProperty))
290       properties->SetString(kConnectPropertyApnUsername,
291                             apn_info[kApnUsernameProperty]);
292     if (ContainsKey(apn_info, kApnPasswordProperty))
293       properties->SetString(kConnectPropertyApnPassword,
294                             apn_info[kApnPasswordProperty]);
295   }
296 }
297 
Connect(const KeyValueStore & properties,Error * error,const ResultCallback & callback)298 void CellularCapabilityGSM::Connect(const KeyValueStore& properties,
299                                     Error* error,
300                                     const ResultCallback& callback) {
301   SLOG(this, 2) << __func__;
302   ResultCallback cb = Bind(&CellularCapabilityGSM::OnConnectReply,
303                            weak_ptr_factory_.GetWeakPtr(),
304                            callback);
305   simple_proxy_->Connect(properties, error, cb, kTimeoutConnect);
306 }
307 
OnConnectReply(const ResultCallback & callback,const Error & error)308 void CellularCapabilityGSM::OnConnectReply(const ResultCallback& callback,
309                                            const Error& error) {
310   CellularServiceRefPtr service = cellular()->service();
311   if (!service) {
312     // The service could have been deleted before our Connect() request
313     // completes if the modem was enabled and then quickly disabled.
314     apn_try_list_.clear();
315   } else if (error.IsFailure()) {
316     service->ClearLastGoodApn();
317     // The APN that was just tried (and failed) is still at the
318     // front of the list, about to be removed. If the list is empty
319     // after that, try one last time without an APN. This may succeed
320     // with some modems in some cases.
321     if (error.type() == Error::kInvalidApn && !apn_try_list_.empty()) {
322       apn_try_list_.pop_front();
323       SLOG(this, 2) << "Connect failed with invalid APN, "
324                     << apn_try_list_.size() << " remaining APNs to try";
325       KeyValueStore props;
326       FillConnectPropertyMap(&props);
327       Error error;
328       Connect(props, &error, callback);
329       return;
330     }
331   } else if (!apn_try_list_.empty()) {
332     service->SetLastGoodApn(apn_try_list_.front());
333     apn_try_list_.clear();
334   }
335   if (!callback.is_null())
336     callback.Run(error);
337 }
338 
AllowRoaming()339 bool CellularCapabilityGSM::AllowRoaming() {
340   return cellular()->provider_requires_roaming() || allow_roaming_property();
341 }
342 
343 // always called from an async context
GetIMEI(const ResultCallback & callback)344 void CellularCapabilityGSM::GetIMEI(const ResultCallback& callback) {
345   SLOG(this, 2) << __func__;
346   CHECK(!callback.is_null());
347   Error error;
348   if (cellular()->imei().empty()) {
349     GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetIMEIReply,
350                                     weak_ptr_factory_.GetWeakPtr(), callback);
351     card_proxy_->GetIMEI(&error, cb, kTimeoutDefault);
352     if (error.IsFailure())
353       callback.Run(error);
354   } else {
355     SLOG(this, 2) << "Already have IMEI " << cellular()->imei();
356     callback.Run(error);
357   }
358 }
359 
360 // always called from an async context
GetIMSI(const ResultCallback & callback)361 void CellularCapabilityGSM::GetIMSI(const ResultCallback& callback) {
362   SLOG(this, 2) << __func__;
363   CHECK(!callback.is_null());
364   Error error;
365   if (cellular()->imsi().empty()) {
366     GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetIMSIReply,
367                                     weak_ptr_factory_.GetWeakPtr(),
368                                     callback);
369     card_proxy_->GetIMSI(&error, cb, kTimeoutDefault);
370     if (error.IsFailure()) {
371       cellular()->home_provider_info()->Reset();
372       callback.Run(error);
373     }
374   } else {
375     SLOG(this, 2) << "Already have IMSI " << cellular()->imsi();
376     callback.Run(error);
377   }
378 }
379 
380 // always called from an async context
GetSPN(const ResultCallback & callback)381 void CellularCapabilityGSM::GetSPN(const ResultCallback& callback) {
382   SLOG(this, 2) << __func__;
383   CHECK(!callback.is_null());
384   Error error;
385   if (spn_.empty()) {
386     GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetSPNReply,
387                                     weak_ptr_factory_.GetWeakPtr(),
388                                     callback);
389     card_proxy_->GetSPN(&error, cb, kTimeoutDefault);
390     if (error.IsFailure())
391       callback.Run(error);
392   } else {
393     SLOG(this, 2) << "Already have SPN " << spn_;
394     callback.Run(error);
395   }
396 }
397 
398 // always called from an async context
GetMSISDN(const ResultCallback & callback)399 void CellularCapabilityGSM::GetMSISDN(const ResultCallback& callback) {
400   SLOG(this, 2) << __func__;
401   CHECK(!callback.is_null());
402   Error error;
403   string mdn = cellular()->mdn();
404   if (mdn.empty()) {
405     GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetMSISDNReply,
406                                     weak_ptr_factory_.GetWeakPtr(),
407                                     callback);
408     card_proxy_->GetMSISDN(&error, cb, kTimeoutDefault);
409     if (error.IsFailure())
410       callback.Run(error);
411   } else {
412     SLOG(this, 2) << "Already have MSISDN " << mdn;
413     callback.Run(error);
414   }
415 }
416 
GetSignalQuality()417 void CellularCapabilityGSM::GetSignalQuality() {
418   SLOG(this, 2) << __func__;
419   SignalQualityCallback callback =
420       Bind(&CellularCapabilityGSM::OnGetSignalQualityReply,
421            weak_ptr_factory_.GetWeakPtr());
422   network_proxy_->GetSignalQuality(nullptr, callback, kTimeoutDefault);
423 }
424 
GetRegistrationState()425 void CellularCapabilityGSM::GetRegistrationState() {
426   SLOG(this, 2) << __func__;
427   RegistrationInfoCallback callback =
428       Bind(&CellularCapabilityGSM::OnGetRegistrationInfoReply,
429            weak_ptr_factory_.GetWeakPtr());
430   network_proxy_->GetRegistrationInfo(nullptr, callback, kTimeoutDefault);
431 }
432 
GetProperties(const ResultCallback & callback)433 void CellularCapabilityGSM::GetProperties(const ResultCallback& callback) {
434   SLOG(this, 2) << __func__;
435 
436   // TODO(petkov): Switch to asynchronous calls (crbug.com/200687).
437   uint32_t tech = network_proxy_->AccessTechnology();
438   SetAccessTechnology(tech);
439   SLOG(this, 2) << "GSM AccessTechnology: " << tech;
440 
441   // TODO(petkov): Switch to asynchronous calls (crbug.com/200687).
442   uint32_t locks = card_proxy_->EnabledFacilityLocks();
443   sim_lock_status_.enabled = locks & MM_MODEM_GSM_FACILITY_SIM;
444   SLOG(this, 2) << "GSM EnabledFacilityLocks: " << locks;
445 
446   callback.Run(Error());
447 }
448 
449 // always called from an async context
Register(const ResultCallback & callback)450 void CellularCapabilityGSM::Register(const ResultCallback& callback) {
451   SLOG(this, 2) << __func__ << " \"" << cellular()->selected_network()
452                 << "\"";
453   CHECK(!callback.is_null());
454   Error error;
455   ResultCallback cb = Bind(&CellularCapabilityGSM::OnRegisterReply,
456                                 weak_ptr_factory_.GetWeakPtr(), callback);
457   network_proxy_->Register(cellular()->selected_network(), &error, cb,
458                            kTimeoutRegister);
459   if (error.IsFailure())
460     callback.Run(error);
461 }
462 
RegisterOnNetwork(const string & network_id,Error * error,const ResultCallback & callback)463 void CellularCapabilityGSM::RegisterOnNetwork(
464     const string& network_id,
465     Error* error,
466     const ResultCallback& callback) {
467   SLOG(this, 2) << __func__ << "(" << network_id << ")";
468   CHECK(error);
469   desired_network_ = network_id;
470   ResultCallback cb = Bind(&CellularCapabilityGSM::OnRegisterReply,
471                                 weak_ptr_factory_.GetWeakPtr(), callback);
472   network_proxy_->Register(network_id, error, cb, kTimeoutRegister);
473 }
474 
OnRegisterReply(const ResultCallback & callback,const Error & error)475 void CellularCapabilityGSM::OnRegisterReply(const ResultCallback& callback,
476                                             const Error& error) {
477   SLOG(this, 2) << __func__ << "(" << error << ")";
478 
479   if (error.IsSuccess()) {
480     cellular()->set_selected_network(desired_network_);
481     desired_network_.clear();
482     callback.Run(error);
483     return;
484   }
485   // If registration on the desired network failed,
486   // try to register on the home network.
487   if (!desired_network_.empty()) {
488     desired_network_.clear();
489     cellular()->set_selected_network("");
490     LOG(INFO) << "Couldn't register on selected network, trying home network";
491     Register(callback);
492     return;
493   }
494   callback.Run(error);
495 }
496 
IsRegistered() const497 bool CellularCapabilityGSM::IsRegistered() const {
498   return (registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
499           registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING);
500 }
501 
SetUnregistered(bool searching)502 void CellularCapabilityGSM::SetUnregistered(bool searching) {
503   // If we're already in some non-registered state, don't override that
504   if (registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
505       registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) {
506     registration_state_ =
507         (searching ? MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING :
508                      MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE);
509   }
510 }
511 
RequirePIN(const std::string & pin,bool require,Error * error,const ResultCallback & callback)512 void CellularCapabilityGSM::RequirePIN(
513     const std::string& pin, bool require,
514     Error* error, const ResultCallback& callback) {
515   CHECK(error);
516   card_proxy_->EnablePIN(pin, require, error, callback, kTimeoutDefault);
517 }
518 
EnterPIN(const string & pin,Error * error,const ResultCallback & callback)519 void CellularCapabilityGSM::EnterPIN(const string& pin,
520                                      Error* error,
521                                      const ResultCallback& callback) {
522   CHECK(error);
523   card_proxy_->SendPIN(pin, error, callback, kTimeoutDefault);
524 }
525 
UnblockPIN(const string & unblock_code,const string & pin,Error * error,const ResultCallback & callback)526 void CellularCapabilityGSM::UnblockPIN(const string& unblock_code,
527                                        const string& pin,
528                                        Error* error,
529                                        const ResultCallback& callback) {
530   CHECK(error);
531   card_proxy_->SendPUK(unblock_code, pin, error, callback, kTimeoutDefault);
532 }
533 
ChangePIN(const string & old_pin,const string & new_pin,Error * error,const ResultCallback & callback)534 void CellularCapabilityGSM::ChangePIN(
535     const string& old_pin, const string& new_pin,
536     Error* error, const ResultCallback& callback) {
537   CHECK(error);
538   card_proxy_->ChangePIN(old_pin, new_pin, error, callback, kTimeoutDefault);
539 }
540 
Scan(Error * error,const ResultStringmapsCallback & callback)541 void CellularCapabilityGSM::Scan(Error* error,
542                                  const ResultStringmapsCallback& callback) {
543   ScanResultsCallback cb = Bind(&CellularCapabilityGSM::OnScanReply,
544                                 weak_ptr_factory_.GetWeakPtr(), callback);
545   network_proxy_->Scan(error, cb, kTimeoutScan);
546 }
547 
OnScanReply(const ResultStringmapsCallback & callback,const GSMScanResults & results,const Error & error)548 void CellularCapabilityGSM::OnScanReply(
549     const ResultStringmapsCallback& callback,
550     const GSMScanResults& results,
551     const Error& error) {
552   Stringmaps found_networks;
553   for (const auto& result : results)
554     found_networks.push_back(ParseScanResult(result));
555   callback.Run(found_networks, error);
556 }
557 
ParseScanResult(const GSMScanResult & result)558 Stringmap CellularCapabilityGSM::ParseScanResult(const GSMScanResult& result) {
559   Stringmap parsed;
560   for (GSMScanResult::const_iterator it = result.begin();
561        it != result.end(); ++it) {
562     // TODO(petkov): Define these in system_api/service_constants.h. The
563     // numerical values are taken from 3GPP TS 27.007 Section 7.3.
564     static const char* const kStatusString[] = {
565       "unknown",
566       "available",
567       "current",
568       "forbidden",
569     };
570     static const char* const kTechnologyString[] = {
571       kNetworkTechnologyGsm,
572       "GSM Compact",
573       kNetworkTechnologyUmts,
574       kNetworkTechnologyEdge,
575       "HSDPA",
576       "HSUPA",
577       kNetworkTechnologyHspa,
578     };
579     SLOG(this, 2) << "Network property: " << it->first << " = "
580                   << it->second;
581     if (it->first == kNetworkPropertyStatus) {
582       int status = 0;
583       if (base::StringToInt(it->second, &status) &&
584           status >= 0 &&
585           status < static_cast<int>(arraysize(kStatusString))) {
586         parsed[kStatusProperty] = kStatusString[status];
587       } else {
588         LOG(ERROR) << "Unexpected status value: " << it->second;
589       }
590     } else if (it->first == kNetworkPropertyID) {
591       parsed[kNetworkIdProperty] = it->second;
592     } else if (it->first == kNetworkPropertyLongName) {
593       parsed[kLongNameProperty] = it->second;
594     } else if (it->first == kNetworkPropertyShortName) {
595       parsed[kShortNameProperty] = it->second;
596     } else if (it->first == kNetworkPropertyAccessTechnology) {
597       int tech = 0;
598       if (base::StringToInt(it->second, &tech) &&
599           tech >= 0 &&
600           tech < static_cast<int>(arraysize(kTechnologyString))) {
601         parsed[kTechnologyProperty] = kTechnologyString[tech];
602       } else {
603         LOG(ERROR) << "Unexpected technology value: " << it->second;
604       }
605     } else {
606       LOG(WARNING) << "Unknown network property ignored: " << it->first;
607     }
608   }
609   // If the long name is not available but the network ID is, look up the long
610   // name in the mobile provider database.
611   if ((!ContainsKey(parsed, kLongNameProperty) ||
612        parsed[kLongNameProperty].empty()) &&
613       ContainsKey(parsed, kNetworkIdProperty)) {
614     mobile_operator_info_->Reset();
615     mobile_operator_info_->UpdateMCCMNC(parsed[kNetworkIdProperty]);
616     if (mobile_operator_info_->IsMobileNetworkOperatorKnown() &&
617         !mobile_operator_info_->operator_name().empty()) {
618       parsed[kLongNameProperty] = mobile_operator_info_->operator_name();
619     }
620   }
621   return parsed;
622 }
623 
SetAccessTechnology(uint32_t access_technology)624 void CellularCapabilityGSM::SetAccessTechnology(uint32_t access_technology) {
625   access_technology_ = access_technology;
626   if (cellular()->service().get()) {
627     cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
628   }
629 }
630 
GetNetworkTechnologyString() const631 string CellularCapabilityGSM::GetNetworkTechnologyString() const {
632   switch (access_technology_) {
633     case MM_MODEM_GSM_ACCESS_TECH_GSM:
634     case MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT:
635       return kNetworkTechnologyGsm;
636     case MM_MODEM_GSM_ACCESS_TECH_GPRS:
637       return kNetworkTechnologyGprs;
638     case MM_MODEM_GSM_ACCESS_TECH_EDGE:
639       return kNetworkTechnologyEdge;
640     case MM_MODEM_GSM_ACCESS_TECH_UMTS:
641       return kNetworkTechnologyUmts;
642     case MM_MODEM_GSM_ACCESS_TECH_HSDPA:
643     case MM_MODEM_GSM_ACCESS_TECH_HSUPA:
644     case MM_MODEM_GSM_ACCESS_TECH_HSPA:
645       return kNetworkTechnologyHspa;
646     case MM_MODEM_GSM_ACCESS_TECH_HSPA_PLUS:
647       return kNetworkTechnologyHspaPlus;
648     default:
649       break;
650   }
651   return "";
652 }
653 
GetRoamingStateString() const654 string CellularCapabilityGSM::GetRoamingStateString() const {
655   switch (registration_state_) {
656     case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME:
657       return kRoamingStateHome;
658     case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING:
659       return kRoamingStateRoaming;
660     default:
661       break;
662   }
663   return kRoamingStateUnknown;
664 }
665 
OnPropertiesChanged(const string & interface,const KeyValueStore & properties,const vector<string> & invalidated_properties)666 void CellularCapabilityGSM::OnPropertiesChanged(
667     const string& interface,
668     const KeyValueStore& properties,
669     const vector<string>& invalidated_properties) {
670   CellularCapabilityClassic::OnPropertiesChanged(interface,
671                                                  properties,
672                                                  invalidated_properties);
673   if (interface == MM_MODEM_GSM_NETWORK_INTERFACE) {
674     if (properties.ContainsUint(kPropertyAccessTechnology)) {
675       SetAccessTechnology(properties.GetUint(kPropertyAccessTechnology));
676     }
677   } else {
678     bool emit = false;
679     if (interface == MM_MODEM_GSM_CARD_INTERFACE) {
680       if (properties.ContainsUint(kPropertyEnabledFacilityLocks)) {
681         uint32_t locks = properties.GetUint(kPropertyEnabledFacilityLocks);
682         sim_lock_status_.enabled = locks & MM_MODEM_GSM_FACILITY_SIM;
683         emit = true;
684       }
685     } else if (interface == MM_MODEM_INTERFACE) {
686       if (properties.ContainsString(kPropertyUnlockRequired)) {
687         sim_lock_status_.lock_type =
688             properties.GetString(kPropertyUnlockRequired);
689         emit = true;
690       }
691       if (properties.ContainsUint(kPropertyUnlockRetries)) {
692         sim_lock_status_.retries_left =
693             properties.GetUint(kPropertyUnlockRetries);
694         emit = true;
695       }
696     }
697     // TODO(pprabhu) Rename |emit| to |sim_present| after |sim_lock_status|
698     // moves to cellular.
699     if (emit) {
700       cellular()->set_sim_present(true);
701       cellular()->adaptor()->EmitKeyValueStoreChanged(
702           kSIMLockStatusProperty, SimLockStatusToProperty(nullptr));
703     }
704   }
705 }
706 
OnNetworkModeSignal(uint32_t)707 void CellularCapabilityGSM::OnNetworkModeSignal(uint32_t /*mode*/) {
708   // TODO(petkov): Implement this.
709   NOTIMPLEMENTED();
710 }
711 
OnRegistrationInfoSignal(uint32_t status,const string & operator_code,const string & operator_name)712 void CellularCapabilityGSM::OnRegistrationInfoSignal(
713     uint32_t status, const string& operator_code, const string& operator_name) {
714   SLOG(this, 2) << __func__ << ": regstate=" << status
715                 << ", opercode=" << operator_code
716                 << ", opername=" << operator_name;
717   registration_state_ = status;
718   cellular()->serving_operator_info()->UpdateMCCMNC(operator_code);
719   cellular()->serving_operator_info()->UpdateOperatorName(operator_name);
720   cellular()->HandleNewRegistrationState();
721 }
722 
OnSignalQualitySignal(uint32_t quality)723 void CellularCapabilityGSM::OnSignalQualitySignal(uint32_t quality) {
724   cellular()->HandleNewSignalQuality(quality);
725 }
726 
OnGetRegistrationInfoReply(uint32_t status,const string & operator_code,const string & operator_name,const Error & error)727 void CellularCapabilityGSM::OnGetRegistrationInfoReply(
728     uint32_t status, const string& operator_code, const string& operator_name,
729     const Error& error) {
730   if (error.IsSuccess())
731     OnRegistrationInfoSignal(status, operator_code, operator_name);
732 }
733 
OnGetSignalQualityReply(uint32_t quality,const Error & error)734 void CellularCapabilityGSM::OnGetSignalQualityReply(uint32_t quality,
735                                                     const Error& error) {
736   if (error.IsSuccess())
737     OnSignalQualitySignal(quality);
738 }
739 
OnGetIMEIReply(const ResultCallback & callback,const string & imei,const Error & error)740 void CellularCapabilityGSM::OnGetIMEIReply(const ResultCallback& callback,
741                                            const string& imei,
742                                            const Error& error) {
743   if (error.IsSuccess()) {
744     SLOG(this, 2) << "IMEI: " << imei;
745     cellular()->set_imei(imei);
746   } else {
747     SLOG(this, 2) << "GetIMEI failed - " << error;
748   }
749   callback.Run(error);
750 }
751 
OnGetIMSIReply(const ResultCallback & callback,const string & imsi,const Error & error)752 void CellularCapabilityGSM::OnGetIMSIReply(const ResultCallback& callback,
753                                            const string& imsi,
754                                            const Error& error) {
755   if (error.IsSuccess()) {
756     SLOG(this, 2) << "IMSI: " << imsi;
757     cellular()->set_imsi(imsi);
758     cellular()->set_sim_present(true);
759     cellular()->home_provider_info()->UpdateIMSI(imsi);
760     // We do not currently obtain the IMSI OTA at all. Provide the IMSI from the
761     // SIM to the serving operator as well to aid in MVNO identification.
762     cellular()->serving_operator_info()->UpdateIMSI(imsi);
763     callback.Run(error);
764   } else if (!sim_lock_status_.lock_type.empty()) {
765     SLOG(this, 2) << "GetIMSI failed - SIM lock in place.";
766     cellular()->set_sim_present(true);
767     callback.Run(error);
768   } else {
769     cellular()->set_sim_present(false);
770     if (get_imsi_retries_++ < kGetIMSIRetryLimit) {
771       SLOG(this, 2) << "GetIMSI failed - " << error << ". Retrying";
772       base::Callback<void(void)> retry_get_imsi_cb =
773           Bind(&CellularCapabilityGSM::GetIMSI,
774                weak_ptr_factory_.GetWeakPtr(), callback);
775       cellular()->dispatcher()->PostDelayedTask(
776           retry_get_imsi_cb,
777           get_imsi_retry_delay_milliseconds_);
778     } else {
779       LOG(INFO) << "GetIMSI failed - " << error;
780       cellular()->home_provider_info()->Reset();
781       callback.Run(error);
782     }
783   }
784 }
785 
OnGetSPNReply(const ResultCallback & callback,const string & spn,const Error & error)786 void CellularCapabilityGSM::OnGetSPNReply(const ResultCallback& callback,
787                                           const string& spn,
788                                           const Error& error) {
789   if (error.IsSuccess()) {
790     SLOG(this, 2) << "SPN: " << spn;
791     spn_ = spn;
792     cellular()->home_provider_info()->UpdateOperatorName(spn);
793   } else {
794     SLOG(this, 2) << "GetSPN failed - " << error;
795   }
796   callback.Run(error);
797 }
798 
OnGetMSISDNReply(const ResultCallback & callback,const string & msisdn,const Error & error)799 void CellularCapabilityGSM::OnGetMSISDNReply(const ResultCallback& callback,
800                                              const string& msisdn,
801                                              const Error& error) {
802   if (error.IsSuccess()) {
803     SLOG(this, 2) << "MSISDN: " << msisdn;
804     cellular()->set_mdn(msisdn);
805   } else {
806     SLOG(this, 2) << "GetMSISDN failed - " << error;
807   }
808   callback.Run(error);
809 }
810 
811 }  // namespace shill
812