1c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
2c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// Copyright (C) 2012 The Android Open Source Project
3c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
4c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// Licensed under the Apache License, Version 2.0 (the "License");
5c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// you may not use this file except in compliance with the License.
6c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// You may obtain a copy of the License at
7c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
8c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//      http://www.apache.org/licenses/LICENSE-2.0
9c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
10c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// Unless required by applicable law or agreed to in writing, software
11c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// distributed under the License is distributed on an "AS IS" BASIS,
12c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// See the License for the specific language governing permissions and
14c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// limitations under the License.
15c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
16daf4386a4775be7c965493749ccfe2fecc2e4167Darin Petkov
17c54afe521739065a5d77e7c049acdb5e603f0592Ben Chan#include "shill/cellular/cellular_capability_gsm.h"
18daf4386a4775be7c965493749ccfe2fecc2e4167Darin Petkov
194c0724af38d0eaf7155bb789280940a41ef9f08cJason Glasgow#include <string>
204c0724af38d0eaf7155bb789280940a41ef9f08cJason Glasgow#include <vector>
214c0724af38d0eaf7155bb789280940a41ef9f08cJason Glasgow
223e20a2341d0aeb7681e4ee0f89eae6817ade2b3bEric Shienbrood#include <base/bind.h>
233e20a2341d0aeb7681e4ee0f89eae6817ade2b3bEric Shienbrood#include <base/stl_util.h>
24a0ddf46e466bd4ba3d20952f0a6988c680c1af14Ben Chan#include <base/strings/string_number_conversions.h>
25a0ddf46e466bd4ba3d20952f0a6988c680c1af14Ben Chan#include <base/strings/stringprintf.h>
26289a5a5e18bb1a676b3dfce111af4c2c00c7776eSamuel Tan#if defined(__ANDROID__)
27289a5a5e18bb1a676b3dfce111af4c2c00c7776eSamuel Tan#include <dbus/service_constants.h>
28289a5a5e18bb1a676b3dfce111af4c2c00c7776eSamuel Tan#else
2920c13ec645faacb9c517fd2c574aa7fa30d890d0Darin Petkov#include <chromeos/dbus/service_constants.h>
30289a5a5e18bb1a676b3dfce111af4c2c00c7776eSamuel Tan#endif  // __ANDROID__
3120c13ec645faacb9c517fd2c574aa7fa30d890d0Darin Petkov#include <mm/mm-modem.h>
32daf4386a4775be7c965493749ccfe2fecc2e4167Darin Petkov
333cfbf216f1f918384866bf0d1a4c9a447d8109e8Darin Petkov#include "shill/adaptor_interfaces.h"
34c54afe521739065a5d77e7c049acdb5e603f0592Ben Chan#include "shill/cellular/cellular_service.h"
35608ec29525f553d51f0a92e84176e3d4b45930a9Peter Qiu#include "shill/control_interface.h"
365de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood#include "shill/error.h"
37b691efd71561246065eae3cdd73a96ca1b8a528dChristopher Wiley#include "shill/logging.h"
38721ac93ab19a9169872a296626adb49279031af5Darin Petkov#include "shill/property_accessor.h"
39daf4386a4775be7c965493749ccfe2fecc2e4167Darin Petkov
403e20a2341d0aeb7681e4ee0f89eae6817ade2b3bEric Shienbroodusing base::Bind;
41b05315fa7beab387bcbfd9330215aa50cdd6c8f4Darin Petkovusing std::string;
424c0724af38d0eaf7155bb789280940a41ef9f08cJason Glasgowusing std::vector;
43b05315fa7beab387bcbfd9330215aa50cdd6c8f4Darin Petkov
44daf4386a4775be7c965493749ccfe2fecc2e4167Darin Petkovnamespace shill {
45daf4386a4775be7c965493749ccfe2fecc2e4167Darin Petkov
46c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silbersteinnamespace Logging {
47c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silbersteinstatic auto kModuleLogScope = ScopeLogger::kCellular;
48f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewartstatic string ObjectID(CellularCapabilityGSM* c) {
49c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein  return c->cellular()->GetRpcIdentifier();
50c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein}
51c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein}
52c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein
53ac635a8fb539fd44f3e24c33872b61fd064c0d60Darin Petkov// static
541272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkovconst char CellularCapabilityGSM::kNetworkPropertyAccessTechnology[] =
551272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov    "access-tech";
561272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkovconst char CellularCapabilityGSM::kNetworkPropertyID[] = "operator-num";
571272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkovconst char CellularCapabilityGSM::kNetworkPropertyLongName[] = "operator-long";
581272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkovconst char CellularCapabilityGSM::kNetworkPropertyShortName[] =
591272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov    "operator-short";
601272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkovconst char CellularCapabilityGSM::kNetworkPropertyStatus[] = "status";
61ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkovconst char CellularCapabilityGSM::kPhoneNumber[] = "*99#";
62ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkovconst char CellularCapabilityGSM::kPropertyAccessTechnology[] =
63ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkov    "AccessTechnology";
6463138a9b8249fd69c83a772ee3170551a589d57aDarin Petkovconst char CellularCapabilityGSM::kPropertyEnabledFacilityLocks[] =
6563138a9b8249fd69c83a772ee3170551a589d57aDarin Petkov    "EnabledFacilityLocks";
66721ac93ab19a9169872a296626adb49279031af5Darin Petkovconst char CellularCapabilityGSM::kPropertyUnlockRequired[] = "UnlockRequired";
67721ac93ab19a9169872a296626adb49279031af5Darin Petkovconst char CellularCapabilityGSM::kPropertyUnlockRetries[] = "UnlockRetries";
681272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov
69c89b928f1dd9240a7466585821ed8f277bdc4203Ben Chanconst int CellularCapabilityGSM::kGetIMSIRetryLimit = 40;
707fab89734d88724a288e96a9996b15548c5294c7Ben Chanconst int64_t CellularCapabilityGSM::kGetIMSIRetryDelayMilliseconds = 500;
7182a31a0323d2f7b72045e5ee6ef25f9a7dfbc3bbGary Morain
7282a31a0323d2f7b72045e5ee6ef25f9a7dfbc3bbGary Morain
73608ec29525f553d51f0a92e84176e3d4b45930a9Peter QiuCellularCapabilityGSM::CellularCapabilityGSM(
74608ec29525f553d51f0a92e84176e3d4b45930a9Peter Qiu    Cellular* cellular,
75608ec29525f553d51f0a92e84176e3d4b45930a9Peter Qiu    ControlInterface* control_interface,
76608ec29525f553d51f0a92e84176e3d4b45930a9Peter Qiu    ModemInfo* modem_info)
77608ec29525f553d51f0a92e84176e3d4b45930a9Peter Qiu    : CellularCapabilityClassic(cellular, control_interface, modem_info),
783e20a2341d0aeb7681e4ee0f89eae6817ade2b3bEric Shienbrood      weak_ptr_factory_(this),
79afe636676760df31a8db2f4b77bfd19faae52782Prathmesh Prabhu      mobile_operator_info_(new MobileOperatorInfo(cellular->dispatcher(),
80afe636676760df31a8db2f4b77bfd19faae52782Prathmesh Prabhu                                                   "ParseScanResult")),
81184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov      registration_state_(MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN),
82ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkov      access_technology_(MM_MODEM_GSM_ACCESS_TECH_UNKNOWN),
83ea18c6c99743aeee9b2e544532ef9fe55dbd182dBen Chan      home_provider_info_(nullptr),
8482a31a0323d2f7b72045e5ee6ef25f9a7dfbc3bbGary Morain      get_imsi_retries_(0),
8549ffffd458c2407c67c2d0d572974e91c7e9bea7Prathmesh Prabhu      get_imsi_retry_delay_milliseconds_(kGetIMSIRetryDelayMilliseconds) {
86c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein  SLOG(this, 2) << "Cellular capability constructed: GSM";
87afe636676760df31a8db2f4b77bfd19faae52782Prathmesh Prabhu  mobile_operator_info_->Init();
88bebf1b8bce52b88c2cc2d93200b9405f9c19cf21mukesh agrawal  HelpRegisterConstDerivedKeyValueStore(
897ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan      kSIMLockStatusProperty, &CellularCapabilityGSM::SimLockStatusToProperty);
909f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu  this->cellular()->set_scanning_supported(true);
913ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan
923ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  // TODO(benchan): This is a hack to initialize the GSM card proxy for GetIMSI
933ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  // before InitProxies is called. There are side-effects of calling InitProxies
943ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  // before the device is enabled. It's better to refactor InitProxies such that
953ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  // proxies can be created when the cellular device/capability is constructed,
963ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  // but callbacks for DBus signal updates are not set up until the device is
973ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  // enabled.
983ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  card_proxy_.reset(
99608ec29525f553d51f0a92e84176e3d4b45930a9Peter Qiu      control_interface->CreateModemGSMCardProxy(cellular->dbus_path(),
10005d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu                                                 cellular->dbus_service()));
1013ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  // TODO(benchan): To allow unit testing using a mock proxy without further
102ea18c6c99743aeee9b2e544532ef9fe55dbd182dBen Chan  // complicating the code, the test proxy factory is set up to return a nullptr
1033ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  // pointer when CellularCapabilityGSM is constructed. Refactor the code to
1043ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  // avoid this hack.
1053ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  if (card_proxy_.get())
1063ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan    InitProperties();
1071272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov}
108daf4386a4775be7c965493749ccfe2fecc2e4167Darin Petkov
109f98f00e52f3408fd3ca6184f19636efb7e66b986Ben ChanCellularCapabilityGSM::~CellularCapabilityGSM() {}
110f98f00e52f3408fd3ca6184f19636efb7e66b986Ben Chan
111f98f00e52f3408fd3ca6184f19636efb7e66b986Ben Chanstring CellularCapabilityGSM::GetTypeString() const {
112f98f00e52f3408fd3ca6184f19636efb7e66b986Ben Chan  return kTechnologyFamilyGsm;
113f98f00e52f3408fd3ca6184f19636efb7e66b986Ben Chan}
114f98f00e52f3408fd3ca6184f19636efb7e66b986Ben Chan
115f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul StewartKeyValueStore CellularCapabilityGSM::SimLockStatusToProperty(Error* /*error*/) {
11663138a9b8249fd69c83a772ee3170551a589d57aDarin Petkov  KeyValueStore status;
1177ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan  status.SetBool(kSIMLockEnabledProperty, sim_lock_status_.enabled);
1187ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan  status.SetString(kSIMLockTypeProperty, sim_lock_status_.lock_type);
1197ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan  status.SetUint(kSIMLockRetriesLeftProperty, sim_lock_status_.retries_left);
12063138a9b8249fd69c83a772ee3170551a589d57aDarin Petkov  return status;
121721ac93ab19a9169872a296626adb49279031af5Darin Petkov}
122721ac93ab19a9169872a296626adb49279031af5Darin Petkov
123bebf1b8bce52b88c2cc2d93200b9405f9c19cf21mukesh agrawalvoid CellularCapabilityGSM::HelpRegisterConstDerivedKeyValueStore(
124f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    const string& name,
125f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    KeyValueStore(CellularCapabilityGSM::*get)(Error* error)) {
12663138a9b8249fd69c83a772ee3170551a589d57aDarin Petkov  cellular()->mutable_store()->RegisterDerivedKeyValueStore(
127721ac93ab19a9169872a296626adb49279031af5Darin Petkov      name,
12863138a9b8249fd69c83a772ee3170551a589d57aDarin Petkov      KeyValueStoreAccessor(
12963138a9b8249fd69c83a772ee3170551a589d57aDarin Petkov          new CustomAccessor<CellularCapabilityGSM, KeyValueStore>(
130ea18c6c99743aeee9b2e544532ef9fe55dbd182dBen Chan              this, get, nullptr)));
131721ac93ab19a9169872a296626adb49279031af5Darin Petkov}
132721ac93ab19a9169872a296626adb49279031af5Darin Petkov
1339a24553461df7036755060423f90804011612249Eric Shienbroodvoid CellularCapabilityGSM::InitProxies() {
13482f9ab3f404c8f414348c6effdc57c4d3b9223d7Jason Glasgow  CellularCapabilityClassic::InitProxies();
1353ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  // TODO(benchan): Remove this check after refactoring the proxy
1363ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  // initialization.
1373ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  if (!card_proxy_.get()) {
1383ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan    card_proxy_.reset(
13905d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu        control_interface()->CreateModemGSMCardProxy(
14005d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu            cellular()->dbus_path(), cellular()->dbus_service()));
1413ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  }
142184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov  network_proxy_.reset(
143608ec29525f553d51f0a92e84176e3d4b45930a9Peter Qiu      control_interface()->CreateModemGSMNetworkProxy(
14405d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu          cellular()->dbus_path(), cellular()->dbus_service()));
1459a24553461df7036755060423f90804011612249Eric Shienbrood  network_proxy_->set_signal_quality_callback(
1469a24553461df7036755060423f90804011612249Eric Shienbrood      Bind(&CellularCapabilityGSM::OnSignalQualitySignal,
1479a24553461df7036755060423f90804011612249Eric Shienbrood           weak_ptr_factory_.GetWeakPtr()));
1489a24553461df7036755060423f90804011612249Eric Shienbrood  network_proxy_->set_network_mode_callback(
1499a24553461df7036755060423f90804011612249Eric Shienbrood      Bind(&CellularCapabilityGSM::OnNetworkModeSignal,
1509a24553461df7036755060423f90804011612249Eric Shienbrood           weak_ptr_factory_.GetWeakPtr()));
1519a24553461df7036755060423f90804011612249Eric Shienbrood  network_proxy_->set_registration_info_callback(
1529a24553461df7036755060423f90804011612249Eric Shienbrood      Bind(&CellularCapabilityGSM::OnRegistrationInfoSignal,
1539a24553461df7036755060423f90804011612249Eric Shienbrood           weak_ptr_factory_.GetWeakPtr()));
1549a24553461df7036755060423f90804011612249Eric Shienbrood}
1559a24553461df7036755060423f90804011612249Eric Shienbrood
1563ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chanvoid CellularCapabilityGSM::InitProperties() {
157f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart  CellularTaskList* tasks = new CellularTaskList();
1583ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  ResultCallback cb_ignore_error =
1593ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan      Bind(&CellularCapabilityGSM::StepCompletedCallback,
1603ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan           weak_ptr_factory_.GetWeakPtr(), ResultCallback(), true, tasks);
161bd3aee80be371d838863eccf708fd107153503efBen Chan  // Chrome checks if a SIM is present before allowing the modem to be enabled,
162bd3aee80be371d838863eccf708fd107153503efBen Chan  // so shill needs to obtain IMSI, as an indicator of SIM presence, even
163bd3aee80be371d838863eccf708fd107153503efBen Chan  // before the device is enabled.
1643ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  tasks->push_back(Bind(&CellularCapabilityGSM::GetIMSI,
1653ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan                        weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
1663ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan  RunNextStep(tasks);
1673ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan}
1683ecdf823b0ae196bd6824d5dc140da752776b3f9Ben Chan
169f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewartvoid CellularCapabilityGSM::StartModem(Error* error,
170f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                       const ResultCallback& callback) {
1719a24553461df7036755060423f90804011612249Eric Shienbrood  InitProxies();
1729a24553461df7036755060423f90804011612249Eric Shienbrood
173f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart  CellularTaskList* tasks = new CellularTaskList();
1749a24553461df7036755060423f90804011612249Eric Shienbrood  ResultCallback cb =
1759a24553461df7036755060423f90804011612249Eric Shienbrood      Bind(&CellularCapabilityGSM::StepCompletedCallback,
176923006bc5f3fde53ea8651de16200d7226f065a2Thieu Le           weak_ptr_factory_.GetWeakPtr(), callback, false, tasks);
177923006bc5f3fde53ea8651de16200d7226f065a2Thieu Le  ResultCallback cb_ignore_error =
178923006bc5f3fde53ea8651de16200d7226f065a2Thieu Le        Bind(&CellularCapabilityGSM::StepCompletedCallback,
179923006bc5f3fde53ea8651de16200d7226f065a2Thieu Le                   weak_ptr_factory_.GetWeakPtr(), callback, true, tasks);
1807fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood  if (!cellular()->IsUnderlyingDeviceEnabled())
1817fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    tasks->push_back(Bind(&CellularCapabilityGSM::EnableModem,
1827fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood                          weak_ptr_factory_.GetWeakPtr(), cb));
1837fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood  // If we're within range of the home network, the modem will try to
1847fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood  // register once it's enabled, or may be already registered if we
1857fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood  // started out enabled.
1869f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu  if (!IsUnderlyingDeviceRegistered() &&
1879f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu      !cellular()->selected_network().empty())
1887fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    tasks->push_back(Bind(&CellularCapabilityGSM::Register,
1897fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood                          weak_ptr_factory_.GetWeakPtr(), cb));
1909a24553461df7036755060423f90804011612249Eric Shienbrood  tasks->push_back(Bind(&CellularCapabilityGSM::GetIMEI,
1919a24553461df7036755060423f90804011612249Eric Shienbrood                        weak_ptr_factory_.GetWeakPtr(), cb));
19282a31a0323d2f7b72045e5ee6ef25f9a7dfbc3bbGary Morain  get_imsi_retries_ = 0;
1939a24553461df7036755060423f90804011612249Eric Shienbrood  tasks->push_back(Bind(&CellularCapabilityGSM::GetIMSI,
1949a24553461df7036755060423f90804011612249Eric Shienbrood                        weak_ptr_factory_.GetWeakPtr(), cb));
1959a24553461df7036755060423f90804011612249Eric Shienbrood  tasks->push_back(Bind(&CellularCapabilityGSM::GetSPN,
196923006bc5f3fde53ea8651de16200d7226f065a2Thieu Le                        weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
1979a24553461df7036755060423f90804011612249Eric Shienbrood  tasks->push_back(Bind(&CellularCapabilityGSM::GetMSISDN,
198923006bc5f3fde53ea8651de16200d7226f065a2Thieu Le                        weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
1999a24553461df7036755060423f90804011612249Eric Shienbrood  tasks->push_back(Bind(&CellularCapabilityGSM::GetProperties,
2009a24553461df7036755060423f90804011612249Eric Shienbrood                        weak_ptr_factory_.GetWeakPtr(), cb));
2019a24553461df7036755060423f90804011612249Eric Shienbrood  tasks->push_back(Bind(&CellularCapabilityGSM::GetModemInfo,
2027fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood                        weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
2039a24553461df7036755060423f90804011612249Eric Shienbrood  tasks->push_back(Bind(&CellularCapabilityGSM::FinishEnable,
2049a24553461df7036755060423f90804011612249Eric Shienbrood                        weak_ptr_factory_.GetWeakPtr(), cb));
2059a24553461df7036755060423f90804011612249Eric Shienbrood
2069a24553461df7036755060423f90804011612249Eric Shienbrood  RunNextStep(tasks);
2079a24553461df7036755060423f90804011612249Eric Shienbrood}
2089a24553461df7036755060423f90804011612249Eric Shienbrood
2097fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbroodbool CellularCapabilityGSM::IsUnderlyingDeviceRegistered() const {
2107fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood  switch (cellular()->modem_state()) {
2117b7d63db971ac701b765cee74f6664334de69280Ben Chan    case Cellular::kModemStateFailed:
2127fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    case Cellular::kModemStateUnknown:
2137fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    case Cellular::kModemStateDisabled:
2147fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    case Cellular::kModemStateInitializing:
2157fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    case Cellular::kModemStateLocked:
2167fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    case Cellular::kModemStateDisabling:
2177fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    case Cellular::kModemStateEnabling:
2187fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    case Cellular::kModemStateEnabled:
2197fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood      return false;
2207fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    case Cellular::kModemStateSearching:
2217fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    case Cellular::kModemStateRegistered:
2227fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    case Cellular::kModemStateDisconnecting:
2237fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    case Cellular::kModemStateConnecting:
2247fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    case Cellular::kModemStateConnected:
2257fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood      return true;
2267fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood  }
2277fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood  return false;
2287fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood}
2297fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood
2309a24553461df7036755060423f90804011612249Eric Shienbroodvoid CellularCapabilityGSM::ReleaseProxies() {
231c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein  SLOG(this, 2) << __func__;
23282f9ab3f404c8f414348c6effdc57c4d3b9223d7Jason Glasgow  CellularCapabilityClassic::ReleaseProxies();
233721ac93ab19a9169872a296626adb49279031af5Darin Petkov  card_proxy_.reset();
234721ac93ab19a9169872a296626adb49279031af5Darin Petkov  network_proxy_.reset();
235721ac93ab19a9169872a296626adb49279031af5Darin Petkov}
236721ac93ab19a9169872a296626adb49279031af5Darin Petkov
23763881a73f4aa10521e0173e4fa5e4dedec45730fThieu Lebool CellularCapabilityGSM::AreProxiesInitialized() const {
23863881a73f4aa10521e0173e4fa5e4dedec45730fThieu Le  return (CellularCapabilityClassic::AreProxiesInitialized() &&
23963881a73f4aa10521e0173e4fa5e4dedec45730fThieu Le          card_proxy_.get() && network_proxy_.get());
24063881a73f4aa10521e0173e4fa5e4dedec45730fThieu Le}
24163881a73f4aa10521e0173e4fa5e4dedec45730fThieu Le
2425f316f65dd63b21c050f48410170482bfe92e286Darin Petkovvoid CellularCapabilityGSM::OnServiceCreated() {
2437ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan  cellular()->service()->SetActivationState(kActivationStateActivated);
244ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkov}
245ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkov
24630bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood// Create the list of APNs to try, in the following order:
24730bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood// - last APN that resulted in a successful connection attempt on the
24830bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood//   current network (if any)
24930bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood// - the APN, if any, that was set by the user
25030bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood// - the list of APNs found in the mobile broadband provider DB for the
25130bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood//   home provider associated with the current SIM
25230bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood// - as a last resort, attempt to connect with no APN
25330bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbroodvoid CellularCapabilityGSM::SetupApnTryList() {
25430bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood  apn_try_list_.clear();
25530bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood
25630bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood  DCHECK(cellular()->service().get());
257f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart  const Stringmap* apn_info = cellular()->service()->GetLastGoodApn();
25830bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood  if (apn_info)
25930bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood    apn_try_list_.push_back(*apn_info);
26030bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood
26130bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood  apn_info = cellular()->service()->GetUserSpecifiedApn();
26230bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood  if (apn_info)
26330bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood    apn_try_list_.push_back(*apn_info);
26430bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood
2659f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu  apn_try_list_.insert(apn_try_list_.end(),
2669f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu                       cellular()->apn_list().begin(),
2679f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu                       cellular()->apn_list().end());
26830bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood}
26930bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood
270ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkovvoid CellularCapabilityGSM::SetupConnectProperties(
27105d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu    KeyValueStore* properties) {
27230bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood  SetupApnTryList();
27330bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood  FillConnectPropertyMap(properties);
27430bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood}
27530bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood
27630bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbroodvoid CellularCapabilityGSM::FillConnectPropertyMap(
27705d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu    KeyValueStore* properties) {
27805d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu  properties->SetString(kConnectPropertyPhoneNumber, kPhoneNumber);
27930bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood
28082f9ab3f404c8f414348c6effdc57c4d3b9223d7Jason Glasgow  if (!AllowRoaming())
28105d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu    properties->SetBool(kConnectPropertyHomeOnly, true);
28230bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood
28330bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood  if (!apn_try_list_.empty()) {
28430bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood    // Leave the APN at the front of the list, so that it can be recorded
28530bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood    // if the connect attempt succeeds.
28630bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood    Stringmap apn_info = apn_try_list_.front();
287c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein    SLOG(this, 2) << __func__ << ": Using APN " << apn_info[kApnProperty];
28805d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu    properties->SetString(kConnectPropertyApn, apn_info[kApnProperty]);
2897ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan    if (ContainsKey(apn_info, kApnUsernameProperty))
29005d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu      properties->SetString(kConnectPropertyApnUsername,
29105d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu                            apn_info[kApnUsernameProperty]);
2927ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan    if (ContainsKey(apn_info, kApnPasswordProperty))
29305d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu      properties->SetString(kConnectPropertyApnPassword,
29405d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu                            apn_info[kApnPasswordProperty]);
29530bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood  }
29630bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood}
29730bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood
29805d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiuvoid CellularCapabilityGSM::Connect(const KeyValueStore& properties,
299f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                    Error* error,
300f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                    const ResultCallback& callback) {
301c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein  SLOG(this, 2) << __func__;
302b54974f033f713690135f3f3516ca14aa7ee6cfdNathan Williams  ResultCallback cb = Bind(&CellularCapabilityGSM::OnConnectReply,
303b54974f033f713690135f3f3516ca14aa7ee6cfdNathan Williams                           weak_ptr_factory_.GetWeakPtr(),
304b54974f033f713690135f3f3516ca14aa7ee6cfdNathan Williams                           callback);
305b54974f033f713690135f3f3516ca14aa7ee6cfdNathan Williams  simple_proxy_->Connect(properties, error, cb, kTimeoutConnect);
306b54974f033f713690135f3f3516ca14aa7ee6cfdNathan Williams}
307b54974f033f713690135f3f3516ca14aa7ee6cfdNathan Williams
308f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewartvoid CellularCapabilityGSM::OnConnectReply(const ResultCallback& callback,
309f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                           const Error& error) {
310b5954a249c1346e5a0da097330a3639753392ecbThieu Le  CellularServiceRefPtr service = cellular()->service();
311b5954a249c1346e5a0da097330a3639753392ecbThieu Le  if (!service) {
312b5954a249c1346e5a0da097330a3639753392ecbThieu Le    // The service could have been deleted before our Connect() request
313b5954a249c1346e5a0da097330a3639753392ecbThieu Le    // completes if the modem was enabled and then quickly disabled.
314b5954a249c1346e5a0da097330a3639753392ecbThieu Le    apn_try_list_.clear();
315b5954a249c1346e5a0da097330a3639753392ecbThieu Le  } else if (error.IsFailure()) {
316b5954a249c1346e5a0da097330a3639753392ecbThieu Le    service->ClearLastGoodApn();
31730bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood    // The APN that was just tried (and failed) is still at the
31830bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood    // front of the list, about to be removed. If the list is empty
31930bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood    // after that, try one last time without an APN. This may succeed
32030bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood    // with some modems in some cases.
32130bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood    if (error.type() == Error::kInvalidApn && !apn_try_list_.empty()) {
32230bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood      apn_try_list_.pop_front();
323c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein      SLOG(this, 2) << "Connect failed with invalid APN, "
324c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein                    << apn_try_list_.size() << " remaining APNs to try";
32505d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu      KeyValueStore props;
32630bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood      FillConnectPropertyMap(&props);
32730bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood      Error error;
32830bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood      Connect(props, &error, callback);
32930bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood      return;
33030bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood    }
33130bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood  } else if (!apn_try_list_.empty()) {
332b5954a249c1346e5a0da097330a3639753392ecbThieu Le    service->SetLastGoodApn(apn_try_list_.front());
33330bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood    apn_try_list_.clear();
33430bc0ecfcdad79f652eab10c7cda9adca33a04faEric Shienbrood  }
335b54974f033f713690135f3f3516ca14aa7ee6cfdNathan Williams  if (!callback.is_null())
336b54974f033f713690135f3f3516ca14aa7ee6cfdNathan Williams    callback.Run(error);
33782f9ab3f404c8f414348c6effdc57c4d3b9223d7Jason Glasgow}
33882f9ab3f404c8f414348c6effdc57c4d3b9223d7Jason Glasgow
33982f9ab3f404c8f414348c6effdc57c4d3b9223d7Jason Glasgowbool CellularCapabilityGSM::AllowRoaming() {
3409f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu  return cellular()->provider_requires_roaming() || allow_roaming_property();
341ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkov}
342ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkov
3439a24553461df7036755060423f90804011612249Eric Shienbrood// always called from an async context
344f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewartvoid CellularCapabilityGSM::GetIMEI(const ResultCallback& callback) {
345c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein  SLOG(this, 2) << __func__;
3469a24553461df7036755060423f90804011612249Eric Shienbrood  CHECK(!callback.is_null());
3479a24553461df7036755060423f90804011612249Eric Shienbrood  Error error;
3489f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu  if (cellular()->imei().empty()) {
3499a24553461df7036755060423f90804011612249Eric Shienbrood    GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetIMEIReply,
3509a24553461df7036755060423f90804011612249Eric Shienbrood                                    weak_ptr_factory_.GetWeakPtr(), callback);
3519a24553461df7036755060423f90804011612249Eric Shienbrood    card_proxy_->GetIMEI(&error, cb, kTimeoutDefault);
3529a24553461df7036755060423f90804011612249Eric Shienbrood    if (error.IsFailure())
3539a24553461df7036755060423f90804011612249Eric Shienbrood      callback.Run(error);
3545de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  } else {
355c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein    SLOG(this, 2) << "Already have IMEI " << cellular()->imei();
3569a24553461df7036755060423f90804011612249Eric Shienbrood    callback.Run(error);
3575de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  }
3585de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood}
3595de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood
3609a24553461df7036755060423f90804011612249Eric Shienbrood// always called from an async context
361f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewartvoid CellularCapabilityGSM::GetIMSI(const ResultCallback& callback) {
362c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein  SLOG(this, 2) << __func__;
3639a24553461df7036755060423f90804011612249Eric Shienbrood  CHECK(!callback.is_null());
3649a24553461df7036755060423f90804011612249Eric Shienbrood  Error error;
3659f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu  if (cellular()->imsi().empty()) {
3669a24553461df7036755060423f90804011612249Eric Shienbrood    GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetIMSIReply,
3679a24553461df7036755060423f90804011612249Eric Shienbrood                                    weak_ptr_factory_.GetWeakPtr(),
3689a24553461df7036755060423f90804011612249Eric Shienbrood                                    callback);
3699a24553461df7036755060423f90804011612249Eric Shienbrood    card_proxy_->GetIMSI(&error, cb, kTimeoutDefault);
37028b4a3b38737f29b98e0e19aefd92b2454165040Prathmesh Prabhu    if (error.IsFailure()) {
3718599e052529c222da6cefd934bd52ceb9c194f5fPrathmesh Prabhu      cellular()->home_provider_info()->Reset();
3729a24553461df7036755060423f90804011612249Eric Shienbrood      callback.Run(error);
37328b4a3b38737f29b98e0e19aefd92b2454165040Prathmesh Prabhu    }
3745de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  } else {
375c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein    SLOG(this, 2) << "Already have IMSI " << cellular()->imsi();
3769a24553461df7036755060423f90804011612249Eric Shienbrood    callback.Run(error);
377cb547737d288052935052eeb341b30922b4022a0Darin Petkov  }
3785de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood}
3795de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood
3809a24553461df7036755060423f90804011612249Eric Shienbrood// always called from an async context
381f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewartvoid CellularCapabilityGSM::GetSPN(const ResultCallback& callback) {
382c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein  SLOG(this, 2) << __func__;
3839a24553461df7036755060423f90804011612249Eric Shienbrood  CHECK(!callback.is_null());
3849a24553461df7036755060423f90804011612249Eric Shienbrood  Error error;
385ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkov  if (spn_.empty()) {
3869a24553461df7036755060423f90804011612249Eric Shienbrood    GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetSPNReply,
3879a24553461df7036755060423f90804011612249Eric Shienbrood                                    weak_ptr_factory_.GetWeakPtr(),
3889a24553461df7036755060423f90804011612249Eric Shienbrood                                    callback);
3899a24553461df7036755060423f90804011612249Eric Shienbrood    card_proxy_->GetSPN(&error, cb, kTimeoutDefault);
3909a24553461df7036755060423f90804011612249Eric Shienbrood    if (error.IsFailure())
3919a24553461df7036755060423f90804011612249Eric Shienbrood      callback.Run(error);
3925de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  } else {
393c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein    SLOG(this, 2) << "Already have SPN " << spn_;
3949a24553461df7036755060423f90804011612249Eric Shienbrood    callback.Run(error);
395cb547737d288052935052eeb341b30922b4022a0Darin Petkov  }
3965de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood}
3975de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood
3989a24553461df7036755060423f90804011612249Eric Shienbrood// always called from an async context
399f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewartvoid CellularCapabilityGSM::GetMSISDN(const ResultCallback& callback) {
400c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein  SLOG(this, 2) << __func__;
4019a24553461df7036755060423f90804011612249Eric Shienbrood  CHECK(!callback.is_null());
4029a24553461df7036755060423f90804011612249Eric Shienbrood  Error error;
4039f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu  string mdn = cellular()->mdn();
4049f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu  if (mdn.empty()) {
4059a24553461df7036755060423f90804011612249Eric Shienbrood    GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetMSISDNReply,
4069a24553461df7036755060423f90804011612249Eric Shienbrood                                    weak_ptr_factory_.GetWeakPtr(),
4079a24553461df7036755060423f90804011612249Eric Shienbrood                                    callback);
4089a24553461df7036755060423f90804011612249Eric Shienbrood    card_proxy_->GetMSISDN(&error, cb, kTimeoutDefault);
4099a24553461df7036755060423f90804011612249Eric Shienbrood    if (error.IsFailure())
4109a24553461df7036755060423f90804011612249Eric Shienbrood      callback.Run(error);
4115de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  } else {
412c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein    SLOG(this, 2) << "Already have MSISDN " << mdn;
4139a24553461df7036755060423f90804011612249Eric Shienbrood    callback.Run(error);
414cb547737d288052935052eeb341b30922b4022a0Darin Petkov  }
415cb547737d288052935052eeb341b30922b4022a0Darin Petkov}
416cb547737d288052935052eeb341b30922b4022a0Darin Petkov
4173e509242f1a0e2e018a5c18a8d7b9224c6044d9aDarin Petkovvoid CellularCapabilityGSM::GetSignalQuality() {
418c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein  SLOG(this, 2) << __func__;
4199a24553461df7036755060423f90804011612249Eric Shienbrood  SignalQualityCallback callback =
4209a24553461df7036755060423f90804011612249Eric Shienbrood      Bind(&CellularCapabilityGSM::OnGetSignalQualityReply,
4219a24553461df7036755060423f90804011612249Eric Shienbrood           weak_ptr_factory_.GetWeakPtr());
422ea18c6c99743aeee9b2e544532ef9fe55dbd182dBen Chan  network_proxy_->GetSignalQuality(nullptr, callback, kTimeoutDefault);
4233e509242f1a0e2e018a5c18a8d7b9224c6044d9aDarin Petkov}
4243e509242f1a0e2e018a5c18a8d7b9224c6044d9aDarin Petkov
4259a24553461df7036755060423f90804011612249Eric Shienbroodvoid CellularCapabilityGSM::GetRegistrationState() {
426c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein  SLOG(this, 2) << __func__;
4279a24553461df7036755060423f90804011612249Eric Shienbrood  RegistrationInfoCallback callback =
4289a24553461df7036755060423f90804011612249Eric Shienbrood      Bind(&CellularCapabilityGSM::OnGetRegistrationInfoReply,
4299a24553461df7036755060423f90804011612249Eric Shienbrood           weak_ptr_factory_.GetWeakPtr());
430ea18c6c99743aeee9b2e544532ef9fe55dbd182dBen Chan  network_proxy_->GetRegistrationInfo(nullptr, callback, kTimeoutDefault);
4315de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood}
4325de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood
433f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewartvoid CellularCapabilityGSM::GetProperties(const ResultCallback& callback) {
434c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein  SLOG(this, 2) << __func__;
43563138a9b8249fd69c83a772ee3170551a589d57aDarin Petkov
436ee6b3d7f9d49fa52072a352fbb59f06127b1ba4cPaul Stewart  // TODO(petkov): Switch to asynchronous calls (crbug.com/200687).
4377fab89734d88724a288e96a9996b15548c5294c7Ben Chan  uint32_t tech = network_proxy_->AccessTechnology();
438ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkov  SetAccessTechnology(tech);
439c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein  SLOG(this, 2) << "GSM AccessTechnology: " << tech;
44063138a9b8249fd69c83a772ee3170551a589d57aDarin Petkov
441ee6b3d7f9d49fa52072a352fbb59f06127b1ba4cPaul Stewart  // TODO(petkov): Switch to asynchronous calls (crbug.com/200687).
4427fab89734d88724a288e96a9996b15548c5294c7Ben Chan  uint32_t locks = card_proxy_->EnabledFacilityLocks();
44363138a9b8249fd69c83a772ee3170551a589d57aDarin Petkov  sim_lock_status_.enabled = locks & MM_MODEM_GSM_FACILITY_SIM;
444c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein  SLOG(this, 2) << "GSM EnabledFacilityLocks: " << locks;
44563138a9b8249fd69c83a772ee3170551a589d57aDarin Petkov
4469a24553461df7036755060423f90804011612249Eric Shienbrood  callback.Run(Error());
447184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov}
448184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov
4499a24553461df7036755060423f90804011612249Eric Shienbrood// always called from an async context
450f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewartvoid CellularCapabilityGSM::Register(const ResultCallback& callback) {
451c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein  SLOG(this, 2) << __func__ << " \"" << cellular()->selected_network()
452c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein                << "\"";
4539a24553461df7036755060423f90804011612249Eric Shienbrood  CHECK(!callback.is_null());
4549a24553461df7036755060423f90804011612249Eric Shienbrood  Error error;
4559a24553461df7036755060423f90804011612249Eric Shienbrood  ResultCallback cb = Bind(&CellularCapabilityGSM::OnRegisterReply,
4569a24553461df7036755060423f90804011612249Eric Shienbrood                                weak_ptr_factory_.GetWeakPtr(), callback);
4579f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu  network_proxy_->Register(cellular()->selected_network(), &error, cb,
4589f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu                           kTimeoutRegister);
4599a24553461df7036755060423f90804011612249Eric Shienbrood  if (error.IsFailure())
4609a24553461df7036755060423f90804011612249Eric Shienbrood    callback.Run(error);
461184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov}
462184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov
463184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkovvoid CellularCapabilityGSM::RegisterOnNetwork(
464f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    const string& network_id,
465f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    Error* error,
466f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    const ResultCallback& callback) {
467c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein  SLOG(this, 2) << __func__ << "(" << network_id << ")";
4689a24553461df7036755060423f90804011612249Eric Shienbrood  CHECK(error);
4695de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  desired_network_ = network_id;
4709a24553461df7036755060423f90804011612249Eric Shienbrood  ResultCallback cb = Bind(&CellularCapabilityGSM::OnRegisterReply,
4719a24553461df7036755060423f90804011612249Eric Shienbrood                                weak_ptr_factory_.GetWeakPtr(), callback);
4729a24553461df7036755060423f90804011612249Eric Shienbrood  network_proxy_->Register(network_id, error, cb, kTimeoutRegister);
4735de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood}
4745de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood
475f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewartvoid CellularCapabilityGSM::OnRegisterReply(const ResultCallback& callback,
476f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                            const Error& error) {
477c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein  SLOG(this, 2) << __func__ << "(" << error << ")";
4785de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood
4795de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  if (error.IsSuccess()) {
4809f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu    cellular()->set_selected_network(desired_network_);
4815de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood    desired_network_.clear();
4829a24553461df7036755060423f90804011612249Eric Shienbrood    callback.Run(error);
4835de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood    return;
4845de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  }
4855de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  // If registration on the desired network failed,
4865de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  // try to register on the home network.
4875de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  if (!desired_network_.empty()) {
4885de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood    desired_network_.clear();
4899f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu    cellular()->set_selected_network("");
4909a24553461df7036755060423f90804011612249Eric Shienbrood    LOG(INFO) << "Couldn't register on selected network, trying home network";
4919a24553461df7036755060423f90804011612249Eric Shienbrood    Register(callback);
4925de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood    return;
4935de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  }
4949a24553461df7036755060423f90804011612249Eric Shienbrood  callback.Run(error);
495184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov}
496184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov
49731ce564d64f68172299d4cde60edd7a5c004b38eBen Chanbool CellularCapabilityGSM::IsRegistered() const {
498b72cf40dc315f9ae3537980f4fd85e737c125a03Darin Petkov  return (registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
499b72cf40dc315f9ae3537980f4fd85e737c125a03Darin Petkov          registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING);
500b72cf40dc315f9ae3537980f4fd85e737c125a03Darin Petkov}
501b72cf40dc315f9ae3537980f4fd85e737c125a03Darin Petkov
5027fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbroodvoid CellularCapabilityGSM::SetUnregistered(bool searching) {
5037fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood  // If we're already in some non-registered state, don't override that
5047fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood  if (registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
5057fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood      registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) {
5067fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    registration_state_ =
5077fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood        (searching ? MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING :
5087fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood                     MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE);
5097fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood  }
5107fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood}
5117fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood
512b05315fa7beab387bcbfd9330215aa50cdd6c8f4Darin Petkovvoid CellularCapabilityGSM::RequirePIN(
513f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    const std::string& pin, bool require,
514f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    Error* error, const ResultCallback& callback) {
5159a24553461df7036755060423f90804011612249Eric Shienbrood  CHECK(error);
5169a24553461df7036755060423f90804011612249Eric Shienbrood  card_proxy_->EnablePIN(pin, require, error, callback, kTimeoutDefault);
517b05315fa7beab387bcbfd9330215aa50cdd6c8f4Darin Petkov}
518b05315fa7beab387bcbfd9330215aa50cdd6c8f4Darin Petkov
519f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewartvoid CellularCapabilityGSM::EnterPIN(const string& pin,
520f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                     Error* error,
521f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                     const ResultCallback& callback) {
5229a24553461df7036755060423f90804011612249Eric Shienbrood  CHECK(error);
5239a24553461df7036755060423f90804011612249Eric Shienbrood  card_proxy_->SendPIN(pin, error, callback, kTimeoutDefault);
524b05315fa7beab387bcbfd9330215aa50cdd6c8f4Darin Petkov}
525b05315fa7beab387bcbfd9330215aa50cdd6c8f4Darin Petkov
526f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewartvoid CellularCapabilityGSM::UnblockPIN(const string& unblock_code,
527f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                       const string& pin,
528f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                       Error* error,
529f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                       const ResultCallback& callback) {
5309a24553461df7036755060423f90804011612249Eric Shienbrood  CHECK(error);
5319a24553461df7036755060423f90804011612249Eric Shienbrood  card_proxy_->SendPUK(unblock_code, pin, error, callback, kTimeoutDefault);
532b05315fa7beab387bcbfd9330215aa50cdd6c8f4Darin Petkov}
533b05315fa7beab387bcbfd9330215aa50cdd6c8f4Darin Petkov
534b05315fa7beab387bcbfd9330215aa50cdd6c8f4Darin Petkovvoid CellularCapabilityGSM::ChangePIN(
535f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    const string& old_pin, const string& new_pin,
536f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    Error* error, const ResultCallback& callback) {
5379a24553461df7036755060423f90804011612249Eric Shienbrood  CHECK(error);
5389a24553461df7036755060423f90804011612249Eric Shienbrood  card_proxy_->ChangePIN(old_pin, new_pin, error, callback, kTimeoutDefault);
539b05315fa7beab387bcbfd9330215aa50cdd6c8f4Darin Petkov}
540b05315fa7beab387bcbfd9330215aa50cdd6c8f4Darin Petkov
541f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewartvoid CellularCapabilityGSM::Scan(Error* error,
542f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                 const ResultStringmapsCallback& callback) {
5439a24553461df7036755060423f90804011612249Eric Shienbrood  ScanResultsCallback cb = Bind(&CellularCapabilityGSM::OnScanReply,
5449a24553461df7036755060423f90804011612249Eric Shienbrood                                weak_ptr_factory_.GetWeakPtr(), callback);
5459a24553461df7036755060423f90804011612249Eric Shienbrood  network_proxy_->Scan(error, cb, kTimeoutScan);
5461272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov}
5471272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov
54849ffffd458c2407c67c2d0d572974e91c7e9bea7Prathmesh Prabhuvoid CellularCapabilityGSM::OnScanReply(
549f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    const ResultStringmapsCallback& callback,
550f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    const GSMScanResults& results,
551f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    const Error& error) {
5529f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu  Stringmaps found_networks;
553f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart  for (const auto& result : results)
55449ffffd458c2407c67c2d0d572974e91c7e9bea7Prathmesh Prabhu    found_networks.push_back(ParseScanResult(result));
55549ffffd458c2407c67c2d0d572974e91c7e9bea7Prathmesh Prabhu  callback.Run(found_networks, error);
5561272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov}
5571272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov
558f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul StewartStringmap CellularCapabilityGSM::ParseScanResult(const GSMScanResult& result) {
5591272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov  Stringmap parsed;
5605de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  for (GSMScanResult::const_iterator it = result.begin();
5615de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood       it != result.end(); ++it) {
5621272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov    // TODO(petkov): Define these in system_api/service_constants.h. The
5631272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov    // numerical values are taken from 3GPP TS 27.007 Section 7.3.
564f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    static const char* const kStatusString[] = {
5651272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov      "unknown",
5661272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov      "available",
5671272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov      "current",
5681272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov      "forbidden",
5691272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov    };
570f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    static const char* const kTechnologyString[] = {
5717ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan      kNetworkTechnologyGsm,
5721272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov      "GSM Compact",
5737ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan      kNetworkTechnologyUmts,
5747ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan      kNetworkTechnologyEdge,
5751272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov      "HSDPA",
5761272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov      "HSUPA",
5777ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan      kNetworkTechnologyHspa,
5781272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov    };
579c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein    SLOG(this, 2) << "Network property: " << it->first << " = "
580c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein                  << it->second;
5811272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov    if (it->first == kNetworkPropertyStatus) {
5821272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov      int status = 0;
5831272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov      if (base::StringToInt(it->second, &status) &&
5841272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov          status >= 0 &&
5851272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov          status < static_cast<int>(arraysize(kStatusString))) {
5867ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan        parsed[kStatusProperty] = kStatusString[status];
5871272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov      } else {
5881272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov        LOG(ERROR) << "Unexpected status value: " << it->second;
5891272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov      }
5901272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov    } else if (it->first == kNetworkPropertyID) {
5917ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan      parsed[kNetworkIdProperty] = it->second;
5921272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov    } else if (it->first == kNetworkPropertyLongName) {
5937ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan      parsed[kLongNameProperty] = it->second;
5941272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov    } else if (it->first == kNetworkPropertyShortName) {
5957ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan      parsed[kShortNameProperty] = it->second;
5961272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov    } else if (it->first == kNetworkPropertyAccessTechnology) {
5971272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov      int tech = 0;
5981272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov      if (base::StringToInt(it->second, &tech) &&
5991272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov          tech >= 0 &&
6001272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov          tech < static_cast<int>(arraysize(kTechnologyString))) {
6017ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan        parsed[kTechnologyProperty] = kTechnologyString[tech];
6021272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov      } else {
6031272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov        LOG(ERROR) << "Unexpected technology value: " << it->second;
6041272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov      }
6051272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov    } else {
6061272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov      LOG(WARNING) << "Unknown network property ignored: " << it->first;
6071272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov    }
6081272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov  }
6091272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov  // If the long name is not available but the network ID is, look up the long
6101272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov  // name in the mobile provider database.
6117ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan  if ((!ContainsKey(parsed, kLongNameProperty) ||
6127ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan       parsed[kLongNameProperty].empty()) &&
6137ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan      ContainsKey(parsed, kNetworkIdProperty)) {
614afe636676760df31a8db2f4b77bfd19faae52782Prathmesh Prabhu    mobile_operator_info_->Reset();
615afe636676760df31a8db2f4b77bfd19faae52782Prathmesh Prabhu    mobile_operator_info_->UpdateMCCMNC(parsed[kNetworkIdProperty]);
616afe636676760df31a8db2f4b77bfd19faae52782Prathmesh Prabhu    if (mobile_operator_info_->IsMobileNetworkOperatorKnown() &&
617afe636676760df31a8db2f4b77bfd19faae52782Prathmesh Prabhu        !mobile_operator_info_->operator_name().empty()) {
618afe636676760df31a8db2f4b77bfd19faae52782Prathmesh Prabhu      parsed[kLongNameProperty] = mobile_operator_info_->operator_name();
6191272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov    }
6201272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov  }
6211272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov  return parsed;
6221272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov}
6231272a43bf73f6a99f51a6cc6345183aa503cfb67Darin Petkov
6247fab89734d88724a288e96a9996b15548c5294c7Ben Chanvoid CellularCapabilityGSM::SetAccessTechnology(uint32_t access_technology) {
625ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkov  access_technology_ = access_technology;
626ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkov  if (cellular()->service().get()) {
627b72cf40dc315f9ae3537980f4fd85e737c125a03Darin Petkov    cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
628ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkov  }
629ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkov}
630ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkov
63120c13ec645faacb9c517fd2c574aa7fa30d890d0Darin Petkovstring CellularCapabilityGSM::GetNetworkTechnologyString() const {
632b72cf40dc315f9ae3537980f4fd85e737c125a03Darin Petkov  switch (access_technology_) {
633b72cf40dc315f9ae3537980f4fd85e737c125a03Darin Petkov    case MM_MODEM_GSM_ACCESS_TECH_GSM:
634b72cf40dc315f9ae3537980f4fd85e737c125a03Darin Petkov    case MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT:
6357ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan      return kNetworkTechnologyGsm;
636b72cf40dc315f9ae3537980f4fd85e737c125a03Darin Petkov    case MM_MODEM_GSM_ACCESS_TECH_GPRS:
6377ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan      return kNetworkTechnologyGprs;
638b72cf40dc315f9ae3537980f4fd85e737c125a03Darin Petkov    case MM_MODEM_GSM_ACCESS_TECH_EDGE:
6397ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan      return kNetworkTechnologyEdge;
640b72cf40dc315f9ae3537980f4fd85e737c125a03Darin Petkov    case MM_MODEM_GSM_ACCESS_TECH_UMTS:
6417ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan      return kNetworkTechnologyUmts;
642b72cf40dc315f9ae3537980f4fd85e737c125a03Darin Petkov    case MM_MODEM_GSM_ACCESS_TECH_HSDPA:
643b72cf40dc315f9ae3537980f4fd85e737c125a03Darin Petkov    case MM_MODEM_GSM_ACCESS_TECH_HSUPA:
644b72cf40dc315f9ae3537980f4fd85e737c125a03Darin Petkov    case MM_MODEM_GSM_ACCESS_TECH_HSPA:
6457ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan      return kNetworkTechnologyHspa;
646b72cf40dc315f9ae3537980f4fd85e737c125a03Darin Petkov    case MM_MODEM_GSM_ACCESS_TECH_HSPA_PLUS:
6477ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan      return kNetworkTechnologyHspaPlus;
648b72cf40dc315f9ae3537980f4fd85e737c125a03Darin Petkov    default:
649b72cf40dc315f9ae3537980f4fd85e737c125a03Darin Petkov      break;
65020c13ec645faacb9c517fd2c574aa7fa30d890d0Darin Petkov  }
65120c13ec645faacb9c517fd2c574aa7fa30d890d0Darin Petkov  return "";
65220c13ec645faacb9c517fd2c574aa7fa30d890d0Darin Petkov}
65320c13ec645faacb9c517fd2c574aa7fa30d890d0Darin Petkov
65420c13ec645faacb9c517fd2c574aa7fa30d890d0Darin Petkovstring CellularCapabilityGSM::GetRoamingStateString() const {
655184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov  switch (registration_state_) {
65620c13ec645faacb9c517fd2c574aa7fa30d890d0Darin Petkov    case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME:
6577ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan      return kRoamingStateHome;
65820c13ec645faacb9c517fd2c574aa7fa30d890d0Darin Petkov    case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING:
6597ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan      return kRoamingStateRoaming;
66020c13ec645faacb9c517fd2c574aa7fa30d890d0Darin Petkov    default:
66120c13ec645faacb9c517fd2c574aa7fa30d890d0Darin Petkov      break;
66220c13ec645faacb9c517fd2c574aa7fa30d890d0Darin Petkov  }
6637ea768e15de860c8a27143ead02a8d3f15ab9f47Ben Chan  return kRoamingStateUnknown;
66420c13ec645faacb9c517fd2c574aa7fa30d890d0Darin Petkov}
66520c13ec645faacb9c517fd2c574aa7fa30d890d0Darin Petkov
66605d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiuvoid CellularCapabilityGSM::OnPropertiesChanged(
667f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    const string& interface,
66805d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu    const KeyValueStore& properties,
669f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    const vector<string>& invalidated_properties) {
67005d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu  CellularCapabilityClassic::OnPropertiesChanged(interface,
67105d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu                                                 properties,
67205d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu                                                 invalidated_properties);
6737fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood  if (interface == MM_MODEM_GSM_NETWORK_INTERFACE) {
67405d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu    if (properties.ContainsUint(kPropertyAccessTechnology)) {
67505d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu      SetAccessTechnology(properties.GetUint(kPropertyAccessTechnology));
6767fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    }
677baeefdf544bfcfe2895e4e15c348db3f7ce4d45bGary Morain  } else {
6787fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    bool emit = false;
679baeefdf544bfcfe2895e4e15c348db3f7ce4d45bGary Morain    if (interface == MM_MODEM_GSM_CARD_INTERFACE) {
68005d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu      if (properties.ContainsUint(kPropertyEnabledFacilityLocks)) {
68105d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu        uint32_t locks = properties.GetUint(kPropertyEnabledFacilityLocks);
682baeefdf544bfcfe2895e4e15c348db3f7ce4d45bGary Morain        sim_lock_status_.enabled = locks & MM_MODEM_GSM_FACILITY_SIM;
683baeefdf544bfcfe2895e4e15c348db3f7ce4d45bGary Morain        emit = true;
684baeefdf544bfcfe2895e4e15c348db3f7ce4d45bGary Morain      }
685baeefdf544bfcfe2895e4e15c348db3f7ce4d45bGary Morain    } else if (interface == MM_MODEM_INTERFACE) {
68605d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu      if (properties.ContainsString(kPropertyUnlockRequired)) {
68705d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu        sim_lock_status_.lock_type =
68805d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu            properties.GetString(kPropertyUnlockRequired);
689baeefdf544bfcfe2895e4e15c348db3f7ce4d45bGary Morain        emit = true;
690baeefdf544bfcfe2895e4e15c348db3f7ce4d45bGary Morain      }
69105d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu      if (properties.ContainsUint(kPropertyUnlockRetries)) {
69205d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu        sim_lock_status_.retries_left =
69305d87e34869f44473edc1eec25d2fe1110d777a7Peter Qiu            properties.GetUint(kPropertyUnlockRetries);
694baeefdf544bfcfe2895e4e15c348db3f7ce4d45bGary Morain        emit = true;
695baeefdf544bfcfe2895e4e15c348db3f7ce4d45bGary Morain      }
6967fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    }
6979f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu    // TODO(pprabhu) Rename |emit| to |sim_present| after |sim_lock_status|
6989f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu    // moves to cellular.
6997fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    if (emit) {
7009f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu      cellular()->set_sim_present(true);
7017fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood      cellular()->adaptor()->EmitKeyValueStoreChanged(
702ea18c6c99743aeee9b2e544532ef9fe55dbd182dBen Chan          kSIMLockStatusProperty, SimLockStatusToProperty(nullptr));
7037fce52c4afdc5e73a9e740dc9b90f1e61ae8cea4Eric Shienbrood    }
70463138a9b8249fd69c83a772ee3170551a589d57aDarin Petkov  }
705ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkov}
706ae0c64ea644748f28b7a320e9663f30cd69e344eDarin Petkov
7077fab89734d88724a288e96a9996b15548c5294c7Ben Chanvoid CellularCapabilityGSM::OnNetworkModeSignal(uint32_t /*mode*/) {
708184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov  // TODO(petkov): Implement this.
709184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov  NOTIMPLEMENTED();
710184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov}
711184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov
7129a24553461df7036755060423f90804011612249Eric Shienbroodvoid CellularCapabilityGSM::OnRegistrationInfoSignal(
713f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    uint32_t status, const string& operator_code, const string& operator_name) {
714c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein  SLOG(this, 2) << __func__ << ": regstate=" << status
715c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein                << ", opercode=" << operator_code
716c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein                << ", opername=" << operator_name;
7179a24553461df7036755060423f90804011612249Eric Shienbrood  registration_state_ = status;
7188599e052529c222da6cefd934bd52ceb9c194f5fPrathmesh Prabhu  cellular()->serving_operator_info()->UpdateMCCMNC(operator_code);
7198599e052529c222da6cefd934bd52ceb9c194f5fPrathmesh Prabhu  cellular()->serving_operator_info()->UpdateOperatorName(operator_name);
7209a24553461df7036755060423f90804011612249Eric Shienbrood  cellular()->HandleNewRegistrationState();
721184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov}
722184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov
7237fab89734d88724a288e96a9996b15548c5294c7Ben Chanvoid CellularCapabilityGSM::OnSignalQualitySignal(uint32_t quality) {
724184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov  cellular()->HandleNewSignalQuality(quality);
725184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov}
726184c54e6091b21fc2a8ba40d8957ca6c84c37d6eDarin Petkov
7279a24553461df7036755060423f90804011612249Eric Shienbroodvoid CellularCapabilityGSM::OnGetRegistrationInfoReply(
728f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    uint32_t status, const string& operator_code, const string& operator_name,
729f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart    const Error& error) {
7309a24553461df7036755060423f90804011612249Eric Shienbrood  if (error.IsSuccess())
7319a24553461df7036755060423f90804011612249Eric Shienbrood    OnRegistrationInfoSignal(status, operator_code, operator_name);
7329a24553461df7036755060423f90804011612249Eric Shienbrood}
7339a24553461df7036755060423f90804011612249Eric Shienbrood
7347fab89734d88724a288e96a9996b15548c5294c7Ben Chanvoid CellularCapabilityGSM::OnGetSignalQualityReply(uint32_t quality,
735f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                                    const Error& error) {
7369a24553461df7036755060423f90804011612249Eric Shienbrood  if (error.IsSuccess())
7379a24553461df7036755060423f90804011612249Eric Shienbrood    OnSignalQualitySignal(quality);
7389a24553461df7036755060423f90804011612249Eric Shienbrood}
7399a24553461df7036755060423f90804011612249Eric Shienbrood
740f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewartvoid CellularCapabilityGSM::OnGetIMEIReply(const ResultCallback& callback,
741f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                           const string& imei,
742f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                           const Error& error) {
7435de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  if (error.IsSuccess()) {
744c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein    SLOG(this, 2) << "IMEI: " << imei;
7459f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu    cellular()->set_imei(imei);
7465de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  } else {
747c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein    SLOG(this, 2) << "GetIMEI failed - " << error;
7485de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  }
7499a24553461df7036755060423f90804011612249Eric Shienbrood  callback.Run(error);
7505de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood}
7515de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood
752f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewartvoid CellularCapabilityGSM::OnGetIMSIReply(const ResultCallback& callback,
753f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                           const string& imsi,
754f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                           const Error& error) {
7555de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  if (error.IsSuccess()) {
756c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein    SLOG(this, 2) << "IMSI: " << imsi;
7579f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu    cellular()->set_imsi(imsi);
7589f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu    cellular()->set_sim_present(true);
7598599e052529c222da6cefd934bd52ceb9c194f5fPrathmesh Prabhu    cellular()->home_provider_info()->UpdateIMSI(imsi);
760c93b6bc9b0fdb1a04240cca858a8f61add0a21e3Prathmesh Prabhu    // We do not currently obtain the IMSI OTA at all. Provide the IMSI from the
761c93b6bc9b0fdb1a04240cca858a8f61add0a21e3Prathmesh Prabhu    // SIM to the serving operator as well to aid in MVNO identification.
762c93b6bc9b0fdb1a04240cca858a8f61add0a21e3Prathmesh Prabhu    cellular()->serving_operator_info()->UpdateIMSI(imsi);
76382a31a0323d2f7b72045e5ee6ef25f9a7dfbc3bbGary Morain    callback.Run(error);
76429505ec166c72afd3dee2317795ca13f7ff94e10Arman Uguray  } else if (!sim_lock_status_.lock_type.empty()) {
765c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein    SLOG(this, 2) << "GetIMSI failed - SIM lock in place.";
7669f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu    cellular()->set_sim_present(true);
76729505ec166c72afd3dee2317795ca13f7ff94e10Arman Uguray    callback.Run(error);
7685de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  } else {
7699f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu    cellular()->set_sim_present(false);
77082a31a0323d2f7b72045e5ee6ef25f9a7dfbc3bbGary Morain    if (get_imsi_retries_++ < kGetIMSIRetryLimit) {
771c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein      SLOG(this, 2) << "GetIMSI failed - " << error << ". Retrying";
77282a31a0323d2f7b72045e5ee6ef25f9a7dfbc3bbGary Morain      base::Callback<void(void)> retry_get_imsi_cb =
77382a31a0323d2f7b72045e5ee6ef25f9a7dfbc3bbGary Morain          Bind(&CellularCapabilityGSM::GetIMSI,
77482a31a0323d2f7b72045e5ee6ef25f9a7dfbc3bbGary Morain               weak_ptr_factory_.GetWeakPtr(), callback);
77582a31a0323d2f7b72045e5ee6ef25f9a7dfbc3bbGary Morain      cellular()->dispatcher()->PostDelayedTask(
77682a31a0323d2f7b72045e5ee6ef25f9a7dfbc3bbGary Morain          retry_get_imsi_cb,
77782a31a0323d2f7b72045e5ee6ef25f9a7dfbc3bbGary Morain          get_imsi_retry_delay_milliseconds_);
77882a31a0323d2f7b72045e5ee6ef25f9a7dfbc3bbGary Morain    } else {
77982a31a0323d2f7b72045e5ee6ef25f9a7dfbc3bbGary Morain      LOG(INFO) << "GetIMSI failed - " << error;
7808599e052529c222da6cefd934bd52ceb9c194f5fPrathmesh Prabhu      cellular()->home_provider_info()->Reset();
78182a31a0323d2f7b72045e5ee6ef25f9a7dfbc3bbGary Morain      callback.Run(error);
78282a31a0323d2f7b72045e5ee6ef25f9a7dfbc3bbGary Morain    }
7835de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  }
7845de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood}
7855de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood
786f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewartvoid CellularCapabilityGSM::OnGetSPNReply(const ResultCallback& callback,
787f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                          const string& spn,
788f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                          const Error& error) {
7895de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  if (error.IsSuccess()) {
790c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein    SLOG(this, 2) << "SPN: " << spn;
7915de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood    spn_ = spn;
7928599e052529c222da6cefd934bd52ceb9c194f5fPrathmesh Prabhu    cellular()->home_provider_info()->UpdateOperatorName(spn);
7935de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  } else {
794c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein    SLOG(this, 2) << "GetSPN failed - " << error;
7955de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  }
796923006bc5f3fde53ea8651de16200d7226f065a2Thieu Le  callback.Run(error);
7975de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood}
7985de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood
799f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewartvoid CellularCapabilityGSM::OnGetMSISDNReply(const ResultCallback& callback,
800f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                             const string& msisdn,
801f58b28e08efb9e3beccb993bc27dd68e0025a7daPaul Stewart                                             const Error& error) {
8025de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  if (error.IsSuccess()) {
803c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein    SLOG(this, 2) << "MSISDN: " << msisdn;
8049f06c870229b00cdd19224cb12c4202de65772aePrathmesh Prabhu    cellular()->set_mdn(msisdn);
8055de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  } else {
806c9c31d8497c3f053c2160408cc386010fc125fadRebecca Silberstein    SLOG(this, 2) << "GetMSISDN failed - " << error;
8075de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood  }
808923006bc5f3fde53ea8651de16200d7226f065a2Thieu Le  callback.Run(error);
8095de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood}
8105de44ab664b7cbb7619e31431e346ec8309548a6Eric Shienbrood
811daf4386a4775be7c965493749ccfe2fecc2e4167Darin Petkov}  // namespace shill
812