blob: bfda7623040ba5691abc49875a1b026994e5cee5 [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
Eric Shienbrood3e20a232012-02-16 11:35:56 -05007#include <base/bind.h>
Darin Petkovdaf43862011-10-27 11:37:28 +02008#include <base/logging.h>
Eric Shienbrood3e20a232012-02-16 11:35:56 -05009#include <base/stl_util.h>
Darin Petkov1272a432011-11-10 15:53:37 +010010#include <base/string_number_conversions.h>
Darin Petkovac635a82012-01-10 16:51:58 +010011#include <base/stringprintf.h>
Darin Petkov20c13ec2011-11-09 15:07:15 +010012#include <chromeos/dbus/service_constants.h>
13#include <mm/mm-modem.h>
Darin Petkov1272a432011-11-10 15:53:37 +010014#include <mobile_provider.h>
Darin Petkovdaf43862011-10-27 11:37:28 +020015
Darin Petkov3cfbf212011-11-21 16:02:09 +010016#include "shill/adaptor_interfaces.h"
Darin Petkovae0c64e2011-11-15 15:50:27 +010017#include "shill/cellular_service.h"
Eric Shienbrood5de44ab2011-12-05 10:46:27 -050018#include "shill/error.h"
Darin Petkov721ac932011-11-16 15:43:09 +010019#include "shill/property_accessor.h"
Darin Petkovdaf43862011-10-27 11:37:28 +020020#include "shill/proxy_factory.h"
21
Eric Shienbrood3e20a232012-02-16 11:35:56 -050022using base::Bind;
Darin Petkovb05315f2011-11-07 10:14:25 +010023using std::string;
24
Darin Petkovdaf43862011-10-27 11:37:28 +020025namespace shill {
26
Darin Petkovac635a82012-01-10 16:51:58 +010027// static
28unsigned int CellularCapabilityGSM::friendly_service_name_id_ = 0;
29
Darin Petkov1272a432011-11-10 15:53:37 +010030const char CellularCapabilityGSM::kNetworkPropertyAccessTechnology[] =
31 "access-tech";
32const char CellularCapabilityGSM::kNetworkPropertyID[] = "operator-num";
33const char CellularCapabilityGSM::kNetworkPropertyLongName[] = "operator-long";
34const char CellularCapabilityGSM::kNetworkPropertyShortName[] =
35 "operator-short";
36const char CellularCapabilityGSM::kNetworkPropertyStatus[] = "status";
Darin Petkovae0c64e2011-11-15 15:50:27 +010037const char CellularCapabilityGSM::kPhoneNumber[] = "*99#";
38const char CellularCapabilityGSM::kPropertyAccessTechnology[] =
39 "AccessTechnology";
Darin Petkov63138a92012-02-06 14:09:15 +010040const char CellularCapabilityGSM::kPropertyEnabledFacilityLocks[] =
41 "EnabledFacilityLocks";
Darin Petkov721ac932011-11-16 15:43:09 +010042const char CellularCapabilityGSM::kPropertyUnlockRequired[] = "UnlockRequired";
43const char CellularCapabilityGSM::kPropertyUnlockRetries[] = "UnlockRetries";
Darin Petkov1272a432011-11-10 15:53:37 +010044
Eric Shienbrood5de44ab2011-12-05 10:46:27 -050045CellularCapabilityGSM::CellularCapabilityGSM(Cellular *cellular,
46 ProxyFactory *proxy_factory)
47 : CellularCapability(cellular, proxy_factory),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050048 weak_ptr_factory_(this),
Darin Petkov184c54e2011-11-15 12:44:39 +010049 registration_state_(MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN),
Darin Petkovae0c64e2011-11-15 15:50:27 +010050 access_technology_(MM_MODEM_GSM_ACCESS_TECH_UNKNOWN),
Darin Petkov0a4dfeb2011-11-18 19:36:13 +010051 home_provider_(NULL),
Darin Petkov1272a432011-11-10 15:53:37 +010052 scanning_(false),
53 scan_interval_(0) {
Darin Petkov5f316f62011-11-18 12:10:26 +010054 VLOG(2) << "Cellular capability constructed: GSM";
Darin Petkov1272a432011-11-10 15:53:37 +010055 PropertyStore *store = cellular->mutable_store();
Darin Petkov184c54e2011-11-15 12:44:39 +010056 store->RegisterConstString(flimflam::kSelectedNetworkProperty,
57 &selected_network_);
Darin Petkov1272a432011-11-10 15:53:37 +010058 store->RegisterConstStringmaps(flimflam::kFoundNetworksProperty,
59 &found_networks_);
60 store->RegisterConstBool(flimflam::kScanningProperty, &scanning_);
61 store->RegisterUint16(flimflam::kScanIntervalProperty, &scan_interval_);
Darin Petkov63138a92012-02-06 14:09:15 +010062 HelpRegisterDerivedKeyValueStore(
63 flimflam::kSIMLockStatusProperty,
64 &CellularCapabilityGSM::SimLockStatusToProperty,
65 NULL);
Darin Petkov3cfbf212011-11-21 16:02:09 +010066 store->RegisterConstStringmaps(flimflam::kCellularApnListProperty,
67 &apn_list_);
Darin Petkov0d06f7d2012-02-03 13:08:19 +010068 scanning_supported_ = true;
Darin Petkov1272a432011-11-10 15:53:37 +010069}
Darin Petkovdaf43862011-10-27 11:37:28 +020070
Darin Petkov63138a92012-02-06 14:09:15 +010071KeyValueStore CellularCapabilityGSM::SimLockStatusToProperty(Error */*error*/) {
72 KeyValueStore status;
73 status.SetBool(flimflam::kSIMLockEnabledProperty, sim_lock_status_.enabled);
74 status.SetString(flimflam::kSIMLockTypeProperty, sim_lock_status_.lock_type);
75 status.SetUint(flimflam::kSIMLockRetriesLeftProperty,
76 sim_lock_status_.retries_left);
77 return status;
Darin Petkov721ac932011-11-16 15:43:09 +010078}
79
Darin Petkov63138a92012-02-06 14:09:15 +010080void CellularCapabilityGSM::HelpRegisterDerivedKeyValueStore(
Darin Petkov721ac932011-11-16 15:43:09 +010081 const string &name,
Darin Petkov63138a92012-02-06 14:09:15 +010082 KeyValueStore(CellularCapabilityGSM::*get)(Error *error),
83 void(CellularCapabilityGSM::*set)(
84 const KeyValueStore &value, Error *error)) {
85 cellular()->mutable_store()->RegisterDerivedKeyValueStore(
Darin Petkov721ac932011-11-16 15:43:09 +010086 name,
Darin Petkov63138a92012-02-06 14:09:15 +010087 KeyValueStoreAccessor(
88 new CustomAccessor<CellularCapabilityGSM, KeyValueStore>(
Darin Petkov721ac932011-11-16 15:43:09 +010089 this, get, set)));
90}
91
Eric Shienbrood9a245532012-03-07 14:20:39 -050092void CellularCapabilityGSM::InitProxies() {
93 CellularCapability::InitProxies();
Darin Petkovcb547732011-11-09 13:55:26 +010094 card_proxy_.reset(
Eric Shienbrood9a245532012-03-07 14:20:39 -050095 proxy_factory()->CreateModemGSMCardProxy(cellular()->dbus_path(),
Darin Petkovdaf43862011-10-27 11:37:28 +020096 cellular()->dbus_owner()));
Darin Petkov184c54e2011-11-15 12:44:39 +010097 network_proxy_.reset(
Eric Shienbrood9a245532012-03-07 14:20:39 -050098 proxy_factory()->CreateModemGSMNetworkProxy(cellular()->dbus_path(),
Darin Petkovdaf43862011-10-27 11:37:28 +020099 cellular()->dbus_owner()));
Eric Shienbrood9a245532012-03-07 14:20:39 -0500100 network_proxy_->set_signal_quality_callback(
101 Bind(&CellularCapabilityGSM::OnSignalQualitySignal,
102 weak_ptr_factory_.GetWeakPtr()));
103 network_proxy_->set_network_mode_callback(
104 Bind(&CellularCapabilityGSM::OnNetworkModeSignal,
105 weak_ptr_factory_.GetWeakPtr()));
106 network_proxy_->set_registration_info_callback(
107 Bind(&CellularCapabilityGSM::OnRegistrationInfoSignal,
108 weak_ptr_factory_.GetWeakPtr()));
Darin Petkovdaf43862011-10-27 11:37:28 +0200109}
110
Eric Shienbrood9a245532012-03-07 14:20:39 -0500111void CellularCapabilityGSM::StartModem(Error *error,
112 const ResultCallback &callback) {
113 InitProxies();
114
115 CellularTaskList *tasks = new CellularTaskList();
116 ResultCallback cb =
117 Bind(&CellularCapabilityGSM::StepCompletedCallback,
Thieu Le923006b2012-04-05 16:32:58 -0700118 weak_ptr_factory_.GetWeakPtr(), callback, false, tasks);
119 ResultCallback cb_ignore_error =
120 Bind(&CellularCapabilityGSM::StepCompletedCallback,
121 weak_ptr_factory_.GetWeakPtr(), callback, true, tasks);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500122 tasks->push_back(Bind(&CellularCapabilityGSM::EnableModem,
123 weak_ptr_factory_.GetWeakPtr(), cb));
124 tasks->push_back(Bind(&CellularCapabilityGSM::Register,
125 weak_ptr_factory_.GetWeakPtr(), cb));
126 tasks->push_back(Bind(&CellularCapabilityGSM::GetModemStatus,
127 weak_ptr_factory_.GetWeakPtr(), cb));
128 tasks->push_back(Bind(&CellularCapabilityGSM::GetIMEI,
129 weak_ptr_factory_.GetWeakPtr(), cb));
130 tasks->push_back(Bind(&CellularCapabilityGSM::GetIMSI,
131 weak_ptr_factory_.GetWeakPtr(), cb));
132 tasks->push_back(Bind(&CellularCapabilityGSM::GetSPN,
Thieu Le923006b2012-04-05 16:32:58 -0700133 weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
Eric Shienbrood9a245532012-03-07 14:20:39 -0500134 tasks->push_back(Bind(&CellularCapabilityGSM::GetMSISDN,
Thieu Le923006b2012-04-05 16:32:58 -0700135 weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
Eric Shienbrood9a245532012-03-07 14:20:39 -0500136 tasks->push_back(Bind(&CellularCapabilityGSM::GetProperties,
137 weak_ptr_factory_.GetWeakPtr(), cb));
138 tasks->push_back(Bind(&CellularCapabilityGSM::GetModemInfo,
139 weak_ptr_factory_.GetWeakPtr(), cb));
140 tasks->push_back(Bind(&CellularCapabilityGSM::FinishEnable,
141 weak_ptr_factory_.GetWeakPtr(), cb));
142
143 RunNextStep(tasks);
144}
145
Eric Shienbrood9a245532012-03-07 14:20:39 -0500146void CellularCapabilityGSM::ReleaseProxies() {
147 VLOG(2) << __func__;
148 CellularCapability::ReleaseProxies();
Darin Petkov721ac932011-11-16 15:43:09 +0100149 card_proxy_.reset();
150 network_proxy_.reset();
151}
152
Darin Petkov5f316f62011-11-18 12:10:26 +0100153void CellularCapabilityGSM::OnServiceCreated() {
Darin Petkov31332412012-01-28 01:50:02 +0100154 // If IMSI is available, base the service's storage identifier on it.
155 if (!imsi_.empty()) {
156 cellular()->service()->SetStorageIdentifier(
Darin Petkovdd3e8662012-02-03 13:16:20 +0100157 string(flimflam::kTypeCellular) + "_" +
158 cellular()->address() + "_" + imsi_);
Darin Petkov31332412012-01-28 01:50:02 +0100159 }
Darin Petkovb9c99332012-01-12 13:13:00 +0100160 cellular()->service()->SetActivationState(
Darin Petkov5f316f62011-11-18 12:10:26 +0100161 flimflam::kActivationStateActivated);
162 UpdateServingOperator();
163}
164
Darin Petkovae0c64e2011-11-15 15:50:27 +0100165void CellularCapabilityGSM::UpdateStatus(const DBusPropertiesMap &properties) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500166 if (ContainsKey(properties, kPropertyIMSI)) {
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100167 SetHomeProvider();
Darin Petkovae0c64e2011-11-15 15:50:27 +0100168 }
169}
170
Eric Shienbrood30bc0ec2012-03-21 18:19:46 -0400171// Create the list of APNs to try, in the following order:
172// - last APN that resulted in a successful connection attempt on the
173// current network (if any)
174// - the APN, if any, that was set by the user
175// - the list of APNs found in the mobile broadband provider DB for the
176// home provider associated with the current SIM
177// - as a last resort, attempt to connect with no APN
178void CellularCapabilityGSM::SetupApnTryList() {
179 apn_try_list_.clear();
180
181 DCHECK(cellular()->service().get());
182 const Stringmap *apn_info = cellular()->service()->GetLastGoodApn();
183 if (apn_info)
184 apn_try_list_.push_back(*apn_info);
185
186 apn_info = cellular()->service()->GetUserSpecifiedApn();
187 if (apn_info)
188 apn_try_list_.push_back(*apn_info);
189
190 apn_try_list_.insert(apn_try_list_.end(), apn_list_.begin(), apn_list_.end());
191}
192
Darin Petkovae0c64e2011-11-15 15:50:27 +0100193void CellularCapabilityGSM::SetupConnectProperties(
194 DBusPropertiesMap *properties) {
Eric Shienbrood30bc0ec2012-03-21 18:19:46 -0400195 SetupApnTryList();
196 FillConnectPropertyMap(properties);
197}
198
199void CellularCapabilityGSM::FillConnectPropertyMap(
200 DBusPropertiesMap *properties) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500201 (*properties)[kConnectPropertyPhoneNumber].writer().append_string(
Darin Petkovae0c64e2011-11-15 15:50:27 +0100202 kPhoneNumber);
Eric Shienbrood30bc0ec2012-03-21 18:19:46 -0400203
204 if (!allow_roaming_)
205 (*properties)[kConnectPropertyHomeOnly].writer().append_bool(true);
206
207 if (!apn_try_list_.empty()) {
208 // Leave the APN at the front of the list, so that it can be recorded
209 // if the connect attempt succeeds.
210 Stringmap apn_info = apn_try_list_.front();
211 VLOG(2) << __func__ << ": Using APN " << apn_info[flimflam::kApnProperty];
212 (*properties)[kConnectPropertyApn].writer().append_string(
213 apn_info[flimflam::kApnProperty].c_str());
214 if (ContainsKey(apn_info, flimflam::kApnUsernameProperty))
215 (*properties)[kConnectPropertyApnUsername].writer().append_string(
216 apn_info[flimflam::kApnUsernameProperty].c_str());
217 if (ContainsKey(apn_info, flimflam::kApnPasswordProperty))
218 (*properties)[kConnectPropertyApnPassword].writer().append_string(
219 apn_info[flimflam::kApnPasswordProperty].c_str());
220 }
221}
222
223void CellularCapabilityGSM::OnConnectReply(const ResultCallback &callback,
224 const Error &error) {
225 if (error.IsFailure()) {
226 cellular()->service()->ClearLastGoodApn();
227 // The APN that was just tried (and failed) is still at the
228 // front of the list, about to be removed. If the list is empty
229 // after that, try one last time without an APN. This may succeed
230 // with some modems in some cases.
231 if (error.type() == Error::kInvalidApn && !apn_try_list_.empty()) {
232 apn_try_list_.pop_front();
233 VLOG(2) << "Connect failed with invalid APN, " << apn_try_list_.size()
234 << " remaining APNs to try";
235 DBusPropertiesMap props;
236 FillConnectPropertyMap(&props);
237 Error error;
238 Connect(props, &error, callback);
239 return;
240 }
241 } else if (!apn_try_list_.empty()) {
242 cellular()->service()->SetLastGoodApn(apn_try_list_.front());
243 apn_try_list_.clear();
244 }
245 CellularCapability::OnConnectReply(callback, error);
Darin Petkovae0c64e2011-11-15 15:50:27 +0100246}
247
Eric Shienbrood9a245532012-03-07 14:20:39 -0500248// always called from an async context
249void CellularCapabilityGSM::GetIMEI(const ResultCallback &callback) {
Darin Petkovcb547732011-11-09 13:55:26 +0100250 VLOG(2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500251 CHECK(!callback.is_null());
252 Error error;
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500253 if (imei_.empty()) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500254 GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetIMEIReply,
255 weak_ptr_factory_.GetWeakPtr(), callback);
256 card_proxy_->GetIMEI(&error, cb, kTimeoutDefault);
257 if (error.IsFailure())
258 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500259 } else {
260 VLOG(2) << "Already have IMEI " << imei_;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500261 callback.Run(error);
Darin Petkovcb547732011-11-09 13:55:26 +0100262 }
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500263}
264
Eric Shienbrood9a245532012-03-07 14:20:39 -0500265// always called from an async context
266void CellularCapabilityGSM::GetIMSI(const ResultCallback &callback) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500267 VLOG(2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500268 CHECK(!callback.is_null());
269 Error error;
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500270 if (imsi_.empty()) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500271 GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetIMSIReply,
272 weak_ptr_factory_.GetWeakPtr(),
273 callback);
274 card_proxy_->GetIMSI(&error, cb, kTimeoutDefault);
275 if (error.IsFailure())
276 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500277 } else {
278 VLOG(2) << "Already have IMSI " << imsi_;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500279 callback.Run(error);
Darin Petkovcb547732011-11-09 13:55:26 +0100280 }
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500281}
282
Eric Shienbrood9a245532012-03-07 14:20:39 -0500283// always called from an async context
284void CellularCapabilityGSM::GetSPN(const ResultCallback &callback) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500285 VLOG(2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500286 CHECK(!callback.is_null());
287 Error error;
Darin Petkovae0c64e2011-11-15 15:50:27 +0100288 if (spn_.empty()) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500289 GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetSPNReply,
290 weak_ptr_factory_.GetWeakPtr(),
291 callback);
292 card_proxy_->GetSPN(&error, cb, kTimeoutDefault);
293 if (error.IsFailure())
294 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500295 } else {
296 VLOG(2) << "Already have SPN " << spn_;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500297 callback.Run(error);
Darin Petkovcb547732011-11-09 13:55:26 +0100298 }
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500299}
300
Eric Shienbrood9a245532012-03-07 14:20:39 -0500301// always called from an async context
302void CellularCapabilityGSM::GetMSISDN(const ResultCallback &callback) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500303 VLOG(2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500304 CHECK(!callback.is_null());
305 Error error;
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500306 if (mdn_.empty()) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500307 GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetMSISDNReply,
308 weak_ptr_factory_.GetWeakPtr(),
309 callback);
310 card_proxy_->GetMSISDN(&error, cb, kTimeoutDefault);
311 if (error.IsFailure())
312 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500313 } else {
314 VLOG(2) << "Already have MSISDN " << mdn_;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500315 callback.Run(error);
Darin Petkovcb547732011-11-09 13:55:26 +0100316 }
317}
318
Darin Petkov3e509242011-11-10 14:46:44 +0100319void CellularCapabilityGSM::GetSignalQuality() {
320 VLOG(2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500321 SignalQualityCallback callback =
322 Bind(&CellularCapabilityGSM::OnGetSignalQualityReply,
323 weak_ptr_factory_.GetWeakPtr());
324 network_proxy_->GetSignalQuality(NULL, callback, kTimeoutDefault);
Darin Petkov3e509242011-11-10 14:46:44 +0100325}
326
Eric Shienbrood9a245532012-03-07 14:20:39 -0500327void CellularCapabilityGSM::GetRegistrationState() {
Darin Petkov184c54e2011-11-15 12:44:39 +0100328 VLOG(2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500329 RegistrationInfoCallback callback =
330 Bind(&CellularCapabilityGSM::OnGetRegistrationInfoReply,
331 weak_ptr_factory_.GetWeakPtr());
332 network_proxy_->GetRegistrationInfo(NULL, callback, kTimeoutDefault);
Darin Petkov184c54e2011-11-15 12:44:39 +0100333}
334
Eric Shienbrood9a245532012-03-07 14:20:39 -0500335void CellularCapabilityGSM::GetProperties(const ResultCallback &callback) {
Darin Petkov184c54e2011-11-15 12:44:39 +0100336 VLOG(2) << __func__;
Darin Petkov63138a92012-02-06 14:09:15 +0100337
Darin Petkov184c54e2011-11-15 12:44:39 +0100338 // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
339 uint32 tech = network_proxy_->AccessTechnology();
Darin Petkovae0c64e2011-11-15 15:50:27 +0100340 SetAccessTechnology(tech);
Darin Petkov184c54e2011-11-15 12:44:39 +0100341 VLOG(2) << "GSM AccessTechnology: " << tech;
Darin Petkov63138a92012-02-06 14:09:15 +0100342
343 // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
344 uint32 locks = card_proxy_->EnabledFacilityLocks();
345 sim_lock_status_.enabled = locks & MM_MODEM_GSM_FACILITY_SIM;
346 VLOG(2) << "GSM EnabledFacilityLocks: " << locks;
347
Eric Shienbrood9a245532012-03-07 14:20:39 -0500348 callback.Run(Error());
Darin Petkov184c54e2011-11-15 12:44:39 +0100349}
350
Darin Petkovac635a82012-01-10 16:51:58 +0100351string CellularCapabilityGSM::CreateFriendlyServiceName() {
352 VLOG(2) << __func__;
353 if (registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME &&
354 !cellular()->home_provider().GetName().empty()) {
355 return cellular()->home_provider().GetName();
356 }
357 if (!serving_operator_.GetName().empty()) {
358 return serving_operator_.GetName();
359 }
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500360 if (!carrier_.empty()) {
361 return carrier_;
Darin Petkovac635a82012-01-10 16:51:58 +0100362 }
363 if (!serving_operator_.GetCode().empty()) {
364 return "cellular_" + serving_operator_.GetCode();
365 }
366 return base::StringPrintf("GSMNetwork%u", friendly_service_name_id_++);
367}
368
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100369void CellularCapabilityGSM::SetHomeProvider() {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500370 VLOG(2) << __func__ << "(IMSI: " << imsi_
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100371 << " SPN: " << spn_ << ")";
372 // TODO(petkov): The test for NULL provider_db should be done by
373 // mobile_provider_lookup_best_match.
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500374 if (imsi_.empty() || !cellular()->provider_db()) {
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100375 return;
376 }
377 mobile_provider *provider =
378 mobile_provider_lookup_best_match(
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500379 cellular()->provider_db(), spn_.c_str(), imsi_.c_str());
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100380 if (!provider) {
381 VLOG(2) << "GSM provider not found.";
382 return;
383 }
384 home_provider_ = provider;
385 Cellular::Operator oper;
386 if (provider->networks) {
387 oper.SetCode(provider->networks[0]);
388 }
389 if (provider->country) {
390 oper.SetCountry(provider->country);
391 }
392 if (spn_.empty()) {
393 const char *name = mobile_provider_get_name(provider);
394 if (name) {
395 oper.SetName(name);
396 }
397 } else {
398 oper.SetName(spn_);
399 }
400 cellular()->set_home_provider(oper);
Darin Petkov3cfbf212011-11-21 16:02:09 +0100401 InitAPNList();
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100402}
403
Darin Petkovae0c64e2011-11-15 15:50:27 +0100404void CellularCapabilityGSM::UpdateOperatorInfo() {
405 VLOG(2) << __func__;
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100406 const string &network_id = serving_operator_.GetCode();
407 if (!network_id.empty()) {
408 VLOG(2) << "Looking up network id: " << network_id;
Darin Petkovae0c64e2011-11-15 15:50:27 +0100409 mobile_provider *provider =
410 mobile_provider_lookup_by_network(cellular()->provider_db(),
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100411 network_id.c_str());
Darin Petkovae0c64e2011-11-15 15:50:27 +0100412 if (provider) {
413 const char *provider_name = mobile_provider_get_name(provider);
414 if (provider_name && *provider_name) {
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100415 serving_operator_.SetName(provider_name);
416 if (provider->country) {
417 serving_operator_.SetCountry(provider->country);
418 }
419 VLOG(2) << "Operator name: " << serving_operator_.GetName()
420 << ", country: " << serving_operator_.GetCountry();
Darin Petkovae0c64e2011-11-15 15:50:27 +0100421 }
422 } else {
423 VLOG(2) << "GSM provider not found.";
424 }
425 }
426 UpdateServingOperator();
427}
428
429void CellularCapabilityGSM::UpdateServingOperator() {
430 VLOG(2) << __func__;
Darin Petkov0a4dfeb2011-11-18 19:36:13 +0100431 if (cellular()->service().get()) {
Darin Petkov9cb02682012-01-28 00:17:38 +0100432 cellular()->service()->SetServingOperator(serving_operator_);
Darin Petkovae0c64e2011-11-15 15:50:27 +0100433 }
Darin Petkovae0c64e2011-11-15 15:50:27 +0100434}
435
Darin Petkov3cfbf212011-11-21 16:02:09 +0100436void CellularCapabilityGSM::InitAPNList() {
437 VLOG(2) << __func__;
438 if (!home_provider_) {
439 return;
440 }
441 apn_list_.clear();
442 for (int i = 0; i < home_provider_->num_apns; ++i) {
443 Stringmap props;
444 mobile_apn *apn = home_provider_->apns[i];
445 if (apn->value) {
446 props[flimflam::kApnProperty] = apn->value;
447 }
448 if (apn->username) {
449 props[flimflam::kApnUsernameProperty] = apn->username;
450 }
451 if (apn->password) {
452 props[flimflam::kApnPasswordProperty] = apn->password;
453 }
454 // Find the first localized and non-localized name, if any.
455 const localized_name *lname = NULL;
456 const localized_name *name = NULL;
457 for (int j = 0; j < apn->num_names; ++j) {
458 if (apn->names[j]->lang) {
459 if (!lname) {
460 lname = apn->names[j];
461 }
462 } else if (!name) {
463 name = apn->names[j];
464 }
465 }
466 if (name) {
467 props[flimflam::kApnNameProperty] = name->name;
468 }
469 if (lname) {
470 props[flimflam::kApnLocalizedNameProperty] = lname->name;
471 props[flimflam::kApnLanguageProperty] = lname->lang;
472 }
473 apn_list_.push_back(props);
474 }
475 cellular()->adaptor()->EmitStringmapsChanged(
476 flimflam::kCellularApnListProperty, apn_list_);
477}
478
Eric Shienbrood9a245532012-03-07 14:20:39 -0500479// always called from an async context
480void CellularCapabilityGSM::Register(const ResultCallback &callback) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500481 VLOG(2) << __func__ << " \"" << selected_network_ << "\"";
Eric Shienbrood9a245532012-03-07 14:20:39 -0500482 CHECK(!callback.is_null());
483 Error error;
484 ResultCallback cb = Bind(&CellularCapabilityGSM::OnRegisterReply,
485 weak_ptr_factory_.GetWeakPtr(), callback);
486 network_proxy_->Register(selected_network_, &error, cb, kTimeoutRegister);
487 if (error.IsFailure())
488 callback.Run(error);
Darin Petkov184c54e2011-11-15 12:44:39 +0100489}
490
491void CellularCapabilityGSM::RegisterOnNetwork(
Eric Shienbrood9a245532012-03-07 14:20:39 -0500492 const string &network_id,
493 Error *error,
494 const ResultCallback &callback) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500495 VLOG(2) << __func__ << "(" << network_id << ")";
Eric Shienbrood9a245532012-03-07 14:20:39 -0500496 CHECK(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500497 desired_network_ = network_id;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500498 ResultCallback cb = Bind(&CellularCapabilityGSM::OnRegisterReply,
499 weak_ptr_factory_.GetWeakPtr(), callback);
500 network_proxy_->Register(network_id, error, cb, kTimeoutRegister);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500501}
502
Eric Shienbrood9a245532012-03-07 14:20:39 -0500503void CellularCapabilityGSM::OnRegisterReply(const ResultCallback &callback,
504 const Error &error) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500505 VLOG(2) << __func__ << "(" << error << ")";
506
507 if (error.IsSuccess()) {
508 selected_network_ = desired_network_;
509 desired_network_.clear();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500510 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500511 return;
512 }
513 // If registration on the desired network failed,
514 // try to register on the home network.
515 if (!desired_network_.empty()) {
516 desired_network_.clear();
517 selected_network_.clear();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500518 LOG(INFO) << "Couldn't register on selected network, trying home network";
519 Register(callback);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500520 return;
521 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500522 callback.Run(error);
Darin Petkov184c54e2011-11-15 12:44:39 +0100523}
524
Darin Petkovb72cf402011-11-22 14:51:39 +0100525bool CellularCapabilityGSM::IsRegistered() {
526 return (registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
527 registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING);
528}
529
Darin Petkovb05315f2011-11-07 10:14:25 +0100530void CellularCapabilityGSM::RequirePIN(
Eric Shienbrood9a245532012-03-07 14:20:39 -0500531 const std::string &pin, bool require,
532 Error *error, const ResultCallback &callback) {
533 CHECK(error);
534 card_proxy_->EnablePIN(pin, require, error, callback, kTimeoutDefault);
Darin Petkovb05315f2011-11-07 10:14:25 +0100535}
536
Darin Petkove5bc2cb2011-12-07 14:47:32 +0100537void CellularCapabilityGSM::EnterPIN(const string &pin,
Eric Shienbrood9a245532012-03-07 14:20:39 -0500538 Error *error,
539 const ResultCallback &callback) {
540 CHECK(error);
541 card_proxy_->SendPIN(pin, error, callback, kTimeoutDefault);
Darin Petkovb05315f2011-11-07 10:14:25 +0100542}
543
Darin Petkovc64fe5e2012-01-11 12:46:13 +0100544void CellularCapabilityGSM::UnblockPIN(const string &unblock_code,
545 const string &pin,
Eric Shienbrood9a245532012-03-07 14:20:39 -0500546 Error *error,
547 const ResultCallback &callback) {
548 CHECK(error);
549 card_proxy_->SendPUK(unblock_code, pin, error, callback, kTimeoutDefault);
Darin Petkovb05315f2011-11-07 10:14:25 +0100550}
551
552void CellularCapabilityGSM::ChangePIN(
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500553 const string &old_pin, const string &new_pin,
Eric Shienbrood9a245532012-03-07 14:20:39 -0500554 Error *error, const ResultCallback &callback) {
555 CHECK(error);
556 card_proxy_->ChangePIN(old_pin, new_pin, error, callback, kTimeoutDefault);
Darin Petkovb05315f2011-11-07 10:14:25 +0100557}
558
Eric Shienbrood9a245532012-03-07 14:20:39 -0500559void CellularCapabilityGSM::Scan(Error *error, const ResultCallback &callback) {
Darin Petkov1272a432011-11-10 15:53:37 +0100560 VLOG(2) << __func__;
561 // TODO(petkov): Defer scan requests if a scan is in progress already.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500562 CHECK(error);
563 ScanResultsCallback cb = Bind(&CellularCapabilityGSM::OnScanReply,
564 weak_ptr_factory_.GetWeakPtr(), callback);
565 network_proxy_->Scan(error, cb, kTimeoutScan);
Darin Petkov1272a432011-11-10 15:53:37 +0100566}
567
Eric Shienbrood9a245532012-03-07 14:20:39 -0500568void CellularCapabilityGSM::OnScanReply(const ResultCallback &callback,
569 const GSMScanResults &results,
570 const Error &error) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500571 VLOG(2) << __func__;
572 if (error.IsFailure()) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500573 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500574 return;
575 }
576 found_networks_.clear();
577 for (GSMScanResults::const_iterator it = results.begin();
578 it != results.end(); ++it) {
579 found_networks_.push_back(ParseScanResult(*it));
580 }
Darin Petkovb7828b02012-02-03 12:34:30 +0100581 cellular()->adaptor()->EmitStringmapsChanged(flimflam::kFoundNetworksProperty,
582 found_networks_);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500583 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500584}
585
586Stringmap CellularCapabilityGSM::ParseScanResult(const GSMScanResult &result) {
Darin Petkov1272a432011-11-10 15:53:37 +0100587 Stringmap parsed;
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500588 for (GSMScanResult::const_iterator it = result.begin();
589 it != result.end(); ++it) {
Darin Petkov1272a432011-11-10 15:53:37 +0100590 // TODO(petkov): Define these in system_api/service_constants.h. The
591 // numerical values are taken from 3GPP TS 27.007 Section 7.3.
592 static const char * const kStatusString[] = {
593 "unknown",
594 "available",
595 "current",
596 "forbidden",
597 };
598 static const char * const kTechnologyString[] = {
599 flimflam::kNetworkTechnologyGsm,
600 "GSM Compact",
601 flimflam::kNetworkTechnologyUmts,
602 flimflam::kNetworkTechnologyEdge,
603 "HSDPA",
604 "HSUPA",
605 flimflam::kNetworkTechnologyHspa,
606 };
607 VLOG(2) << "Network property: " << it->first << " = " << it->second;
608 if (it->first == kNetworkPropertyStatus) {
609 int status = 0;
610 if (base::StringToInt(it->second, &status) &&
611 status >= 0 &&
612 status < static_cast<int>(arraysize(kStatusString))) {
613 parsed[flimflam::kStatusProperty] = kStatusString[status];
614 } else {
615 LOG(ERROR) << "Unexpected status value: " << it->second;
616 }
617 } else if (it->first == kNetworkPropertyID) {
618 parsed[flimflam::kNetworkIdProperty] = it->second;
619 } else if (it->first == kNetworkPropertyLongName) {
620 parsed[flimflam::kLongNameProperty] = it->second;
621 } else if (it->first == kNetworkPropertyShortName) {
622 parsed[flimflam::kShortNameProperty] = it->second;
623 } else if (it->first == kNetworkPropertyAccessTechnology) {
624 int tech = 0;
625 if (base::StringToInt(it->second, &tech) &&
626 tech >= 0 &&
627 tech < static_cast<int>(arraysize(kTechnologyString))) {
628 parsed[flimflam::kTechnologyProperty] = kTechnologyString[tech];
629 } else {
630 LOG(ERROR) << "Unexpected technology value: " << it->second;
631 }
632 } else {
633 LOG(WARNING) << "Unknown network property ignored: " << it->first;
634 }
635 }
636 // If the long name is not available but the network ID is, look up the long
637 // name in the mobile provider database.
638 if ((!ContainsKey(parsed, flimflam::kLongNameProperty) ||
639 parsed[flimflam::kLongNameProperty].empty()) &&
640 ContainsKey(parsed, flimflam::kNetworkIdProperty)) {
641 mobile_provider *provider =
642 mobile_provider_lookup_by_network(
643 cellular()->provider_db(),
644 parsed[flimflam::kNetworkIdProperty].c_str());
645 if (provider) {
646 const char *long_name = mobile_provider_get_name(provider);
647 if (long_name && *long_name) {
648 parsed[flimflam::kLongNameProperty] = long_name;
649 }
650 }
651 }
652 return parsed;
653}
654
Darin Petkovae0c64e2011-11-15 15:50:27 +0100655void CellularCapabilityGSM::SetAccessTechnology(uint32 access_technology) {
656 access_technology_ = access_technology;
657 if (cellular()->service().get()) {
Darin Petkovb72cf402011-11-22 14:51:39 +0100658 cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
Darin Petkovae0c64e2011-11-15 15:50:27 +0100659 }
660}
661
Darin Petkov20c13ec2011-11-09 15:07:15 +0100662string CellularCapabilityGSM::GetNetworkTechnologyString() const {
Darin Petkovb72cf402011-11-22 14:51:39 +0100663 switch (access_technology_) {
664 case MM_MODEM_GSM_ACCESS_TECH_GSM:
665 case MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT:
666 return flimflam::kNetworkTechnologyGsm;
667 case MM_MODEM_GSM_ACCESS_TECH_GPRS:
668 return flimflam::kNetworkTechnologyGprs;
669 case MM_MODEM_GSM_ACCESS_TECH_EDGE:
670 return flimflam::kNetworkTechnologyEdge;
671 case MM_MODEM_GSM_ACCESS_TECH_UMTS:
672 return flimflam::kNetworkTechnologyUmts;
673 case MM_MODEM_GSM_ACCESS_TECH_HSDPA:
674 case MM_MODEM_GSM_ACCESS_TECH_HSUPA:
675 case MM_MODEM_GSM_ACCESS_TECH_HSPA:
676 return flimflam::kNetworkTechnologyHspa;
677 case MM_MODEM_GSM_ACCESS_TECH_HSPA_PLUS:
678 return flimflam::kNetworkTechnologyHspaPlus;
679 default:
680 break;
Darin Petkov20c13ec2011-11-09 15:07:15 +0100681 }
682 return "";
683}
684
685string CellularCapabilityGSM::GetRoamingStateString() const {
Darin Petkov184c54e2011-11-15 12:44:39 +0100686 switch (registration_state_) {
Darin Petkov20c13ec2011-11-09 15:07:15 +0100687 case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME:
688 return flimflam::kRoamingStateHome;
689 case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING:
690 return flimflam::kRoamingStateRoaming;
691 default:
692 break;
693 }
694 return flimflam::kRoamingStateUnknown;
695}
696
Darin Petkovae0c64e2011-11-15 15:50:27 +0100697void CellularCapabilityGSM::OnModemManagerPropertiesChanged(
698 const DBusPropertiesMap &properties) {
699 uint32 access_technology = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
700 if (DBusProperties::GetUint32(properties,
701 kPropertyAccessTechnology,
702 &access_technology)) {
703 SetAccessTechnology(access_technology);
704 }
Darin Petkov63138a92012-02-06 14:09:15 +0100705 bool emit = false;
706 uint32 locks = 0;
707 if (DBusProperties::GetUint32(
708 properties, kPropertyEnabledFacilityLocks, &locks)) {
709 sim_lock_status_.enabled = locks & MM_MODEM_GSM_FACILITY_SIM;
710 emit = true;
711 }
712 if (DBusProperties::GetString(
713 properties, kPropertyUnlockRequired, &sim_lock_status_.lock_type)) {
714 emit = true;
715 }
716 if (DBusProperties::GetUint32(
717 properties, kPropertyUnlockRetries, &sim_lock_status_.retries_left)) {
718 emit = true;
719 }
720 if (emit) {
721 cellular()->adaptor()->EmitKeyValueStoreChanged(
722 flimflam::kSIMLockStatusProperty, SimLockStatusToProperty(NULL));
723 }
Darin Petkovae0c64e2011-11-15 15:50:27 +0100724}
725
Eric Shienbrood9a245532012-03-07 14:20:39 -0500726void CellularCapabilityGSM::OnNetworkModeSignal(uint32 /*mode*/) {
Darin Petkov184c54e2011-11-15 12:44:39 +0100727 // TODO(petkov): Implement this.
728 NOTIMPLEMENTED();
729}
730
Eric Shienbrood9a245532012-03-07 14:20:39 -0500731void CellularCapabilityGSM::OnRegistrationInfoSignal(
732 uint32 status, const string &operator_code, const string &operator_name) {
733 VLOG(2) << __func__ << ": regstate=" << status
734 << ", opercode=" << operator_code
735 << ", opername=" << operator_name;
736 registration_state_ = status;
737 serving_operator_.SetCode(operator_code);
738 serving_operator_.SetName(operator_name);
739 UpdateOperatorInfo();
740 cellular()->HandleNewRegistrationState();
Darin Petkov184c54e2011-11-15 12:44:39 +0100741}
742
Eric Shienbrood9a245532012-03-07 14:20:39 -0500743void CellularCapabilityGSM::OnSignalQualitySignal(uint32 quality) {
Darin Petkov184c54e2011-11-15 12:44:39 +0100744 cellular()->HandleNewSignalQuality(quality);
745}
746
Eric Shienbrood9a245532012-03-07 14:20:39 -0500747void CellularCapabilityGSM::OnGetRegistrationInfoReply(
748 uint32 status, const string &operator_code, const string &operator_name,
749 const Error &error) {
750 if (error.IsSuccess())
751 OnRegistrationInfoSignal(status, operator_code, operator_name);
752}
753
754void CellularCapabilityGSM::OnGetSignalQualityReply(uint32 quality,
755 const Error &error) {
756 if (error.IsSuccess())
757 OnSignalQualitySignal(quality);
758}
759
760void CellularCapabilityGSM::OnGetIMEIReply(const ResultCallback &callback,
761 const string &imei,
762 const Error &error) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500763 if (error.IsSuccess()) {
764 VLOG(2) << "IMEI: " << imei;
765 imei_ = imei;
766 } else {
767 VLOG(2) << "GetIMEI failed - " << error;
768 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500769 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500770}
771
Eric Shienbrood9a245532012-03-07 14:20:39 -0500772void CellularCapabilityGSM::OnGetIMSIReply(const ResultCallback &callback,
773 const string &imsi,
774 const Error &error) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500775 if (error.IsSuccess()) {
776 VLOG(2) << "IMSI: " << imsi;
777 imsi_ = imsi;
778 SetHomeProvider();
779 } else {
780 VLOG(2) << "GetIMSI failed - " << error;
781 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500782 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500783}
784
Eric Shienbrood9a245532012-03-07 14:20:39 -0500785void CellularCapabilityGSM::OnGetSPNReply(const ResultCallback &callback,
786 const string &spn,
787 const Error &error) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500788 if (error.IsSuccess()) {
789 VLOG(2) << "SPN: " << spn;
790 spn_ = spn;
791 SetHomeProvider();
792 } else {
793 VLOG(2) << "GetSPN failed - " << error;
794 }
Thieu Le923006b2012-04-05 16:32:58 -0700795 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500796}
797
Eric Shienbrood9a245532012-03-07 14:20:39 -0500798void CellularCapabilityGSM::OnGetMSISDNReply(const ResultCallback &callback,
799 const string &msisdn,
800 const Error &error) {
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500801 if (error.IsSuccess()) {
802 VLOG(2) << "MSISDN: " << msisdn;
803 mdn_ = msisdn;
804 } else {
805 VLOG(2) << "GetMSISDN failed - " << error;
806 }
Thieu Le923006b2012-04-05 16:32:58 -0700807 callback.Run(error);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500808}
809
Darin Petkovdaf43862011-10-27 11:37:28 +0200810} // namespace shill