Home | History | Annotate | Download | only in cellular
      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;
     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 
     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 
    109 CellularCapabilityGSM::~CellularCapabilityGSM() {}
    110 
    111 string CellularCapabilityGSM::GetTypeString() const {
    112   return kTechnologyFamilyGsm;
    113 }
    114 
    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 
    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 
    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 
    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 
    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 
    209 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 
    230 void CellularCapabilityGSM::ReleaseProxies() {
    231   SLOG(this, 2) << __func__;
    232   CellularCapabilityClassic::ReleaseProxies();
    233   card_proxy_.reset();
    234   network_proxy_.reset();
    235 }
    236 
    237 bool CellularCapabilityGSM::AreProxiesInitialized() const {
    238   return (CellularCapabilityClassic::AreProxiesInitialized() &&
    239           card_proxy_.get() && network_proxy_.get());
    240 }
    241 
    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
    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 
    270 void CellularCapabilityGSM::SetupConnectProperties(
    271     KeyValueStore* properties) {
    272   SetupApnTryList();
    273   FillConnectPropertyMap(properties);
    274 }
    275 
    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 
    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 
    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 
    339 bool CellularCapabilityGSM::AllowRoaming() {
    340   return cellular()->provider_requires_roaming() || allow_roaming_property();
    341 }
    342 
    343 // always called from an async context
    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
    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
    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
    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 
    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 
    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 
    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
    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 
    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 
    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 
    497 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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    631 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 
    654 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 
    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 
    707 void CellularCapabilityGSM::OnNetworkModeSignal(uint32_t /*mode*/) {
    708   // TODO(petkov): Implement this.
    709   NOTIMPLEMENTED();
    710 }
    711 
    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 
    723 void CellularCapabilityGSM::OnSignalQualitySignal(uint32_t quality) {
    724   cellular()->HandleNewSignalQuality(quality);
    725 }
    726 
    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 
    734 void CellularCapabilityGSM::OnGetSignalQualityReply(uint32_t quality,
    735                                                     const Error& error) {
    736   if (error.IsSuccess())
    737     OnSignalQualitySignal(quality);
    738 }
    739 
    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 
    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 
    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 
    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