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
40using base::Bind;
41using std::string;
42using std::vector;
43
44namespace shill {
45
46namespace Logging {
47static auto kModuleLogScope = ScopeLogger::kCellular;
48static string ObjectID(CellularCapabilityGSM* c) {
49  return c->cellular()->GetRpcIdentifier();
50}
51}
52
53// static
54const char CellularCapabilityGSM::kNetworkPropertyAccessTechnology[] =
55    "access-tech";
56const char CellularCapabilityGSM::kNetworkPropertyID[] = "operator-num";
57const char CellularCapabilityGSM::kNetworkPropertyLongName[] = "operator-long";
58const char CellularCapabilityGSM::kNetworkPropertyShortName[] =
59    "operator-short";
60const char CellularCapabilityGSM::kNetworkPropertyStatus[] = "status";
61const char CellularCapabilityGSM::kPhoneNumber[] = "*99#";
62const char CellularCapabilityGSM::kPropertyAccessTechnology[] =
63    "AccessTechnology";
64const char CellularCapabilityGSM::kPropertyEnabledFacilityLocks[] =
65    "EnabledFacilityLocks";
66const char CellularCapabilityGSM::kPropertyUnlockRequired[] = "UnlockRequired";
67const char CellularCapabilityGSM::kPropertyUnlockRetries[] = "UnlockRetries";
68
69const int CellularCapabilityGSM::kGetIMSIRetryLimit = 40;
70const int64_t CellularCapabilityGSM::kGetIMSIRetryDelayMilliseconds = 500;
71
72
73CellularCapabilityGSM::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
109CellularCapabilityGSM::~CellularCapabilityGSM() {}
110
111string CellularCapabilityGSM::GetTypeString() const {
112  return kTechnologyFamilyGsm;
113}
114
115KeyValueStore 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
123void 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
133void 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
156void 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
169void 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
209bool 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
230void CellularCapabilityGSM::ReleaseProxies() {
231  SLOG(this, 2) << __func__;
232  CellularCapabilityClassic::ReleaseProxies();
233  card_proxy_.reset();
234  network_proxy_.reset();
235}
236
237bool CellularCapabilityGSM::AreProxiesInitialized() const {
238  return (CellularCapabilityClassic::AreProxiesInitialized() &&
239          card_proxy_.get() && network_proxy_.get());
240}
241
242void 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
253void 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
270void CellularCapabilityGSM::SetupConnectProperties(
271    KeyValueStore* properties) {
272  SetupApnTryList();
273  FillConnectPropertyMap(properties);
274}
275
276void 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
298void 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
308void 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
339bool CellularCapabilityGSM::AllowRoaming() {
340  return cellular()->provider_requires_roaming() || allow_roaming_property();
341}
342
343// always called from an async context
344void 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
361void 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
381void 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
399void 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
417void 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
425void 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
433void 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
450void 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
463void 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
475void 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
497bool 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
502void 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
512void 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
519void 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
526void 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
534void 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
541void 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
548void 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
558Stringmap 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
624void CellularCapabilityGSM::SetAccessTechnology(uint32_t access_technology) {
625  access_technology_ = access_technology;
626  if (cellular()->service().get()) {
627    cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
628  }
629}
630
631string 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
654string 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
666void 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
707void CellularCapabilityGSM::OnNetworkModeSignal(uint32_t /*mode*/) {
708  // TODO(petkov): Implement this.
709  NOTIMPLEMENTED();
710}
711
712void 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
723void CellularCapabilityGSM::OnSignalQualitySignal(uint32_t quality) {
724  cellular()->HandleNewSignalQuality(quality);
725}
726
727void 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
734void CellularCapabilityGSM::OnGetSignalQualityReply(uint32_t quality,
735                                                    const Error& error) {
736  if (error.IsSuccess())
737    OnSignalQualitySignal(quality);
738}
739
740void 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
752void 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
786void 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
799void 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