1 //
2 // Copyright (C) 2012 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "shill/cellular/cellular_capability_gsm.h"
18
19 #include <string>
20 #include <vector>
21
22 #include <base/bind.h>
23 #include <base/stl_util.h>
24 #include <base/strings/string_number_conversions.h>
25 #include <base/strings/stringprintf.h>
26 #if defined(__ANDROID__)
27 #include <dbus/service_constants.h>
28 #else
29 #include <chromeos/dbus/service_constants.h>
30 #endif // __ANDROID__
31 #include <mm/mm-modem.h>
32
33 #include "shill/adaptor_interfaces.h"
34 #include "shill/cellular/cellular_service.h"
35 #include "shill/control_interface.h"
36 #include "shill/error.h"
37 #include "shill/logging.h"
38 #include "shill/property_accessor.h"
39
40 using base::Bind;
41 using std::string;
42 using std::vector;
43
44 namespace shill {
45
46 namespace Logging {
47 static auto kModuleLogScope = ScopeLogger::kCellular;
ObjectID(CellularCapabilityGSM * c)48 static string ObjectID(CellularCapabilityGSM* c) {
49 return c->cellular()->GetRpcIdentifier();
50 }
51 }
52
53 // static
54 const char CellularCapabilityGSM::kNetworkPropertyAccessTechnology[] =
55 "access-tech";
56 const char CellularCapabilityGSM::kNetworkPropertyID[] = "operator-num";
57 const char CellularCapabilityGSM::kNetworkPropertyLongName[] = "operator-long";
58 const char CellularCapabilityGSM::kNetworkPropertyShortName[] =
59 "operator-short";
60 const char CellularCapabilityGSM::kNetworkPropertyStatus[] = "status";
61 const char CellularCapabilityGSM::kPhoneNumber[] = "*99#";
62 const char CellularCapabilityGSM::kPropertyAccessTechnology[] =
63 "AccessTechnology";
64 const char CellularCapabilityGSM::kPropertyEnabledFacilityLocks[] =
65 "EnabledFacilityLocks";
66 const char CellularCapabilityGSM::kPropertyUnlockRequired[] = "UnlockRequired";
67 const char CellularCapabilityGSM::kPropertyUnlockRetries[] = "UnlockRetries";
68
69 const int CellularCapabilityGSM::kGetIMSIRetryLimit = 40;
70 const int64_t CellularCapabilityGSM::kGetIMSIRetryDelayMilliseconds = 500;
71
72
CellularCapabilityGSM(Cellular * cellular,ControlInterface * control_interface,ModemInfo * modem_info)73 CellularCapabilityGSM::CellularCapabilityGSM(
74 Cellular* cellular,
75 ControlInterface* control_interface,
76 ModemInfo* modem_info)
77 : CellularCapabilityClassic(cellular, control_interface, modem_info),
78 weak_ptr_factory_(this),
79 mobile_operator_info_(new MobileOperatorInfo(cellular->dispatcher(),
80 "ParseScanResult")),
81 registration_state_(MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN),
82 access_technology_(MM_MODEM_GSM_ACCESS_TECH_UNKNOWN),
83 home_provider_info_(nullptr),
84 get_imsi_retries_(0),
85 get_imsi_retry_delay_milliseconds_(kGetIMSIRetryDelayMilliseconds) {
86 SLOG(this, 2) << "Cellular capability constructed: GSM";
87 mobile_operator_info_->Init();
88 HelpRegisterConstDerivedKeyValueStore(
89 kSIMLockStatusProperty, &CellularCapabilityGSM::SimLockStatusToProperty);
90 this->cellular()->set_scanning_supported(true);
91
92 // TODO(benchan): This is a hack to initialize the GSM card proxy for GetIMSI
93 // before InitProxies is called. There are side-effects of calling InitProxies
94 // before the device is enabled. It's better to refactor InitProxies such that
95 // proxies can be created when the cellular device/capability is constructed,
96 // but callbacks for DBus signal updates are not set up until the device is
97 // enabled.
98 card_proxy_.reset(
99 control_interface->CreateModemGSMCardProxy(cellular->dbus_path(),
100 cellular->dbus_service()));
101 // TODO(benchan): To allow unit testing using a mock proxy without further
102 // complicating the code, the test proxy factory is set up to return a nullptr
103 // pointer when CellularCapabilityGSM is constructed. Refactor the code to
104 // avoid this hack.
105 if (card_proxy_.get())
106 InitProperties();
107 }
108
~CellularCapabilityGSM()109 CellularCapabilityGSM::~CellularCapabilityGSM() {}
110
GetTypeString() const111 string CellularCapabilityGSM::GetTypeString() const {
112 return kTechnologyFamilyGsm;
113 }
114
SimLockStatusToProperty(Error *)115 KeyValueStore CellularCapabilityGSM::SimLockStatusToProperty(Error* /*error*/) {
116 KeyValueStore status;
117 status.SetBool(kSIMLockEnabledProperty, sim_lock_status_.enabled);
118 status.SetString(kSIMLockTypeProperty, sim_lock_status_.lock_type);
119 status.SetUint(kSIMLockRetriesLeftProperty, sim_lock_status_.retries_left);
120 return status;
121 }
122
HelpRegisterConstDerivedKeyValueStore(const string & name,KeyValueStore (CellularCapabilityGSM::* get)(Error * error))123 void CellularCapabilityGSM::HelpRegisterConstDerivedKeyValueStore(
124 const string& name,
125 KeyValueStore(CellularCapabilityGSM::*get)(Error* error)) {
126 cellular()->mutable_store()->RegisterDerivedKeyValueStore(
127 name,
128 KeyValueStoreAccessor(
129 new CustomAccessor<CellularCapabilityGSM, KeyValueStore>(
130 this, get, nullptr)));
131 }
132
InitProxies()133 void CellularCapabilityGSM::InitProxies() {
134 CellularCapabilityClassic::InitProxies();
135 // TODO(benchan): Remove this check after refactoring the proxy
136 // initialization.
137 if (!card_proxy_.get()) {
138 card_proxy_.reset(
139 control_interface()->CreateModemGSMCardProxy(
140 cellular()->dbus_path(), cellular()->dbus_service()));
141 }
142 network_proxy_.reset(
143 control_interface()->CreateModemGSMNetworkProxy(
144 cellular()->dbus_path(), cellular()->dbus_service()));
145 network_proxy_->set_signal_quality_callback(
146 Bind(&CellularCapabilityGSM::OnSignalQualitySignal,
147 weak_ptr_factory_.GetWeakPtr()));
148 network_proxy_->set_network_mode_callback(
149 Bind(&CellularCapabilityGSM::OnNetworkModeSignal,
150 weak_ptr_factory_.GetWeakPtr()));
151 network_proxy_->set_registration_info_callback(
152 Bind(&CellularCapabilityGSM::OnRegistrationInfoSignal,
153 weak_ptr_factory_.GetWeakPtr()));
154 }
155
InitProperties()156 void CellularCapabilityGSM::InitProperties() {
157 CellularTaskList* tasks = new CellularTaskList();
158 ResultCallback cb_ignore_error =
159 Bind(&CellularCapabilityGSM::StepCompletedCallback,
160 weak_ptr_factory_.GetWeakPtr(), ResultCallback(), true, tasks);
161 // Chrome checks if a SIM is present before allowing the modem to be enabled,
162 // so shill needs to obtain IMSI, as an indicator of SIM presence, even
163 // before the device is enabled.
164 tasks->push_back(Bind(&CellularCapabilityGSM::GetIMSI,
165 weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
166 RunNextStep(tasks);
167 }
168
StartModem(Error * error,const ResultCallback & callback)169 void CellularCapabilityGSM::StartModem(Error* error,
170 const ResultCallback& callback) {
171 InitProxies();
172
173 CellularTaskList* tasks = new CellularTaskList();
174 ResultCallback cb =
175 Bind(&CellularCapabilityGSM::StepCompletedCallback,
176 weak_ptr_factory_.GetWeakPtr(), callback, false, tasks);
177 ResultCallback cb_ignore_error =
178 Bind(&CellularCapabilityGSM::StepCompletedCallback,
179 weak_ptr_factory_.GetWeakPtr(), callback, true, tasks);
180 if (!cellular()->IsUnderlyingDeviceEnabled())
181 tasks->push_back(Bind(&CellularCapabilityGSM::EnableModem,
182 weak_ptr_factory_.GetWeakPtr(), cb));
183 // If we're within range of the home network, the modem will try to
184 // register once it's enabled, or may be already registered if we
185 // started out enabled.
186 if (!IsUnderlyingDeviceRegistered() &&
187 !cellular()->selected_network().empty())
188 tasks->push_back(Bind(&CellularCapabilityGSM::Register,
189 weak_ptr_factory_.GetWeakPtr(), cb));
190 tasks->push_back(Bind(&CellularCapabilityGSM::GetIMEI,
191 weak_ptr_factory_.GetWeakPtr(), cb));
192 get_imsi_retries_ = 0;
193 tasks->push_back(Bind(&CellularCapabilityGSM::GetIMSI,
194 weak_ptr_factory_.GetWeakPtr(), cb));
195 tasks->push_back(Bind(&CellularCapabilityGSM::GetSPN,
196 weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
197 tasks->push_back(Bind(&CellularCapabilityGSM::GetMSISDN,
198 weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
199 tasks->push_back(Bind(&CellularCapabilityGSM::GetProperties,
200 weak_ptr_factory_.GetWeakPtr(), cb));
201 tasks->push_back(Bind(&CellularCapabilityGSM::GetModemInfo,
202 weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
203 tasks->push_back(Bind(&CellularCapabilityGSM::FinishEnable,
204 weak_ptr_factory_.GetWeakPtr(), cb));
205
206 RunNextStep(tasks);
207 }
208
IsUnderlyingDeviceRegistered() const209 bool CellularCapabilityGSM::IsUnderlyingDeviceRegistered() const {
210 switch (cellular()->modem_state()) {
211 case Cellular::kModemStateFailed:
212 case Cellular::kModemStateUnknown:
213 case Cellular::kModemStateDisabled:
214 case Cellular::kModemStateInitializing:
215 case Cellular::kModemStateLocked:
216 case Cellular::kModemStateDisabling:
217 case Cellular::kModemStateEnabling:
218 case Cellular::kModemStateEnabled:
219 return false;
220 case Cellular::kModemStateSearching:
221 case Cellular::kModemStateRegistered:
222 case Cellular::kModemStateDisconnecting:
223 case Cellular::kModemStateConnecting:
224 case Cellular::kModemStateConnected:
225 return true;
226 }
227 return false;
228 }
229
ReleaseProxies()230 void CellularCapabilityGSM::ReleaseProxies() {
231 SLOG(this, 2) << __func__;
232 CellularCapabilityClassic::ReleaseProxies();
233 card_proxy_.reset();
234 network_proxy_.reset();
235 }
236
AreProxiesInitialized() const237 bool CellularCapabilityGSM::AreProxiesInitialized() const {
238 return (CellularCapabilityClassic::AreProxiesInitialized() &&
239 card_proxy_.get() && network_proxy_.get());
240 }
241
OnServiceCreated()242 void CellularCapabilityGSM::OnServiceCreated() {
243 cellular()->service()->SetActivationState(kActivationStateActivated);
244 }
245
246 // Create the list of APNs to try, in the following order:
247 // - last APN that resulted in a successful connection attempt on the
248 // current network (if any)
249 // - the APN, if any, that was set by the user
250 // - the list of APNs found in the mobile broadband provider DB for the
251 // home provider associated with the current SIM
252 // - as a last resort, attempt to connect with no APN
SetupApnTryList()253 void CellularCapabilityGSM::SetupApnTryList() {
254 apn_try_list_.clear();
255
256 DCHECK(cellular()->service().get());
257 const Stringmap* apn_info = cellular()->service()->GetLastGoodApn();
258 if (apn_info)
259 apn_try_list_.push_back(*apn_info);
260
261 apn_info = cellular()->service()->GetUserSpecifiedApn();
262 if (apn_info)
263 apn_try_list_.push_back(*apn_info);
264
265 apn_try_list_.insert(apn_try_list_.end(),
266 cellular()->apn_list().begin(),
267 cellular()->apn_list().end());
268 }
269
SetupConnectProperties(KeyValueStore * properties)270 void CellularCapabilityGSM::SetupConnectProperties(
271 KeyValueStore* properties) {
272 SetupApnTryList();
273 FillConnectPropertyMap(properties);
274 }
275
FillConnectPropertyMap(KeyValueStore * properties)276 void CellularCapabilityGSM::FillConnectPropertyMap(
277 KeyValueStore* properties) {
278 properties->SetString(kConnectPropertyPhoneNumber, kPhoneNumber);
279
280 if (!AllowRoaming())
281 properties->SetBool(kConnectPropertyHomeOnly, true);
282
283 if (!apn_try_list_.empty()) {
284 // Leave the APN at the front of the list, so that it can be recorded
285 // if the connect attempt succeeds.
286 Stringmap apn_info = apn_try_list_.front();
287 SLOG(this, 2) << __func__ << ": Using APN " << apn_info[kApnProperty];
288 properties->SetString(kConnectPropertyApn, apn_info[kApnProperty]);
289 if (ContainsKey(apn_info, kApnUsernameProperty))
290 properties->SetString(kConnectPropertyApnUsername,
291 apn_info[kApnUsernameProperty]);
292 if (ContainsKey(apn_info, kApnPasswordProperty))
293 properties->SetString(kConnectPropertyApnPassword,
294 apn_info[kApnPasswordProperty]);
295 }
296 }
297
Connect(const KeyValueStore & properties,Error * error,const ResultCallback & callback)298 void CellularCapabilityGSM::Connect(const KeyValueStore& properties,
299 Error* error,
300 const ResultCallback& callback) {
301 SLOG(this, 2) << __func__;
302 ResultCallback cb = Bind(&CellularCapabilityGSM::OnConnectReply,
303 weak_ptr_factory_.GetWeakPtr(),
304 callback);
305 simple_proxy_->Connect(properties, error, cb, kTimeoutConnect);
306 }
307
OnConnectReply(const ResultCallback & callback,const Error & error)308 void CellularCapabilityGSM::OnConnectReply(const ResultCallback& callback,
309 const Error& error) {
310 CellularServiceRefPtr service = cellular()->service();
311 if (!service) {
312 // The service could have been deleted before our Connect() request
313 // completes if the modem was enabled and then quickly disabled.
314 apn_try_list_.clear();
315 } else if (error.IsFailure()) {
316 service->ClearLastGoodApn();
317 // The APN that was just tried (and failed) is still at the
318 // front of the list, about to be removed. If the list is empty
319 // after that, try one last time without an APN. This may succeed
320 // with some modems in some cases.
321 if (error.type() == Error::kInvalidApn && !apn_try_list_.empty()) {
322 apn_try_list_.pop_front();
323 SLOG(this, 2) << "Connect failed with invalid APN, "
324 << apn_try_list_.size() << " remaining APNs to try";
325 KeyValueStore props;
326 FillConnectPropertyMap(&props);
327 Error error;
328 Connect(props, &error, callback);
329 return;
330 }
331 } else if (!apn_try_list_.empty()) {
332 service->SetLastGoodApn(apn_try_list_.front());
333 apn_try_list_.clear();
334 }
335 if (!callback.is_null())
336 callback.Run(error);
337 }
338
AllowRoaming()339 bool CellularCapabilityGSM::AllowRoaming() {
340 return cellular()->provider_requires_roaming() || allow_roaming_property();
341 }
342
343 // always called from an async context
GetIMEI(const ResultCallback & callback)344 void CellularCapabilityGSM::GetIMEI(const ResultCallback& callback) {
345 SLOG(this, 2) << __func__;
346 CHECK(!callback.is_null());
347 Error error;
348 if (cellular()->imei().empty()) {
349 GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetIMEIReply,
350 weak_ptr_factory_.GetWeakPtr(), callback);
351 card_proxy_->GetIMEI(&error, cb, kTimeoutDefault);
352 if (error.IsFailure())
353 callback.Run(error);
354 } else {
355 SLOG(this, 2) << "Already have IMEI " << cellular()->imei();
356 callback.Run(error);
357 }
358 }
359
360 // always called from an async context
GetIMSI(const ResultCallback & callback)361 void CellularCapabilityGSM::GetIMSI(const ResultCallback& callback) {
362 SLOG(this, 2) << __func__;
363 CHECK(!callback.is_null());
364 Error error;
365 if (cellular()->imsi().empty()) {
366 GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetIMSIReply,
367 weak_ptr_factory_.GetWeakPtr(),
368 callback);
369 card_proxy_->GetIMSI(&error, cb, kTimeoutDefault);
370 if (error.IsFailure()) {
371 cellular()->home_provider_info()->Reset();
372 callback.Run(error);
373 }
374 } else {
375 SLOG(this, 2) << "Already have IMSI " << cellular()->imsi();
376 callback.Run(error);
377 }
378 }
379
380 // always called from an async context
GetSPN(const ResultCallback & callback)381 void CellularCapabilityGSM::GetSPN(const ResultCallback& callback) {
382 SLOG(this, 2) << __func__;
383 CHECK(!callback.is_null());
384 Error error;
385 if (spn_.empty()) {
386 GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetSPNReply,
387 weak_ptr_factory_.GetWeakPtr(),
388 callback);
389 card_proxy_->GetSPN(&error, cb, kTimeoutDefault);
390 if (error.IsFailure())
391 callback.Run(error);
392 } else {
393 SLOG(this, 2) << "Already have SPN " << spn_;
394 callback.Run(error);
395 }
396 }
397
398 // always called from an async context
GetMSISDN(const ResultCallback & callback)399 void CellularCapabilityGSM::GetMSISDN(const ResultCallback& callback) {
400 SLOG(this, 2) << __func__;
401 CHECK(!callback.is_null());
402 Error error;
403 string mdn = cellular()->mdn();
404 if (mdn.empty()) {
405 GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetMSISDNReply,
406 weak_ptr_factory_.GetWeakPtr(),
407 callback);
408 card_proxy_->GetMSISDN(&error, cb, kTimeoutDefault);
409 if (error.IsFailure())
410 callback.Run(error);
411 } else {
412 SLOG(this, 2) << "Already have MSISDN " << mdn;
413 callback.Run(error);
414 }
415 }
416
GetSignalQuality()417 void CellularCapabilityGSM::GetSignalQuality() {
418 SLOG(this, 2) << __func__;
419 SignalQualityCallback callback =
420 Bind(&CellularCapabilityGSM::OnGetSignalQualityReply,
421 weak_ptr_factory_.GetWeakPtr());
422 network_proxy_->GetSignalQuality(nullptr, callback, kTimeoutDefault);
423 }
424
GetRegistrationState()425 void CellularCapabilityGSM::GetRegistrationState() {
426 SLOG(this, 2) << __func__;
427 RegistrationInfoCallback callback =
428 Bind(&CellularCapabilityGSM::OnGetRegistrationInfoReply,
429 weak_ptr_factory_.GetWeakPtr());
430 network_proxy_->GetRegistrationInfo(nullptr, callback, kTimeoutDefault);
431 }
432
GetProperties(const ResultCallback & callback)433 void CellularCapabilityGSM::GetProperties(const ResultCallback& callback) {
434 SLOG(this, 2) << __func__;
435
436 // TODO(petkov): Switch to asynchronous calls (crbug.com/200687).
437 uint32_t tech = network_proxy_->AccessTechnology();
438 SetAccessTechnology(tech);
439 SLOG(this, 2) << "GSM AccessTechnology: " << tech;
440
441 // TODO(petkov): Switch to asynchronous calls (crbug.com/200687).
442 uint32_t locks = card_proxy_->EnabledFacilityLocks();
443 sim_lock_status_.enabled = locks & MM_MODEM_GSM_FACILITY_SIM;
444 SLOG(this, 2) << "GSM EnabledFacilityLocks: " << locks;
445
446 callback.Run(Error());
447 }
448
449 // always called from an async context
Register(const ResultCallback & callback)450 void CellularCapabilityGSM::Register(const ResultCallback& callback) {
451 SLOG(this, 2) << __func__ << " \"" << cellular()->selected_network()
452 << "\"";
453 CHECK(!callback.is_null());
454 Error error;
455 ResultCallback cb = Bind(&CellularCapabilityGSM::OnRegisterReply,
456 weak_ptr_factory_.GetWeakPtr(), callback);
457 network_proxy_->Register(cellular()->selected_network(), &error, cb,
458 kTimeoutRegister);
459 if (error.IsFailure())
460 callback.Run(error);
461 }
462
RegisterOnNetwork(const string & network_id,Error * error,const ResultCallback & callback)463 void CellularCapabilityGSM::RegisterOnNetwork(
464 const string& network_id,
465 Error* error,
466 const ResultCallback& callback) {
467 SLOG(this, 2) << __func__ << "(" << network_id << ")";
468 CHECK(error);
469 desired_network_ = network_id;
470 ResultCallback cb = Bind(&CellularCapabilityGSM::OnRegisterReply,
471 weak_ptr_factory_.GetWeakPtr(), callback);
472 network_proxy_->Register(network_id, error, cb, kTimeoutRegister);
473 }
474
OnRegisterReply(const ResultCallback & callback,const Error & error)475 void CellularCapabilityGSM::OnRegisterReply(const ResultCallback& callback,
476 const Error& error) {
477 SLOG(this, 2) << __func__ << "(" << error << ")";
478
479 if (error.IsSuccess()) {
480 cellular()->set_selected_network(desired_network_);
481 desired_network_.clear();
482 callback.Run(error);
483 return;
484 }
485 // If registration on the desired network failed,
486 // try to register on the home network.
487 if (!desired_network_.empty()) {
488 desired_network_.clear();
489 cellular()->set_selected_network("");
490 LOG(INFO) << "Couldn't register on selected network, trying home network";
491 Register(callback);
492 return;
493 }
494 callback.Run(error);
495 }
496
IsRegistered() const497 bool CellularCapabilityGSM::IsRegistered() const {
498 return (registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
499 registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING);
500 }
501
SetUnregistered(bool searching)502 void CellularCapabilityGSM::SetUnregistered(bool searching) {
503 // If we're already in some non-registered state, don't override that
504 if (registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
505 registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) {
506 registration_state_ =
507 (searching ? MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING :
508 MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE);
509 }
510 }
511
RequirePIN(const std::string & pin,bool require,Error * error,const ResultCallback & callback)512 void CellularCapabilityGSM::RequirePIN(
513 const std::string& pin, bool require,
514 Error* error, const ResultCallback& callback) {
515 CHECK(error);
516 card_proxy_->EnablePIN(pin, require, error, callback, kTimeoutDefault);
517 }
518
EnterPIN(const string & pin,Error * error,const ResultCallback & callback)519 void CellularCapabilityGSM::EnterPIN(const string& pin,
520 Error* error,
521 const ResultCallback& callback) {
522 CHECK(error);
523 card_proxy_->SendPIN(pin, error, callback, kTimeoutDefault);
524 }
525
UnblockPIN(const string & unblock_code,const string & pin,Error * error,const ResultCallback & callback)526 void CellularCapabilityGSM::UnblockPIN(const string& unblock_code,
527 const string& pin,
528 Error* error,
529 const ResultCallback& callback) {
530 CHECK(error);
531 card_proxy_->SendPUK(unblock_code, pin, error, callback, kTimeoutDefault);
532 }
533
ChangePIN(const string & old_pin,const string & new_pin,Error * error,const ResultCallback & callback)534 void CellularCapabilityGSM::ChangePIN(
535 const string& old_pin, const string& new_pin,
536 Error* error, const ResultCallback& callback) {
537 CHECK(error);
538 card_proxy_->ChangePIN(old_pin, new_pin, error, callback, kTimeoutDefault);
539 }
540
Scan(Error * error,const ResultStringmapsCallback & callback)541 void CellularCapabilityGSM::Scan(Error* error,
542 const ResultStringmapsCallback& callback) {
543 ScanResultsCallback cb = Bind(&CellularCapabilityGSM::OnScanReply,
544 weak_ptr_factory_.GetWeakPtr(), callback);
545 network_proxy_->Scan(error, cb, kTimeoutScan);
546 }
547
OnScanReply(const ResultStringmapsCallback & callback,const GSMScanResults & results,const Error & error)548 void CellularCapabilityGSM::OnScanReply(
549 const ResultStringmapsCallback& callback,
550 const GSMScanResults& results,
551 const Error& error) {
552 Stringmaps found_networks;
553 for (const auto& result : results)
554 found_networks.push_back(ParseScanResult(result));
555 callback.Run(found_networks, error);
556 }
557
ParseScanResult(const GSMScanResult & result)558 Stringmap CellularCapabilityGSM::ParseScanResult(const GSMScanResult& result) {
559 Stringmap parsed;
560 for (GSMScanResult::const_iterator it = result.begin();
561 it != result.end(); ++it) {
562 // TODO(petkov): Define these in system_api/service_constants.h. The
563 // numerical values are taken from 3GPP TS 27.007 Section 7.3.
564 static const char* const kStatusString[] = {
565 "unknown",
566 "available",
567 "current",
568 "forbidden",
569 };
570 static const char* const kTechnologyString[] = {
571 kNetworkTechnologyGsm,
572 "GSM Compact",
573 kNetworkTechnologyUmts,
574 kNetworkTechnologyEdge,
575 "HSDPA",
576 "HSUPA",
577 kNetworkTechnologyHspa,
578 };
579 SLOG(this, 2) << "Network property: " << it->first << " = "
580 << it->second;
581 if (it->first == kNetworkPropertyStatus) {
582 int status = 0;
583 if (base::StringToInt(it->second, &status) &&
584 status >= 0 &&
585 status < static_cast<int>(arraysize(kStatusString))) {
586 parsed[kStatusProperty] = kStatusString[status];
587 } else {
588 LOG(ERROR) << "Unexpected status value: " << it->second;
589 }
590 } else if (it->first == kNetworkPropertyID) {
591 parsed[kNetworkIdProperty] = it->second;
592 } else if (it->first == kNetworkPropertyLongName) {
593 parsed[kLongNameProperty] = it->second;
594 } else if (it->first == kNetworkPropertyShortName) {
595 parsed[kShortNameProperty] = it->second;
596 } else if (it->first == kNetworkPropertyAccessTechnology) {
597 int tech = 0;
598 if (base::StringToInt(it->second, &tech) &&
599 tech >= 0 &&
600 tech < static_cast<int>(arraysize(kTechnologyString))) {
601 parsed[kTechnologyProperty] = kTechnologyString[tech];
602 } else {
603 LOG(ERROR) << "Unexpected technology value: " << it->second;
604 }
605 } else {
606 LOG(WARNING) << "Unknown network property ignored: " << it->first;
607 }
608 }
609 // If the long name is not available but the network ID is, look up the long
610 // name in the mobile provider database.
611 if ((!ContainsKey(parsed, kLongNameProperty) ||
612 parsed[kLongNameProperty].empty()) &&
613 ContainsKey(parsed, kNetworkIdProperty)) {
614 mobile_operator_info_->Reset();
615 mobile_operator_info_->UpdateMCCMNC(parsed[kNetworkIdProperty]);
616 if (mobile_operator_info_->IsMobileNetworkOperatorKnown() &&
617 !mobile_operator_info_->operator_name().empty()) {
618 parsed[kLongNameProperty] = mobile_operator_info_->operator_name();
619 }
620 }
621 return parsed;
622 }
623
SetAccessTechnology(uint32_t access_technology)624 void CellularCapabilityGSM::SetAccessTechnology(uint32_t access_technology) {
625 access_technology_ = access_technology;
626 if (cellular()->service().get()) {
627 cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
628 }
629 }
630
GetNetworkTechnologyString() const631 string CellularCapabilityGSM::GetNetworkTechnologyString() const {
632 switch (access_technology_) {
633 case MM_MODEM_GSM_ACCESS_TECH_GSM:
634 case MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT:
635 return kNetworkTechnologyGsm;
636 case MM_MODEM_GSM_ACCESS_TECH_GPRS:
637 return kNetworkTechnologyGprs;
638 case MM_MODEM_GSM_ACCESS_TECH_EDGE:
639 return kNetworkTechnologyEdge;
640 case MM_MODEM_GSM_ACCESS_TECH_UMTS:
641 return kNetworkTechnologyUmts;
642 case MM_MODEM_GSM_ACCESS_TECH_HSDPA:
643 case MM_MODEM_GSM_ACCESS_TECH_HSUPA:
644 case MM_MODEM_GSM_ACCESS_TECH_HSPA:
645 return kNetworkTechnologyHspa;
646 case MM_MODEM_GSM_ACCESS_TECH_HSPA_PLUS:
647 return kNetworkTechnologyHspaPlus;
648 default:
649 break;
650 }
651 return "";
652 }
653
GetRoamingStateString() const654 string CellularCapabilityGSM::GetRoamingStateString() const {
655 switch (registration_state_) {
656 case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME:
657 return kRoamingStateHome;
658 case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING:
659 return kRoamingStateRoaming;
660 default:
661 break;
662 }
663 return kRoamingStateUnknown;
664 }
665
OnPropertiesChanged(const string & interface,const KeyValueStore & properties,const vector<string> & invalidated_properties)666 void CellularCapabilityGSM::OnPropertiesChanged(
667 const string& interface,
668 const KeyValueStore& properties,
669 const vector<string>& invalidated_properties) {
670 CellularCapabilityClassic::OnPropertiesChanged(interface,
671 properties,
672 invalidated_properties);
673 if (interface == MM_MODEM_GSM_NETWORK_INTERFACE) {
674 if (properties.ContainsUint(kPropertyAccessTechnology)) {
675 SetAccessTechnology(properties.GetUint(kPropertyAccessTechnology));
676 }
677 } else {
678 bool emit = false;
679 if (interface == MM_MODEM_GSM_CARD_INTERFACE) {
680 if (properties.ContainsUint(kPropertyEnabledFacilityLocks)) {
681 uint32_t locks = properties.GetUint(kPropertyEnabledFacilityLocks);
682 sim_lock_status_.enabled = locks & MM_MODEM_GSM_FACILITY_SIM;
683 emit = true;
684 }
685 } else if (interface == MM_MODEM_INTERFACE) {
686 if (properties.ContainsString(kPropertyUnlockRequired)) {
687 sim_lock_status_.lock_type =
688 properties.GetString(kPropertyUnlockRequired);
689 emit = true;
690 }
691 if (properties.ContainsUint(kPropertyUnlockRetries)) {
692 sim_lock_status_.retries_left =
693 properties.GetUint(kPropertyUnlockRetries);
694 emit = true;
695 }
696 }
697 // TODO(pprabhu) Rename |emit| to |sim_present| after |sim_lock_status|
698 // moves to cellular.
699 if (emit) {
700 cellular()->set_sim_present(true);
701 cellular()->adaptor()->EmitKeyValueStoreChanged(
702 kSIMLockStatusProperty, SimLockStatusToProperty(nullptr));
703 }
704 }
705 }
706
OnNetworkModeSignal(uint32_t)707 void CellularCapabilityGSM::OnNetworkModeSignal(uint32_t /*mode*/) {
708 // TODO(petkov): Implement this.
709 NOTIMPLEMENTED();
710 }
711
OnRegistrationInfoSignal(uint32_t status,const string & operator_code,const string & operator_name)712 void CellularCapabilityGSM::OnRegistrationInfoSignal(
713 uint32_t status, const string& operator_code, const string& operator_name) {
714 SLOG(this, 2) << __func__ << ": regstate=" << status
715 << ", opercode=" << operator_code
716 << ", opername=" << operator_name;
717 registration_state_ = status;
718 cellular()->serving_operator_info()->UpdateMCCMNC(operator_code);
719 cellular()->serving_operator_info()->UpdateOperatorName(operator_name);
720 cellular()->HandleNewRegistrationState();
721 }
722
OnSignalQualitySignal(uint32_t quality)723 void CellularCapabilityGSM::OnSignalQualitySignal(uint32_t quality) {
724 cellular()->HandleNewSignalQuality(quality);
725 }
726
OnGetRegistrationInfoReply(uint32_t status,const string & operator_code,const string & operator_name,const Error & error)727 void CellularCapabilityGSM::OnGetRegistrationInfoReply(
728 uint32_t status, const string& operator_code, const string& operator_name,
729 const Error& error) {
730 if (error.IsSuccess())
731 OnRegistrationInfoSignal(status, operator_code, operator_name);
732 }
733
OnGetSignalQualityReply(uint32_t quality,const Error & error)734 void CellularCapabilityGSM::OnGetSignalQualityReply(uint32_t quality,
735 const Error& error) {
736 if (error.IsSuccess())
737 OnSignalQualitySignal(quality);
738 }
739
OnGetIMEIReply(const ResultCallback & callback,const string & imei,const Error & error)740 void CellularCapabilityGSM::OnGetIMEIReply(const ResultCallback& callback,
741 const string& imei,
742 const Error& error) {
743 if (error.IsSuccess()) {
744 SLOG(this, 2) << "IMEI: " << imei;
745 cellular()->set_imei(imei);
746 } else {
747 SLOG(this, 2) << "GetIMEI failed - " << error;
748 }
749 callback.Run(error);
750 }
751
OnGetIMSIReply(const ResultCallback & callback,const string & imsi,const Error & error)752 void CellularCapabilityGSM::OnGetIMSIReply(const ResultCallback& callback,
753 const string& imsi,
754 const Error& error) {
755 if (error.IsSuccess()) {
756 SLOG(this, 2) << "IMSI: " << imsi;
757 cellular()->set_imsi(imsi);
758 cellular()->set_sim_present(true);
759 cellular()->home_provider_info()->UpdateIMSI(imsi);
760 // We do not currently obtain the IMSI OTA at all. Provide the IMSI from the
761 // SIM to the serving operator as well to aid in MVNO identification.
762 cellular()->serving_operator_info()->UpdateIMSI(imsi);
763 callback.Run(error);
764 } else if (!sim_lock_status_.lock_type.empty()) {
765 SLOG(this, 2) << "GetIMSI failed - SIM lock in place.";
766 cellular()->set_sim_present(true);
767 callback.Run(error);
768 } else {
769 cellular()->set_sim_present(false);
770 if (get_imsi_retries_++ < kGetIMSIRetryLimit) {
771 SLOG(this, 2) << "GetIMSI failed - " << error << ". Retrying";
772 base::Callback<void(void)> retry_get_imsi_cb =
773 Bind(&CellularCapabilityGSM::GetIMSI,
774 weak_ptr_factory_.GetWeakPtr(), callback);
775 cellular()->dispatcher()->PostDelayedTask(
776 retry_get_imsi_cb,
777 get_imsi_retry_delay_milliseconds_);
778 } else {
779 LOG(INFO) << "GetIMSI failed - " << error;
780 cellular()->home_provider_info()->Reset();
781 callback.Run(error);
782 }
783 }
784 }
785
OnGetSPNReply(const ResultCallback & callback,const string & spn,const Error & error)786 void CellularCapabilityGSM::OnGetSPNReply(const ResultCallback& callback,
787 const string& spn,
788 const Error& error) {
789 if (error.IsSuccess()) {
790 SLOG(this, 2) << "SPN: " << spn;
791 spn_ = spn;
792 cellular()->home_provider_info()->UpdateOperatorName(spn);
793 } else {
794 SLOG(this, 2) << "GetSPN failed - " << error;
795 }
796 callback.Run(error);
797 }
798
OnGetMSISDNReply(const ResultCallback & callback,const string & msisdn,const Error & error)799 void CellularCapabilityGSM::OnGetMSISDNReply(const ResultCallback& callback,
800 const string& msisdn,
801 const Error& error) {
802 if (error.IsSuccess()) {
803 SLOG(this, 2) << "MSISDN: " << msisdn;
804 cellular()->set_mdn(msisdn);
805 } else {
806 SLOG(this, 2) << "GetMSISDN failed - " << error;
807 }
808 callback.Run(error);
809 }
810
811 } // namespace shill
812