blob: e7b887bf4628bc11427cdede13101a69cd191769 [file] [log] [blame]
Darin Petkovc64fe5e2012-01-11 12:46:13 +01001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Darin Petkovdaf43862011-10-27 11:37:28 +02002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/cellular_capability_gsm.h"
6
Jason Glasgow4c0724a2012-04-17 15:47:40 -04007#include <string>
8#include <vector>
9
Eric Shienbrood3e20a232012-02-16 11:35:56 -050010#include <base/bind.h>
Eric Shienbrood3e20a232012-02-16 11:35:56 -050011#include <base/stl_util.h>
Darin Petkov1272a432011-11-10 15:53:37 +010012#include <base/string_number_conversions.h>
Darin Petkovac635a82012-01-10 16:51:58 +010013#include <base/stringprintf.h>
Darin Petkov20c13ec2011-11-09 15:07:15 +010014#include <chromeos/dbus/service_constants.h>
15#include <mm/mm-modem.h>
Darin Petkov1272a432011-11-10 15:53:37 +010016#include <mobile_provider.h>
Darin Petkovdaf43862011-10-27 11:37:28 +020017
Darin Petkov3cfbf212011-11-21 16:02:09 +010018#include "shill/adaptor_interfaces.h"
Darin Petkovae0c64e2011-11-15 15:50:27 +010019#include "shill/cellular_service.h"
Eric Shienbrood5de44ab2011-12-05 10:46:27 -050020#include "shill/error.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070021#include "shill/logging.h"
Darin Petkov721ac932011-11-16 15:43:09 +010022#include "shill/property_accessor.h"
Darin Petkovdaf43862011-10-27 11:37:28 +020023#include "shill/proxy_factory.h"
24
Eric Shienbrood3e20a232012-02-16 11:35:56 -050025using base::Bind;
Darin Petkovb05315f2011-11-07 10:14:25 +010026using std::string;
Jason Glasgow4c0724a2012-04-17 15:47:40 -040027using std::vector;
Darin Petkovb05315f2011-11-07 10:14:25 +010028
Darin Petkovdaf43862011-10-27 11:37:28 +020029namespace shill {
30
Darin Petkovac635a82012-01-10 16:51:58 +010031// static
32unsigned int CellularCapabilityGSM::friendly_service_name_id_ = 0;
33
Darin Petkov1272a432011-11-10 15:53:37 +010034const char CellularCapabilityGSM::kNetworkPropertyAccessTechnology[] =
35 "access-tech";
36const char CellularCapabilityGSM::kNetworkPropertyID[] = "operator-num";
37const char CellularCapabilityGSM::kNetworkPropertyLongName[] = "operator-long";
38const char CellularCapabilityGSM::kNetworkPropertyShortName[] =
39 "operator-short";
40const char CellularCapabilityGSM::kNetworkPropertyStatus[] = "status";
Darin Petkovae0c64e2011-11-15 15:50:27 +010041const char CellularCapabilityGSM::kPhoneNumber[] = "*99#";
42const char CellularCapabilityGSM::kPropertyAccessTechnology[] =
43 "AccessTechnology";
Darin Petkov63138a92012-02-06 14:09:15 +010044const char CellularCapabilityGSM::kPropertyEnabledFacilityLocks[] =
45 "EnabledFacilityLocks";
Darin Petkov721ac932011-11-16 15:43:09 +010046const char CellularCapabilityGSM::kPropertyUnlockRequired[] = "UnlockRequired";
47const char CellularCapabilityGSM::kPropertyUnlockRetries[] = "UnlockRetries";
Darin Petkov1272a432011-11-10 15:53:37 +010048
Gary Morain82a31a02012-08-02 18:03:32 -070049const int CellularCapabilityGSM::kGetIMSIRetryLimit = 10;
50const int64 CellularCapabilityGSM::kGetIMSIRetryDelayMilliseconds = 200;
51
52
Eric Shienbrood5de44ab2011-12-05 10:46:27 -050053CellularCapabilityGSM::CellularCapabilityGSM(Cellular *cellular,
54 ProxyFactory *proxy_factory)
Jason Glasgow82f9ab32012-04-04 14:27:19 -040055 : CellularCapabilityClassic(cellular, proxy_factory),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050056 weak_ptr_factory_(this),
Darin Petkov184c54e2011-11-15 12:44:39 +010057 registration_state_(MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN),
Darin Petkovae0c64e2011-11-15 15:50:27 +010058 access_technology_(MM_MODEM_GSM_ACCESS_TECH_UNKNOWN),
Darin Petkov0a4dfeb2011-11-18 19:36:13 +010059 home_provider_(NULL),
Darin Petkovf508c822012-09-21 13:43:17 +020060 provider_requires_roaming_(false),
Gary Morain82a31a02012-08-02 18:03:32 -070061 get_imsi_retries_(0),
62 get_imsi_retry_delay_milliseconds_(kGetIMSIRetryDelayMilliseconds),
Darin Petkov1272a432011-11-10 15:53:37 +010063 scanning_(false),
64 scan_interval_(0) {
Ben Chanfad4a0b2012-04-18 15:49:59 -070065 SLOG(Cellular, 2) << "Cellular capability constructed: GSM";
Darin Petkov1272a432011-11-10 15:53:37 +010066 PropertyStore *store = cellular->mutable_store();
Darin Petkov184c54e2011-11-15 12:44:39 +010067 store->RegisterConstString(flimflam::kSelectedNetworkProperty,
68 &selected_network_);
Darin Petkov1272a432011-11-10 15:53:37 +010069 store->RegisterConstStringmaps(flimflam::kFoundNetworksProperty,
70 &found_networks_);
Darin Petkovf508c822012-09-21 13:43:17 +020071 store->RegisterConstBool(shill::kProviderRequiresRoamingProperty,
72 &provider_requires_roaming_);
Darin Petkov1272a432011-11-10 15:53:37 +010073 store->RegisterConstBool(flimflam::kScanningProperty, &scanning_);
74 store->RegisterUint16(flimflam::kScanIntervalProperty, &scan_interval_);
Darin Petkov63138a92012-02-06 14:09:15 +010075 HelpRegisterDerivedKeyValueStore(
76 flimflam::kSIMLockStatusProperty,
77 &CellularCapabilityGSM::SimLockStatusToProperty,
78 NULL);
Darin Petkov3cfbf212011-11-21 16:02:09 +010079 store->RegisterConstStringmaps(flimflam::kCellularApnListProperty,
80 &apn_list_);
Darin Petkov0d06f7d2012-02-03 13:08:19 +010081 scanning_supported_ = true;
Ben Chan3ecdf822012-08-06 12:29:23 -070082
83 // TODO(benchan): This is a hack to initialize the GSM card proxy for GetIMSI
84 // before InitProxies is called. There are side-effects of calling InitProxies
85 // before the device is enabled. It's better to refactor InitProxies such that
86 // proxies can be created when the cellular device/capability is constructed,
87 // but callbacks for DBus signal updates are not set up until the device is
88 // enabled.
89 card_proxy_.reset(
90 proxy_factory->CreateModemGSMCardProxy(cellular->dbus_path(),
91 cellular->dbus_owner()));
92 // TODO(benchan): To allow unit testing using a mock proxy without further
93 // complicating the code, the test proxy factory is set up to return a NULL
94 // pointer when CellularCapabilityGSM is constructed. Refactor the code to
95 // avoid this hack.
96 if (card_proxy_.get())
97 InitProperties();
Darin Petkov1272a432011-11-10 15:53:37 +010098}
Darin Petkovdaf43862011-10-27 11:37:28 +020099
Darin Petkov63138a92012-02-06 14:09:15 +0100100KeyValueStore CellularCapabilityGSM::SimLockStatusToProperty(Error */*error*/) {
101 KeyValueStore status;
102 status.SetBool(flimflam::kSIMLockEnabledProperty, sim_lock_status_.enabled);
103 status.SetString(flimflam::kSIMLockTypeProperty, sim_lock_status_.lock_type);
104 status.SetUint(flimflam::kSIMLockRetriesLeftProperty,
105 sim_lock_status_.retries_left);
106 return status;
Darin Petkov721ac932011-11-16 15:43:09 +0100107}
108
Darin Petkov63138a92012-02-06 14:09:15 +0100109void CellularCapabilityGSM::HelpRegisterDerivedKeyValueStore(
Darin Petkov721ac932011-11-16 15:43:09 +0100110 const string &name,
Darin Petkov63138a92012-02-06 14:09:15 +0100111 KeyValueStore(CellularCapabilityGSM::*get)(Error *error),
112 void(CellularCapabilityGSM::*set)(
113 const KeyValueStore &value, Error *error)) {
114 cellular()->mutable_store()->RegisterDerivedKeyValueStore(
Darin Petkov721ac932011-11-16 15:43:09 +0100115 name,
Darin Petkov63138a92012-02-06 14:09:15 +0100116 KeyValueStoreAccessor(
117 new CustomAccessor<CellularCapabilityGSM, KeyValueStore>(
Darin Petkov721ac932011-11-16 15:43:09 +0100118 this, get, set)));
119}
120
Eric Shienbrood9a245532012-03-07 14:20:39 -0500121void CellularCapabilityGSM::InitProxies() {
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400122 CellularCapabilityClassic::InitProxies();
Ben Chan3ecdf822012-08-06 12:29:23 -0700123 // TODO(benchan): Remove this check after refactoring the proxy
124 // initialization.
125 if (!card_proxy_.get()) {
126 card_proxy_.reset(
127 proxy_factory()->CreateModemGSMCardProxy(cellular()->dbus_path(),
128 cellular()->dbus_owner()));
129 }
Darin Petkov184c54e2011-11-15 12:44:39 +0100130 network_proxy_.reset(
Eric Shienbrood9a245532012-03-07 14:20:39 -0500131 proxy_factory()->CreateModemGSMNetworkProxy(cellular()->dbus_path(),
Darin Petkovdaf43862011-10-27 11:37:28 +0200132 cellular()->dbus_owner()));
Eric Shienbrood9a245532012-03-07 14:20:39 -0500133 network_proxy_->set_signal_quality_callback(
134 Bind(&CellularCapabilityGSM::OnSignalQualitySignal,
135 weak_ptr_factory_.GetWeakPtr()));
136 network_proxy_->set_network_mode_callback(
137 Bind(&CellularCapabilityGSM::OnNetworkModeSignal,
138 weak_ptr_factory_.GetWeakPtr()));
139 network_proxy_->set_registration_info_callback(
140 Bind(&CellularCapabilityGSM::OnRegistrationInfoSignal,
141 weak_ptr_factory_.GetWeakPtr()));
Darin Petkovdaf43862011-10-27 11:37:28 +0200142}
143
Ben Chan3ecdf822012-08-06 12:29:23 -0700144void CellularCapabilityGSM::InitProperties() {
145 CellularTaskList *tasks = new CellularTaskList();
146 ResultCallback cb_ignore_error =
147 Bind(&CellularCapabilityGSM::StepCompletedCallback,
148 weak_ptr_factory_.GetWeakPtr(), ResultCallback(), true, tasks);
149 // Chrome uses IMSI to determine if a SIM is present before allowing the
150 // modem to be enabled, so shill needs to obtain IMSI even before the device
151 // is enabled.
152 tasks->push_back(Bind(&CellularCapabilityGSM::GetIMSI,
153 weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
154 RunNextStep(tasks);
155}
156
Eric Shienbrood9a245532012-03-07 14:20:39 -0500157void CellularCapabilityGSM::StartModem(Error *error,
158 const ResultCallback &callback) {
159 InitProxies();
160
161 CellularTaskList *tasks = new CellularTaskList();
162 ResultCallback cb =
163 Bind(&CellularCapabilityGSM::StepCompletedCallback,
Thieu Le923006b2012-04-05 16:32:58 -0700164 weak_ptr_factory_.GetWeakPtr(), callback, false, tasks);
165 ResultCallback cb_ignore_error =
166 Bind(&CellularCapabilityGSM::StepCompletedCallback,
167 weak_ptr_factory_.GetWeakPtr(), callback, true, tasks);
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400168 if (!cellular()->IsUnderlyingDeviceEnabled())
169 tasks->push_back(Bind(&CellularCapabilityGSM::EnableModem,
170 weak_ptr_factory_.GetWeakPtr(), cb));
171 // If we're within range of the home network, the modem will try to
172 // register once it's enabled, or may be already registered if we
173 // started out enabled.
174 if (!IsUnderlyingDeviceRegistered() && !selected_network_.empty())
175 tasks->push_back(Bind(&CellularCapabilityGSM::Register,
176 weak_ptr_factory_.GetWeakPtr(), cb));
Eric Shienbrood9a245532012-03-07 14:20:39 -0500177 tasks->push_back(Bind(&CellularCapabilityGSM::GetIMEI,
178 weak_ptr_factory_.GetWeakPtr(), cb));
Gary Morain82a31a02012-08-02 18:03:32 -0700179 get_imsi_retries_ = 0;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500180 tasks->push_back(Bind(&CellularCapabilityGSM::GetIMSI,
181 weak_ptr_factory_.GetWeakPtr(), cb));
182 tasks->push_back(Bind(&CellularCapabilityGSM::GetSPN,
Thieu Le923006b2012-04-05 16:32:58 -0700183 weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
Eric Shienbrood9a245532012-03-07 14:20:39 -0500184 tasks->push_back(Bind(&CellularCapabilityGSM::GetMSISDN,
Thieu Le923006b2012-04-05 16:32:58 -0700185 weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
Eric Shienbrood9a245532012-03-07 14:20:39 -0500186 tasks->push_back(Bind(&CellularCapabilityGSM::GetProperties,
187 weak_ptr_factory_.GetWeakPtr(), cb));
188 tasks->push_back(Bind(&CellularCapabilityGSM::GetModemInfo,
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400189 weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
Eric Shienbrood9a245532012-03-07 14:20:39 -0500190 tasks->push_back(Bind(&CellularCapabilityGSM::FinishEnable,
191 weak_ptr_factory_.GetWeakPtr(), cb));
192
193 RunNextStep(tasks);
194}
195
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400196bool CellularCapabilityGSM::IsUnderlyingDeviceRegistered() const {
197 switch (cellular()->modem_state()) {
198 case Cellular::kModemStateUnknown:
199 case Cellular::kModemStateDisabled:
200 case Cellular::kModemStateInitializing:
201 case Cellular::kModemStateLocked:
202 case Cellular::kModemStateDisabling:
203 case Cellular::kModemStateEnabling:
204 case Cellular::kModemStateEnabled:
205 return false;
206 case Cellular::kModemStateSearching:
207 case Cellular::kModemStateRegistered:
208 case Cellular::kModemStateDisconnecting:
209 case Cellular::kModemStateConnecting:
210 case Cellular::kModemStateConnected:
211 return true;
212 }
213 return false;
214}
215
Eric Shienbrood9a245532012-03-07 14:20:39 -0500216void CellularCapabilityGSM::ReleaseProxies() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700217 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400218 CellularCapabilityClassic::ReleaseProxies();
Darin Petkov721ac932011-11-16 15:43:09 +0100219 card_proxy_.reset();
220 network_proxy_.reset();
221}
222
Darin Petkov5f316f62011-11-18 12:10:26 +0100223void CellularCapabilityGSM::OnServiceCreated() {
Darin Petkov31332412012-01-28 01:50:02 +0100224 // If IMSI is available, base the service's storage identifier on it.
225 if (!imsi_.empty()) {
226 cellular()->service()->SetStorageIdentifier(
Darin Petkovdd3e8662012-02-03 13:16:20 +0100227 string(flimflam::kTypeCellular) + "_" +
228 cellular()->address() + "_" + imsi_);
Darin Petkov31332412012-01-28 01:50:02 +0100229 }
Darin Petkovb9c99332012-01-12 13:13:00 +0100230 cellular()->service()->SetActivationState(
Darin Petkov5f316f62011-11-18 12:10:26 +0100231 flimflam::kActivationStateActivated);
232 UpdateServingOperator();
233}
234
Darin Petkovae0c64e2011-11-15 15:50:27 +0100235void CellularCapabilityGSM::UpdateStatus(const DBusPropertiesMap &properties) {
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400236 if (ContainsKey(properties, kModemPropertyIMSI)) {
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100237 SetHomeProvider();
Darin Petkovae0c64e2011-11-15 15:50:27 +0100238 }
239}
240
Eric Shienbrood30bc0ec2012-03-21 18:19:46 -0400241// Create the list of APNs to try, in the following order:
242// - last APN that resulted in a successful connection attempt on the
243// current network (if any)
244// - the APN, if any, that was set by the user
245// - the list of APNs found in the mobile broadband provider DB for the
246// home provider associated with the current SIM
247// - as a last resort, attempt to connect with no APN
248void CellularCapabilityGSM::SetupApnTryList() {
249 apn_try_list_.clear();
250
251 DCHECK(cellular()->service().get());
252 const Stringmap *apn_info = cellular()->service()->GetLastGoodApn();
253 if (apn_info)
254 apn_try_list_.push_back(*apn_info);
255
256 apn_info = cellular()->service()->GetUserSpecifiedApn();
257 if (apn_info)
258 apn_try_list_.push_back(*apn_info);
259
260 apn_try_list_.insert(apn_try_list_.end(), apn_list_.begin(), apn_list_.end());
261}
262
Darin Petkovae0c64e2011-11-15 15:50:27 +0100263void CellularCapabilityGSM::SetupConnectProperties(
264 DBusPropertiesMap *properties) {
Eric Shienbrood30bc0ec2012-03-21 18:19:46 -0400265 SetupApnTryList();
266 FillConnectPropertyMap(properties);
267}
268
269void CellularCapabilityGSM::FillConnectPropertyMap(
270 DBusPropertiesMap *properties) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500271 (*properties)[kConnectPropertyPhoneNumber].writer().append_string(
Darin Petkovae0c64e2011-11-15 15:50:27 +0100272 kPhoneNumber);
Eric Shienbrood30bc0ec2012-03-21 18:19:46 -0400273
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400274 if (!AllowRoaming())
Eric Shienbrood30bc0ec2012-03-21 18:19:46 -0400275 (*properties)[kConnectPropertyHomeOnly].writer().append_bool(true);
276
277 if (!apn_try_list_.empty()) {
278 // Leave the APN at the front of the list, so that it can be recorded
279 // if the connect attempt succeeds.
280 Stringmap apn_info = apn_try_list_.front();
Ben Chanfad4a0b2012-04-18 15:49:59 -0700281 SLOG(Cellular, 2) << __func__ << ": Using APN "
282 << apn_info[flimflam::kApnProperty];
Eric Shienbrood30bc0ec2012-03-21 18:19:46 -0400283 (*properties)[kConnectPropertyApn].writer().append_string(
284 apn_info[flimflam::kApnProperty].c_str());
285 if (ContainsKey(apn_info, flimflam::kApnUsernameProperty))
286 (*properties)[kConnectPropertyApnUsername].writer().append_string(
287 apn_info[flimflam::kApnUsernameProperty].c_str());
288 if (ContainsKey(apn_info, flimflam::kApnPasswordProperty))
289 (*properties)[kConnectPropertyApnPassword].writer().append_string(
290 apn_info[flimflam::kApnPasswordProperty].c_str());
291 }
292}
293
Nathan Williamsb54974f2012-04-19 11:16:30 -0400294void CellularCapabilityGSM::Connect(const DBusPropertiesMap &properties,
295 Error *error,
296 const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700297 SLOG(Cellular, 2) << __func__;
Nathan Williamsb54974f2012-04-19 11:16:30 -0400298 ResultCallback cb = Bind(&CellularCapabilityGSM::OnConnectReply,
299 weak_ptr_factory_.GetWeakPtr(),
300 callback);
301 simple_proxy_->Connect(properties, error, cb, kTimeoutConnect);
302}
303
Eric Shienbrood30bc0ec2012-03-21 18:19:46 -0400304void CellularCapabilityGSM::OnConnectReply(const ResultCallback &callback,
305 const Error &error) {
Thieu Leb5954a22012-05-18 10:37:34 -0700306 CellularServiceRefPtr service = cellular()->service();
307 if (!service) {
308 // The service could have been deleted before our Connect() request
309 // completes if the modem was enabled and then quickly disabled.
310 apn_try_list_.clear();
311 } else if (error.IsFailure()) {
312 service->ClearLastGoodApn();
Eric Shienbrood30bc0ec2012-03-21 18:19:46 -0400313 // The APN that was just tried (and failed) is still at the
314 // front of the list, about to be removed. If the list is empty
315 // after that, try one last time without an APN. This may succeed
316 // with some modems in some cases.
317 if (error.type() == Error::kInvalidApn && !apn_try_list_.empty()) {
318 apn_try_list_.pop_front();
Ben Chanfad4a0b2012-04-18 15:49:59 -0700319 SLOG(Cellular, 2) << "Connect failed with invalid APN, "
320 << apn_try_list_.size() << " remaining APNs to try";
Eric Shienbrood30bc0ec2012-03-21 18:19:46 -0400321 DBusPropertiesMap props;
322 FillConnectPropertyMap(&props);
323 Error error;
324 Connect(props, &error, callback);
325 return;
326 }
327 } else if (!apn_try_list_.empty()) {
Thieu Leb5954a22012-05-18 10:37:34 -0700328 service->SetLastGoodApn(apn_try_list_.front());
Eric Shienbrood30bc0ec2012-03-21 18:19:46 -0400329 apn_try_list_.clear();
330 }
Nathan Williamsb54974f2012-04-19 11:16:30 -0400331 if (!callback.is_null())
332 callback.Run(error);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400333}
334
335bool CellularCapabilityGSM::AllowRoaming() {
Darin Petkovf508c822012-09-21 13:43:17 +0200336 return provider_requires_roaming_ || allow_roaming_property();
Darin Petkovae0c64e2011-11-15 15:50:27 +0100337}
338
Eric Shienbrood9a245532012-03-07 14:20:39 -0500339// always called from an async context
340void CellularCapabilityGSM::GetIMEI(const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700341 SLOG(Cellular, 2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500342 CHECK(!callback.is_null());
343 Error error;
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500344 if (imei_.empty()) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500345 GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetIMEIReply,
346 weak_ptr_factory_.GetWeakPtr(), callback);
347 card_proxy_->GetIMEI(&error, cb, kTimeoutDefault);
348 if (error.IsFailure())
349 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500350 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700351 SLOG(Cellular, 2) << "Already have IMEI " << imei_;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500352 callback.Run(error);
Darin Petkovcb547732011-11-09 13:55:26 +0100353 }
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500354}
355
Eric Shienbrood9a245532012-03-07 14:20:39 -0500356// always called from an async context
357void CellularCapabilityGSM::GetIMSI(const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700358 SLOG(Cellular, 2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500359 CHECK(!callback.is_null());
360 Error error;
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500361 if (imsi_.empty()) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500362 GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetIMSIReply,
363 weak_ptr_factory_.GetWeakPtr(),
364 callback);
365 card_proxy_->GetIMSI(&error, cb, kTimeoutDefault);
366 if (error.IsFailure())
367 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500368 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700369 SLOG(Cellular, 2) << "Already have IMSI " << imsi_;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500370 callback.Run(error);
Darin Petkovcb547732011-11-09 13:55:26 +0100371 }
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500372}
373
Eric Shienbrood9a245532012-03-07 14:20:39 -0500374// always called from an async context
375void CellularCapabilityGSM::GetSPN(const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700376 SLOG(Cellular, 2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500377 CHECK(!callback.is_null());
378 Error error;
Darin Petkovae0c64e2011-11-15 15:50:27 +0100379 if (spn_.empty()) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500380 GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetSPNReply,
381 weak_ptr_factory_.GetWeakPtr(),
382 callback);
383 card_proxy_->GetSPN(&error, cb, kTimeoutDefault);
384 if (error.IsFailure())
385 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500386 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700387 SLOG(Cellular, 2) << "Already have SPN " << spn_;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500388 callback.Run(error);
Darin Petkovcb547732011-11-09 13:55:26 +0100389 }
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500390}
391
Eric Shienbrood9a245532012-03-07 14:20:39 -0500392// always called from an async context
393void CellularCapabilityGSM::GetMSISDN(const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700394 SLOG(Cellular, 2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500395 CHECK(!callback.is_null());
396 Error error;
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500397 if (mdn_.empty()) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500398 GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetMSISDNReply,
399 weak_ptr_factory_.GetWeakPtr(),
400 callback);
401 card_proxy_->GetMSISDN(&error, cb, kTimeoutDefault);
402 if (error.IsFailure())
403 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500404 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700405 SLOG(Cellular, 2) << "Already have MSISDN " << mdn_;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500406 callback.Run(error);
Darin Petkovcb547732011-11-09 13:55:26 +0100407 }
408}
409
Darin Petkov3e509242011-11-10 14:46:44 +0100410void CellularCapabilityGSM::GetSignalQuality() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700411 SLOG(Cellular, 2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500412 SignalQualityCallback callback =
413 Bind(&CellularCapabilityGSM::OnGetSignalQualityReply,
414 weak_ptr_factory_.GetWeakPtr());
415 network_proxy_->GetSignalQuality(NULL, callback, kTimeoutDefault);
Darin Petkov3e509242011-11-10 14:46:44 +0100416}
417
Eric Shienbrood9a245532012-03-07 14:20:39 -0500418void CellularCapabilityGSM::GetRegistrationState() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700419 SLOG(Cellular, 2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500420 RegistrationInfoCallback callback =
421 Bind(&CellularCapabilityGSM::OnGetRegistrationInfoReply,
422 weak_ptr_factory_.GetWeakPtr());
423 network_proxy_->GetRegistrationInfo(NULL, callback, kTimeoutDefault);
Darin Petkov184c54e2011-11-15 12:44:39 +0100424}
425
Eric Shienbrood9a245532012-03-07 14:20:39 -0500426void CellularCapabilityGSM::GetProperties(const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700427 SLOG(Cellular, 2) << __func__;
Darin Petkov63138a92012-02-06 14:09:15 +0100428
Darin Petkov184c54e2011-11-15 12:44:39 +0100429 // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
430 uint32 tech = network_proxy_->AccessTechnology();
Darin Petkovae0c64e2011-11-15 15:50:27 +0100431 SetAccessTechnology(tech);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700432 SLOG(Cellular, 2) << "GSM AccessTechnology: " << tech;
Darin Petkov63138a92012-02-06 14:09:15 +0100433
434 // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
435 uint32 locks = card_proxy_->EnabledFacilityLocks();
436 sim_lock_status_.enabled = locks & MM_MODEM_GSM_FACILITY_SIM;
Ben Chanfad4a0b2012-04-18 15:49:59 -0700437 SLOG(Cellular, 2) << "GSM EnabledFacilityLocks: " << locks;
Darin Petkov63138a92012-02-06 14:09:15 +0100438
Eric Shienbrood9a245532012-03-07 14:20:39 -0500439 callback.Run(Error());
Darin Petkov184c54e2011-11-15 12:44:39 +0100440}
441
Darin Petkovac635a82012-01-10 16:51:58 +0100442string CellularCapabilityGSM::CreateFriendlyServiceName() {
Darin Petkova4ca3c32012-08-17 16:05:24 +0200443 SLOG(Cellular, 2) << __func__ << ": " << GetRoamingStateString();
444 string name = serving_operator_.GetName();
445 string home_provider_name = cellular()->home_provider().GetName();
446 if (!name.empty()) {
447 // If roaming, try to show "<home-provider> | <serving-operator>", per 3GPP
448 // rules (TS 31.102 and annex A of 122.101).
449 if (registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING &&
450 !home_provider_name.empty()) {
451 return home_provider_name + " | " + name;
452 }
453 return name;
Darin Petkovac635a82012-01-10 16:51:58 +0100454 }
Darin Petkova4ca3c32012-08-17 16:05:24 +0200455 if (registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME &&
456 !home_provider_name.empty()) {
457 return home_provider_name;
Darin Petkovac635a82012-01-10 16:51:58 +0100458 }
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500459 if (!carrier_.empty()) {
460 return carrier_;
Darin Petkovac635a82012-01-10 16:51:58 +0100461 }
Darin Petkova4ca3c32012-08-17 16:05:24 +0200462 string serving_operator_code = serving_operator_.GetCode();
463 if (!serving_operator_code.empty()) {
464 return "cellular_" + serving_operator_code;
Darin Petkovac635a82012-01-10 16:51:58 +0100465 }
466 return base::StringPrintf("GSMNetwork%u", friendly_service_name_id_++);
467}
468
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100469void CellularCapabilityGSM::SetHomeProvider() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700470 SLOG(Cellular, 2) << __func__ << "(IMSI: " << imsi_
471 << " SPN: " << spn_ << ")";
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100472 // TODO(petkov): The test for NULL provider_db should be done by
473 // mobile_provider_lookup_best_match.
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500474 if (imsi_.empty() || !cellular()->provider_db()) {
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100475 return;
476 }
477 mobile_provider *provider =
478 mobile_provider_lookup_best_match(
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500479 cellular()->provider_db(), spn_.c_str(), imsi_.c_str());
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100480 if (!provider) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700481 SLOG(Cellular, 2) << "GSM provider not found.";
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100482 return;
483 }
484 home_provider_ = provider;
Darin Petkovf508c822012-09-21 13:43:17 +0200485 provider_requires_roaming_ = home_provider_->requires_roaming;
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100486 Cellular::Operator oper;
Darin Petkovb4fccd22012-08-10 11:59:26 +0200487 if (provider->networks && provider->networks[0]) {
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100488 oper.SetCode(provider->networks[0]);
489 }
490 if (provider->country) {
491 oper.SetCountry(provider->country);
492 }
493 if (spn_.empty()) {
494 const char *name = mobile_provider_get_name(provider);
495 if (name) {
496 oper.SetName(name);
497 }
498 } else {
499 oper.SetName(spn_);
500 }
501 cellular()->set_home_provider(oper);
Darin Petkova4ca3c32012-08-17 16:05:24 +0200502 SLOG(Cellular, 2) << "Home provider: " << oper.GetCode() << ", "
Darin Petkovf508c822012-09-21 13:43:17 +0200503 << oper.GetName() << ", " << oper.GetCountry()
504 << (provider_requires_roaming_ ? ", roaming required" : "");
Darin Petkov3cfbf212011-11-21 16:02:09 +0100505 InitAPNList();
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100506}
507
Darin Petkovae0c64e2011-11-15 15:50:27 +0100508void CellularCapabilityGSM::UpdateOperatorInfo() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700509 SLOG(Cellular, 2) << __func__;
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100510 const string &network_id = serving_operator_.GetCode();
511 if (!network_id.empty()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700512 SLOG(Cellular, 2) << "Looking up network id: " << network_id;
Darin Petkovae0c64e2011-11-15 15:50:27 +0100513 mobile_provider *provider =
514 mobile_provider_lookup_by_network(cellular()->provider_db(),
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100515 network_id.c_str());
Darin Petkovae0c64e2011-11-15 15:50:27 +0100516 if (provider) {
Darin Petkova4ca3c32012-08-17 16:05:24 +0200517 if (serving_operator_.GetName().empty()) {
518 const char *provider_name = mobile_provider_get_name(provider);
519 if (provider_name && *provider_name) {
520 serving_operator_.SetName(provider_name);
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100521 }
Darin Petkovae0c64e2011-11-15 15:50:27 +0100522 }
Darin Petkova4ca3c32012-08-17 16:05:24 +0200523 if (provider->country && *provider->country) {
524 serving_operator_.SetCountry(provider->country);
525 }
526 SLOG(Cellular, 2) << "Operator name: " << serving_operator_.GetName()
527 << ", country: " << serving_operator_.GetCountry();
Darin Petkovae0c64e2011-11-15 15:50:27 +0100528 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700529 SLOG(Cellular, 2) << "GSM provider not found.";
Darin Petkovae0c64e2011-11-15 15:50:27 +0100530 }
531 }
532 UpdateServingOperator();
533}
534
535void CellularCapabilityGSM::UpdateServingOperator() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700536 SLOG(Cellular, 2) << __func__;
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100537 if (cellular()->service().get()) {
Darin Petkov9cb02682012-01-28 00:17:38 +0100538 cellular()->service()->SetServingOperator(serving_operator_);
Darin Petkovae0c64e2011-11-15 15:50:27 +0100539 }
Darin Petkovae0c64e2011-11-15 15:50:27 +0100540}
541
Darin Petkov3cfbf212011-11-21 16:02:09 +0100542void CellularCapabilityGSM::InitAPNList() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700543 SLOG(Cellular, 2) << __func__;
Darin Petkov3cfbf212011-11-21 16:02:09 +0100544 if (!home_provider_) {
545 return;
546 }
547 apn_list_.clear();
548 for (int i = 0; i < home_provider_->num_apns; ++i) {
549 Stringmap props;
550 mobile_apn *apn = home_provider_->apns[i];
551 if (apn->value) {
552 props[flimflam::kApnProperty] = apn->value;
553 }
554 if (apn->username) {
555 props[flimflam::kApnUsernameProperty] = apn->username;
556 }
557 if (apn->password) {
558 props[flimflam::kApnPasswordProperty] = apn->password;
559 }
560 // Find the first localized and non-localized name, if any.
561 const localized_name *lname = NULL;
562 const localized_name *name = NULL;
563 for (int j = 0; j < apn->num_names; ++j) {
564 if (apn->names[j]->lang) {
565 if (!lname) {
566 lname = apn->names[j];
567 }
568 } else if (!name) {
569 name = apn->names[j];
570 }
571 }
572 if (name) {
573 props[flimflam::kApnNameProperty] = name->name;
574 }
575 if (lname) {
576 props[flimflam::kApnLocalizedNameProperty] = lname->name;
577 props[flimflam::kApnLanguageProperty] = lname->lang;
578 }
579 apn_list_.push_back(props);
580 }
Darin Petkovdb6083f2012-08-16 12:50:23 +0200581 if (cellular()->adaptor()) {
582 cellular()->adaptor()->EmitStringmapsChanged(
583 flimflam::kCellularApnListProperty, apn_list_);
584 } else {
585 LOG(ERROR) << "Null RPC service adaptor.";
586 }
Darin Petkov3cfbf212011-11-21 16:02:09 +0100587}
588
Eric Shienbrood9a245532012-03-07 14:20:39 -0500589// always called from an async context
590void CellularCapabilityGSM::Register(const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700591 SLOG(Cellular, 2) << __func__ << " \"" << selected_network_ << "\"";
Eric Shienbrood9a245532012-03-07 14:20:39 -0500592 CHECK(!callback.is_null());
593 Error error;
594 ResultCallback cb = Bind(&CellularCapabilityGSM::OnRegisterReply,
595 weak_ptr_factory_.GetWeakPtr(), callback);
596 network_proxy_->Register(selected_network_, &error, cb, kTimeoutRegister);
597 if (error.IsFailure())
598 callback.Run(error);
Darin Petkov184c54e2011-11-15 12:44:39 +0100599}
600
601void CellularCapabilityGSM::RegisterOnNetwork(
Eric Shienbrood9a245532012-03-07 14:20:39 -0500602 const string &network_id,
603 Error *error,
604 const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700605 SLOG(Cellular, 2) << __func__ << "(" << network_id << ")";
Eric Shienbrood9a245532012-03-07 14:20:39 -0500606 CHECK(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500607 desired_network_ = network_id;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500608 ResultCallback cb = Bind(&CellularCapabilityGSM::OnRegisterReply,
609 weak_ptr_factory_.GetWeakPtr(), callback);
610 network_proxy_->Register(network_id, error, cb, kTimeoutRegister);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500611}
612
Eric Shienbrood9a245532012-03-07 14:20:39 -0500613void CellularCapabilityGSM::OnRegisterReply(const ResultCallback &callback,
614 const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700615 SLOG(Cellular, 2) << __func__ << "(" << error << ")";
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500616
617 if (error.IsSuccess()) {
618 selected_network_ = desired_network_;
619 desired_network_.clear();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500620 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500621 return;
622 }
623 // If registration on the desired network failed,
624 // try to register on the home network.
625 if (!desired_network_.empty()) {
626 desired_network_.clear();
627 selected_network_.clear();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500628 LOG(INFO) << "Couldn't register on selected network, trying home network";
629 Register(callback);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500630 return;
631 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500632 callback.Run(error);
Darin Petkov184c54e2011-11-15 12:44:39 +0100633}
634
Darin Petkovb72cf402011-11-22 14:51:39 +0100635bool CellularCapabilityGSM::IsRegistered() {
636 return (registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
637 registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING);
638}
639
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400640void CellularCapabilityGSM::SetUnregistered(bool searching) {
641 // If we're already in some non-registered state, don't override that
642 if (registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
643 registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) {
644 registration_state_ =
645 (searching ? MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING :
646 MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE);
647 }
648}
649
Darin Petkovb05315f2011-11-07 10:14:25 +0100650void CellularCapabilityGSM::RequirePIN(
Eric Shienbrood9a245532012-03-07 14:20:39 -0500651 const std::string &pin, bool require,
652 Error *error, const ResultCallback &callback) {
653 CHECK(error);
654 card_proxy_->EnablePIN(pin, require, error, callback, kTimeoutDefault);
Darin Petkovb05315f2011-11-07 10:14:25 +0100655}
656
Darin Petkove5bc2cb2011-12-07 14:47:32 +0100657void CellularCapabilityGSM::EnterPIN(const string &pin,
Eric Shienbrood9a245532012-03-07 14:20:39 -0500658 Error *error,
659 const ResultCallback &callback) {
660 CHECK(error);
661 card_proxy_->SendPIN(pin, error, callback, kTimeoutDefault);
Darin Petkovb05315f2011-11-07 10:14:25 +0100662}
663
Darin Petkovc64fe5e2012-01-11 12:46:13 +0100664void CellularCapabilityGSM::UnblockPIN(const string &unblock_code,
665 const string &pin,
Eric Shienbrood9a245532012-03-07 14:20:39 -0500666 Error *error,
667 const ResultCallback &callback) {
668 CHECK(error);
669 card_proxy_->SendPUK(unblock_code, pin, error, callback, kTimeoutDefault);
Darin Petkovb05315f2011-11-07 10:14:25 +0100670}
671
672void CellularCapabilityGSM::ChangePIN(
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500673 const string &old_pin, const string &new_pin,
Eric Shienbrood9a245532012-03-07 14:20:39 -0500674 Error *error, const ResultCallback &callback) {
675 CHECK(error);
676 card_proxy_->ChangePIN(old_pin, new_pin, error, callback, kTimeoutDefault);
Darin Petkovb05315f2011-11-07 10:14:25 +0100677}
678
Eric Shienbrood9a245532012-03-07 14:20:39 -0500679void CellularCapabilityGSM::Scan(Error *error, const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700680 SLOG(Cellular, 2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500681 CHECK(error);
Jason Glasgowcd0349c2012-05-03 23:32:15 -0400682 if (scanning_) {
683 Error::PopulateAndLog(error, Error::kInProgress, "Already scanning");
684 return;
685 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500686 ScanResultsCallback cb = Bind(&CellularCapabilityGSM::OnScanReply,
687 weak_ptr_factory_.GetWeakPtr(), callback);
688 network_proxy_->Scan(error, cb, kTimeoutScan);
Jason Glasgowcd0349c2012-05-03 23:32:15 -0400689 if (!error->IsFailure()) {
690 scanning_ = true;
691 cellular()->adaptor()->EmitBoolChanged(flimflam::kScanningProperty,
692 scanning_);
693 }
Darin Petkov1272a432011-11-10 15:53:37 +0100694}
695
Eric Shienbrood9a245532012-03-07 14:20:39 -0500696void CellularCapabilityGSM::OnScanReply(const ResultCallback &callback,
697 const GSMScanResults &results,
698 const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700699 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400700
701 // Error handling is weak. The current expectation is that on any
702 // error, found_networks_ should be cleared and a property change
703 // notification sent out.
704 //
705 // TODO(jglasgow): fix error handling
Jason Glasgowcd0349c2012-05-03 23:32:15 -0400706 scanning_ = false;
707 cellular()->adaptor()->EmitBoolChanged(flimflam::kScanningProperty,
708 scanning_);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500709 found_networks_.clear();
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400710 if (!error.IsFailure()) {
711 for (GSMScanResults::const_iterator it = results.begin();
712 it != results.end(); ++it) {
713 found_networks_.push_back(ParseScanResult(*it));
714 }
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500715 }
Darin Petkovb7828b02012-02-03 12:34:30 +0100716 cellular()->adaptor()->EmitStringmapsChanged(flimflam::kFoundNetworksProperty,
717 found_networks_);
Gary Moraina49062a2012-04-27 13:48:41 -0700718 if (!callback.is_null())
719 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500720}
721
722Stringmap CellularCapabilityGSM::ParseScanResult(const GSMScanResult &result) {
Darin Petkov1272a432011-11-10 15:53:37 +0100723 Stringmap parsed;
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500724 for (GSMScanResult::const_iterator it = result.begin();
725 it != result.end(); ++it) {
Darin Petkov1272a432011-11-10 15:53:37 +0100726 // TODO(petkov): Define these in system_api/service_constants.h. The
727 // numerical values are taken from 3GPP TS 27.007 Section 7.3.
728 static const char * const kStatusString[] = {
729 "unknown",
730 "available",
731 "current",
732 "forbidden",
733 };
734 static const char * const kTechnologyString[] = {
735 flimflam::kNetworkTechnologyGsm,
736 "GSM Compact",
737 flimflam::kNetworkTechnologyUmts,
738 flimflam::kNetworkTechnologyEdge,
739 "HSDPA",
740 "HSUPA",
741 flimflam::kNetworkTechnologyHspa,
742 };
Ben Chanfad4a0b2012-04-18 15:49:59 -0700743 SLOG(Cellular, 2) << "Network property: " << it->first << " = "
744 << it->second;
Darin Petkov1272a432011-11-10 15:53:37 +0100745 if (it->first == kNetworkPropertyStatus) {
746 int status = 0;
747 if (base::StringToInt(it->second, &status) &&
748 status >= 0 &&
749 status < static_cast<int>(arraysize(kStatusString))) {
750 parsed[flimflam::kStatusProperty] = kStatusString[status];
751 } else {
752 LOG(ERROR) << "Unexpected status value: " << it->second;
753 }
754 } else if (it->first == kNetworkPropertyID) {
755 parsed[flimflam::kNetworkIdProperty] = it->second;
756 } else if (it->first == kNetworkPropertyLongName) {
757 parsed[flimflam::kLongNameProperty] = it->second;
758 } else if (it->first == kNetworkPropertyShortName) {
759 parsed[flimflam::kShortNameProperty] = it->second;
760 } else if (it->first == kNetworkPropertyAccessTechnology) {
761 int tech = 0;
762 if (base::StringToInt(it->second, &tech) &&
763 tech >= 0 &&
764 tech < static_cast<int>(arraysize(kTechnologyString))) {
765 parsed[flimflam::kTechnologyProperty] = kTechnologyString[tech];
766 } else {
767 LOG(ERROR) << "Unexpected technology value: " << it->second;
768 }
769 } else {
770 LOG(WARNING) << "Unknown network property ignored: " << it->first;
771 }
772 }
773 // If the long name is not available but the network ID is, look up the long
774 // name in the mobile provider database.
775 if ((!ContainsKey(parsed, flimflam::kLongNameProperty) ||
776 parsed[flimflam::kLongNameProperty].empty()) &&
777 ContainsKey(parsed, flimflam::kNetworkIdProperty)) {
778 mobile_provider *provider =
779 mobile_provider_lookup_by_network(
780 cellular()->provider_db(),
781 parsed[flimflam::kNetworkIdProperty].c_str());
782 if (provider) {
783 const char *long_name = mobile_provider_get_name(provider);
784 if (long_name && *long_name) {
785 parsed[flimflam::kLongNameProperty] = long_name;
786 }
787 }
788 }
789 return parsed;
790}
791
Darin Petkovae0c64e2011-11-15 15:50:27 +0100792void CellularCapabilityGSM::SetAccessTechnology(uint32 access_technology) {
793 access_technology_ = access_technology;
794 if (cellular()->service().get()) {
Darin Petkovb72cf402011-11-22 14:51:39 +0100795 cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
Darin Petkovae0c64e2011-11-15 15:50:27 +0100796 }
797}
798
Darin Petkov20c13ec2011-11-09 15:07:15 +0100799string CellularCapabilityGSM::GetNetworkTechnologyString() const {
Darin Petkovb72cf402011-11-22 14:51:39 +0100800 switch (access_technology_) {
801 case MM_MODEM_GSM_ACCESS_TECH_GSM:
802 case MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT:
803 return flimflam::kNetworkTechnologyGsm;
804 case MM_MODEM_GSM_ACCESS_TECH_GPRS:
805 return flimflam::kNetworkTechnologyGprs;
806 case MM_MODEM_GSM_ACCESS_TECH_EDGE:
807 return flimflam::kNetworkTechnologyEdge;
808 case MM_MODEM_GSM_ACCESS_TECH_UMTS:
809 return flimflam::kNetworkTechnologyUmts;
810 case MM_MODEM_GSM_ACCESS_TECH_HSDPA:
811 case MM_MODEM_GSM_ACCESS_TECH_HSUPA:
812 case MM_MODEM_GSM_ACCESS_TECH_HSPA:
813 return flimflam::kNetworkTechnologyHspa;
814 case MM_MODEM_GSM_ACCESS_TECH_HSPA_PLUS:
815 return flimflam::kNetworkTechnologyHspaPlus;
816 default:
817 break;
Darin Petkov20c13ec2011-11-09 15:07:15 +0100818 }
819 return "";
820}
821
822string CellularCapabilityGSM::GetRoamingStateString() const {
Darin Petkov184c54e2011-11-15 12:44:39 +0100823 switch (registration_state_) {
Darin Petkov20c13ec2011-11-09 15:07:15 +0100824 case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME:
825 return flimflam::kRoamingStateHome;
826 case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING:
827 return flimflam::kRoamingStateRoaming;
828 default:
829 break;
830 }
831 return flimflam::kRoamingStateUnknown;
832}
833
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400834void CellularCapabilityGSM::OnDBusPropertiesChanged(
835 const string &interface,
836 const DBusPropertiesMap &properties,
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400837 const vector<string> &invalidated_properties) {
838 CellularCapabilityClassic::OnDBusPropertiesChanged(interface,
839 properties,
840 invalidated_properties);
841 if (interface == MM_MODEM_GSM_NETWORK_INTERFACE) {
842 uint32 access_technology = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
843 if (DBusProperties::GetUint32(properties,
844 kPropertyAccessTechnology,
845 &access_technology)) {
846 SetAccessTechnology(access_technology);
847 }
Gary Morainbaeefdf2012-04-30 14:53:35 -0700848 } else {
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400849 bool emit = false;
Gary Morainbaeefdf2012-04-30 14:53:35 -0700850 if (interface == MM_MODEM_GSM_CARD_INTERFACE) {
851 uint32 locks = 0;
852 if (DBusProperties::GetUint32(
853 properties, kPropertyEnabledFacilityLocks, &locks)) {
854 sim_lock_status_.enabled = locks & MM_MODEM_GSM_FACILITY_SIM;
855 emit = true;
856 }
857 } else if (interface == MM_MODEM_INTERFACE) {
858 if (DBusProperties::GetString(properties, kPropertyUnlockRequired,
859 &sim_lock_status_.lock_type)) {
860 emit = true;
861 }
862 if (DBusProperties::GetUint32(properties, kPropertyUnlockRetries,
863 &sim_lock_status_.retries_left)) {
864 emit = true;
865 }
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400866 }
867 if (emit) {
868 cellular()->adaptor()->EmitKeyValueStoreChanged(
869 flimflam::kSIMLockStatusProperty, SimLockStatusToProperty(NULL));
870 }
Darin Petkov63138a92012-02-06 14:09:15 +0100871 }
Darin Petkovae0c64e2011-11-15 15:50:27 +0100872}
873
Eric Shienbrood9a245532012-03-07 14:20:39 -0500874void CellularCapabilityGSM::OnNetworkModeSignal(uint32 /*mode*/) {
Darin Petkov184c54e2011-11-15 12:44:39 +0100875 // TODO(petkov): Implement this.
876 NOTIMPLEMENTED();
877}
878
Eric Shienbrood9a245532012-03-07 14:20:39 -0500879void CellularCapabilityGSM::OnRegistrationInfoSignal(
880 uint32 status, const string &operator_code, const string &operator_name) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700881 SLOG(Cellular, 2) << __func__ << ": regstate=" << status
882 << ", opercode=" << operator_code
883 << ", opername=" << operator_name;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500884 registration_state_ = status;
885 serving_operator_.SetCode(operator_code);
886 serving_operator_.SetName(operator_name);
887 UpdateOperatorInfo();
888 cellular()->HandleNewRegistrationState();
Darin Petkov184c54e2011-11-15 12:44:39 +0100889}
890
Eric Shienbrood9a245532012-03-07 14:20:39 -0500891void CellularCapabilityGSM::OnSignalQualitySignal(uint32 quality) {
Darin Petkov184c54e2011-11-15 12:44:39 +0100892 cellular()->HandleNewSignalQuality(quality);
893}
894
Eric Shienbrood9a245532012-03-07 14:20:39 -0500895void CellularCapabilityGSM::OnGetRegistrationInfoReply(
896 uint32 status, const string &operator_code, const string &operator_name,
897 const Error &error) {
898 if (error.IsSuccess())
899 OnRegistrationInfoSignal(status, operator_code, operator_name);
900}
901
902void CellularCapabilityGSM::OnGetSignalQualityReply(uint32 quality,
903 const Error &error) {
904 if (error.IsSuccess())
905 OnSignalQualitySignal(quality);
906}
907
908void CellularCapabilityGSM::OnGetIMEIReply(const ResultCallback &callback,
909 const string &imei,
910 const Error &error) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500911 if (error.IsSuccess()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700912 SLOG(Cellular, 2) << "IMEI: " << imei;
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500913 imei_ = imei;
914 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700915 SLOG(Cellular, 2) << "GetIMEI failed - " << error;
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500916 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500917 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500918}
919
Eric Shienbrood9a245532012-03-07 14:20:39 -0500920void CellularCapabilityGSM::OnGetIMSIReply(const ResultCallback &callback,
921 const string &imsi,
922 const Error &error) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500923 if (error.IsSuccess()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700924 SLOG(Cellular, 2) << "IMSI: " << imsi;
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500925 imsi_ = imsi;
926 SetHomeProvider();
Gary Morain82a31a02012-08-02 18:03:32 -0700927 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500928 } else {
Gary Morain82a31a02012-08-02 18:03:32 -0700929 if (get_imsi_retries_++ < kGetIMSIRetryLimit) {
930 SLOG(Cellular, 2) << "GetIMSI failed - " << error << ". Retrying";
931 base::Callback<void(void)> retry_get_imsi_cb =
932 Bind(&CellularCapabilityGSM::GetIMSI,
933 weak_ptr_factory_.GetWeakPtr(), callback);
934 cellular()->dispatcher()->PostDelayedTask(
935 retry_get_imsi_cb,
936 get_imsi_retry_delay_milliseconds_);
937 } else {
938 LOG(INFO) << "GetIMSI failed - " << error;
939 callback.Run(error);
940 }
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500941 }
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500942}
943
Eric Shienbrood9a245532012-03-07 14:20:39 -0500944void CellularCapabilityGSM::OnGetSPNReply(const ResultCallback &callback,
945 const string &spn,
946 const Error &error) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500947 if (error.IsSuccess()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700948 SLOG(Cellular, 2) << "SPN: " << spn;
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500949 spn_ = spn;
950 SetHomeProvider();
951 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700952 SLOG(Cellular, 2) << "GetSPN failed - " << error;
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500953 }
Thieu Le923006b2012-04-05 16:32:58 -0700954 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500955}
956
Eric Shienbrood9a245532012-03-07 14:20:39 -0500957void CellularCapabilityGSM::OnGetMSISDNReply(const ResultCallback &callback,
958 const string &msisdn,
959 const Error &error) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500960 if (error.IsSuccess()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700961 SLOG(Cellular, 2) << "MSISDN: " << msisdn;
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500962 mdn_ = msisdn;
963 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700964 SLOG(Cellular, 2) << "GetMSISDN failed - " << error;
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500965 }
Thieu Le923006b2012-04-05 16:32:58 -0700966 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500967}
968
Darin Petkovdaf43862011-10-27 11:37:28 +0200969} // namespace shill