blob: 85956b38be7317588f113c714fa10007e485ccb3 [file] [log] [blame]
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2// 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_universal.h"
6
7#include <base/bind.h>
8#include <base/logging.h>
9#include <base/stl_util.h>
10#include <base/string_number_conversions.h>
11#include <base/stringprintf.h>
12#include <chromeos/dbus/service_constants.h>
13#include <mobile_provider.h>
14#include <mm/ModemManager-names.h>
15
16#include <string>
17#include <vector>
18
19#include "shill/adaptor_interfaces.h"
20#include "shill/cellular_service.h"
21#include "shill/error.h"
22#include "shill/property_accessor.h"
23#include "shill/proxy_factory.h"
24
25#ifdef MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN
26#error "Do not include mm-modem.h"
27#endif
28
29// The following are constants that should be found in
30// mm/ModemManager-names.h The are reproduced here as #define because
31// that is how they will appear eventually in ModemManager-names.h
32#define MM_MODEM_SIMPLE_CONNECT_PIN "pin"
33#define MM_MODEM_SIMPLE_CONNECT_OPERATOR_ID "operator-id"
34#define MM_MODEM_SIMPLE_CONNECT_BANDS "bands"
35#define MM_MODEM_SIMPLE_CONNECT_ALLWOED_MODES "allowed-modes"
36#define MM_MODEM_SIMPLE_CONNECT_PREFERRED_MODE "preferred-mode"
37#define MM_MODEM_SIMPLE_CONNECT_APN "apn"
38#define MM_MODEM_SIMPLE_CONNECT_IP_TYPE "ip-type"
39#define MM_MODEM_SIMPLE_CONNECT_USER "user"
40#define MM_MODEM_SIMPLE_CONNECT_PASSWORD "password"
41#define MM_MODEM_SIMPLE_CONNECT_NUMBER "number"
42#define MM_MODEM_SIMPLE_CONNECT_ALLOW_ROAMING "allow-roaming"
43#define MM_MODEM_SIMPLE_CONNECT_RM_PROTOCOL "rm-protocol"
44
45using base::Bind;
46using std::string;
47using std::vector;
48
49namespace shill {
50
51// static
52unsigned int CellularCapabilityUniversal::friendly_service_name_id_ = 0;
53
54static const char kPhoneNumber[] = "*99#";
55
56static string AccessTechnologyToString(uint32 access_technologies) {
57 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_LTE)
58 return flimflam::kNetworkTechnologyLte;
59 if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 |
60 MM_MODEM_ACCESS_TECHNOLOGY_EVDOA |
61 MM_MODEM_ACCESS_TECHNOLOGY_EVDOB))
62 return flimflam::kNetworkTechnologyEvdo;
63 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_1XRTT)
64 return flimflam::kNetworkTechnology1Xrtt;
65 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS)
66 return flimflam::kNetworkTechnologyHspaPlus;
67 if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_HSPA |
68 MM_MODEM_ACCESS_TECHNOLOGY_HSUPA |
69 MM_MODEM_ACCESS_TECHNOLOGY_HSDPA))
70 return flimflam::kNetworkTechnologyHspa;
71 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_UMTS)
72 return flimflam::kNetworkTechnologyUmts;
73 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_EDGE)
74 return flimflam::kNetworkTechnologyEdge;
75 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_GPRS)
76 return flimflam::kNetworkTechnologyGprs;
77 if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT |
78 MM_MODEM_ACCESS_TECHNOLOGY_GSM))
79 return flimflam::kNetworkTechnologyGsm;
80 return "";
81}
82
83CellularCapabilityUniversal::CellularCapabilityUniversal(
84 Cellular *cellular,
85 ProxyFactory *proxy_factory)
86 : CellularCapability(cellular, proxy_factory),
87 weak_ptr_factory_(this),
88 registration_state_(MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN),
89 cdma_registration_state_(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN),
90 access_technologies_(MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN),
91 home_provider_(NULL),
92 scanning_supported_(true),
93 scanning_(false),
94 scan_interval_(0) {
95 VLOG(2) << "Cellular capability constructed: Universal";
96 PropertyStore *store = cellular->mutable_store();
97
98 store->RegisterConstString(flimflam::kCarrierProperty, &carrier_);
99 store->RegisterConstBool(flimflam::kSupportNetworkScanProperty,
100 &scanning_supported_);
101 store->RegisterConstString(flimflam::kEsnProperty, &esn_);
102 store->RegisterConstString(flimflam::kFirmwareRevisionProperty,
103 &firmware_revision_);
104 store->RegisterConstString(flimflam::kHardwareRevisionProperty,
105 &hardware_revision_);
106 store->RegisterConstString(flimflam::kImeiProperty, &imei_);
107 store->RegisterConstString(flimflam::kImsiProperty, &imsi_);
108 store->RegisterConstString(flimflam::kManufacturerProperty, &manufacturer_);
109 store->RegisterConstString(flimflam::kMdnProperty, &mdn_);
110 store->RegisterConstString(flimflam::kMeidProperty, &meid_);
111 store->RegisterConstString(flimflam::kMinProperty, &min_);
112 store->RegisterConstString(flimflam::kModelIDProperty, &model_id_);
113 store->RegisterConstString(flimflam::kSelectedNetworkProperty,
114 &selected_network_);
115 store->RegisterConstStringmaps(flimflam::kFoundNetworksProperty,
116 &found_networks_);
117 store->RegisterConstBool(flimflam::kScanningProperty, &scanning_);
118 store->RegisterUint16(flimflam::kScanIntervalProperty, &scan_interval_);
119 HelpRegisterDerivedKeyValueStore(
120 flimflam::kSIMLockStatusProperty,
121 &CellularCapabilityUniversal::SimLockStatusToProperty,
122 NULL);
123 store->RegisterConstStringmaps(flimflam::kCellularApnListProperty,
124 &apn_list_);
125}
126
127KeyValueStore CellularCapabilityUniversal::SimLockStatusToProperty(
128 Error */*error*/) {
129 KeyValueStore status;
130 status.SetBool(flimflam::kSIMLockEnabledProperty, sim_lock_status_.enabled);
131 status.SetString(flimflam::kSIMLockTypeProperty, sim_lock_status_.lock_type);
132 status.SetUint(flimflam::kSIMLockRetriesLeftProperty,
133 sim_lock_status_.retries_left);
134 return status;
135}
136
137void CellularCapabilityUniversal::HelpRegisterDerivedKeyValueStore(
138 const string &name,
139 KeyValueStore(CellularCapabilityUniversal::*get)(Error *error),
140 void(CellularCapabilityUniversal::*set)(
141 const KeyValueStore &value, Error *error)) {
142 cellular()->mutable_store()->RegisterDerivedKeyValueStore(
143 name,
144 KeyValueStoreAccessor(
145 new CustomAccessor<CellularCapabilityUniversal, KeyValueStore>(
146 this, get, set)));
147}
148
149void CellularCapabilityUniversal::InitProxies() {
150 modem_3gpp_proxy_.reset(
151 proxy_factory()->CreateMM1ModemModem3gppProxy(cellular()->dbus_path(),
152 cellular()->dbus_owner()));
153 modem_cdma_proxy_.reset(
154 proxy_factory()->CreateMM1ModemModemCdmaProxy(cellular()->dbus_path(),
155 cellular()->dbus_owner()));
156 modem_proxy_.reset(
157 proxy_factory()->CreateMM1ModemProxy(cellular()->dbus_path(),
158 cellular()->dbus_owner()));
159 modem_simple_proxy_.reset(
160 proxy_factory()->CreateMM1ModemSimpleProxy(cellular()->dbus_path(),
161 cellular()->dbus_owner()));
162 // Do not create a SIM proxy until the device is enabled because we
163 // do not yet know the object path of the sim object.
164
165 // TODO(jglasgow): register callbacks
166}
167
168void CellularCapabilityUniversal::StartModem(Error *error,
169 const ResultCallback &callback) {
170 OnUnsupportedOperation(__func__, error);
171}
172
173void CellularCapabilityUniversal::StopModem(Error *error,
174 const ResultCallback &callback) {
175 OnUnsupportedOperation(__func__, error);
176}
177
178void CellularCapabilityUniversal::Connect(const DBusPropertiesMap &properties,
179 Error *error,
180 const ResultCallback &callback) {
181 OnUnsupportedOperation(__func__, error);
182}
183
184void CellularCapabilityUniversal::Disconnect(Error *error,
185 const ResultCallback &callback) {
186 OnUnsupportedOperation(__func__, error);
187}
188
189void CellularCapabilityUniversal::Activate(const std::string &carrier,
190 Error *error,
191 const ResultCallback &callback) {
192 OnUnsupportedOperation(__func__, error);
193}
194
195void CellularCapabilityUniversal::ReleaseProxies() {
196 VLOG(2) << __func__;
197 modem_3gpp_proxy_.reset();
198 modem_cdma_proxy_.reset();
199 modem_proxy_.reset();
200 modem_simple_proxy_.reset();
201 sim_proxy_.reset();
202}
203
204void CellularCapabilityUniversal::OnServiceCreated() {
205 // If IMSI is available, base the service's storage identifier on it.
206 if (!imsi_.empty()) {
207 cellular()->service()->SetStorageIdentifier(
208 string(flimflam::kTypeCellular) + "_" +
209 cellular()->address() + "_" + imsi_);
210 }
211 cellular()->service()->SetActivationState(
212 flimflam::kActivationStateActivated);
213 UpdateServingOperator();
214}
215
216void CellularCapabilityUniversal::UpdateStatus(
217 const DBusPropertiesMap &properties) {
218 if (ContainsKey(properties, kPropertyIMSI)) {
219 SetHomeProvider();
220 }
221}
222
223// Create the list of APNs to try, in the following order:
224// - last APN that resulted in a successful connection attempt on the
225// current network (if any)
226// - the APN, if any, that was set by the user
227// - the list of APNs found in the mobile broadband provider DB for the
228// home provider associated with the current SIM
229// - as a last resort, attempt to connect with no APN
230void CellularCapabilityUniversal::SetupApnTryList() {
231 apn_try_list_.clear();
232
233 DCHECK(cellular()->service().get());
234 const Stringmap *apn_info = cellular()->service()->GetLastGoodApn();
235 if (apn_info)
236 apn_try_list_.push_back(*apn_info);
237
238 apn_info = cellular()->service()->GetUserSpecifiedApn();
239 if (apn_info)
240 apn_try_list_.push_back(*apn_info);
241
242 apn_try_list_.insert(apn_try_list_.end(), apn_list_.begin(), apn_list_.end());
243}
244
245void CellularCapabilityUniversal::SetupConnectProperties(
246 DBusPropertiesMap *properties) {
247 SetupApnTryList();
248 FillConnectPropertyMap(properties);
249}
250
251void CellularCapabilityUniversal::FillConnectPropertyMap(
252 DBusPropertiesMap *properties) {
253
254 // TODO(jglasgow): Is this really needed anymore?
255 (*properties)[MM_MODEM_SIMPLE_CONNECT_NUMBER].writer().append_string(
256 kPhoneNumber);
257
258 (*properties)[MM_MODEM_SIMPLE_CONNECT_ALLOW_ROAMING].writer().append_bool(
259 AllowRoaming());
260
261 if (!apn_try_list_.empty()) {
262 // Leave the APN at the front of the list, so that it can be recorded
263 // if the connect attempt succeeds.
264 Stringmap apn_info = apn_try_list_.front();
265 VLOG(2) << __func__ << ": Using APN " << apn_info[flimflam::kApnProperty];
266 (*properties)[MM_MODEM_SIMPLE_CONNECT_APN].writer().append_string(
267 apn_info[flimflam::kApnProperty].c_str());
268 if (ContainsKey(apn_info, flimflam::kApnUsernameProperty))
269 (*properties)[MM_MODEM_SIMPLE_CONNECT_USER].writer().append_string(
270 apn_info[flimflam::kApnUsernameProperty].c_str());
271 if (ContainsKey(apn_info, flimflam::kApnPasswordProperty))
272 (*properties)[MM_MODEM_SIMPLE_CONNECT_PASSWORD].writer().append_string(
273 apn_info[flimflam::kApnPasswordProperty].c_str());
274 }
275}
276
277void CellularCapabilityUniversal::OnConnectReply(const ResultCallback &callback,
278 const Error &error) {
279 if (error.IsFailure()) {
280 cellular()->service()->ClearLastGoodApn();
281 // The APN that was just tried (and failed) is still at the
282 // front of the list, about to be removed. If the list is empty
283 // after that, try one last time without an APN. This may succeed
284 // with some modems in some cases.
285 if (error.type() == Error::kInvalidApn && !apn_try_list_.empty()) {
286 apn_try_list_.pop_front();
287 VLOG(2) << "Connect failed with invalid APN, " << apn_try_list_.size()
288 << " remaining APNs to try";
289 DBusPropertiesMap props;
290 FillConnectPropertyMap(&props);
291 Error error;
292 Connect(props, &error, callback);
293 return;
294 }
295 cellular()->OnConnectFailed(error);
296 } else {
297 if (!apn_try_list_.empty()) {
298 cellular()->service()->SetLastGoodApn(apn_try_list_.front());
299 apn_try_list_.clear();
300 }
301 cellular()->OnConnected();
302 }
303
304 if (!callback.is_null())
305 callback.Run(error);
306}
307
308bool CellularCapabilityUniversal::AllowRoaming() {
309 bool requires_roaming =
310 home_provider_ ? home_provider_->requires_roaming : false;
311 return requires_roaming || allow_roaming_property();
312}
313
314void CellularCapabilityUniversal::GetRegistrationState() {
315 VLOG(2) << __func__;
316 string operator_code;
317 string operator_name;
318
319 const MMModem3gppRegistrationState state =
320 static_cast<MMModem3gppRegistrationState>(
321 modem_3gpp_proxy_->RegistrationState());
322 if (state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
323 state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) {
324 operator_code = modem_3gpp_proxy_->OperatorCode();
325 operator_name = modem_3gpp_proxy_->OperatorName();
326 }
327 On3GPPRegistrationChanged(state, operator_code, operator_name);
328}
329
330void CellularCapabilityUniversal::GetProperties(
331 const ResultCallback &callback) {
332 VLOG(2) << __func__;
333
334 // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
335 uint32 technologies = modem_proxy_->AccessTechnologies();
336 // TODO(jglasgow): figure out the most likely one that we are using....
337 SetAccessTechnologies(technologies);
338 VLOG(2) << "AccessTechnologies: " << technologies;
339
340 // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
341 uint32 locks = modem_3gpp_proxy_->EnabledFacilityLocks();
342 sim_lock_status_.enabled = locks & MM_MODEM_3GPP_FACILITY_SIM;
343 VLOG(2) << "GSM EnabledFacilityLocks: " << locks;
344
345 // TODO(jglasgow): Switch to asynchronous calls (crosbug.com/17583).
346 const DBus::Struct<unsigned int, bool> quality =
347 modem_proxy_->SignalQuality();
348 OnSignalQualityChanged(quality._1);
349
350 // TODO(jglasgow): Switch to asynchronous calls (crosbug.com/17583).
351 if (imei_.empty()) {
352 imei_ = modem_3gpp_proxy_->Imei();
353 }
354 if (imsi_.empty()) {
355 imsi_ = sim_proxy_->Imsi();
356 }
357 if (spn_.empty()) {
358 spn_ = sim_proxy_->OperatorName();
359 // TODO(jglasgow): May eventually want to get SPDI, etc
360 }
361 if (mdn_.empty()) {
362 // TODO(jglasgow): use OwnNumbers()
363 }
364 GetRegistrationState();
365
366 callback.Run(Error());
367}
368
369string CellularCapabilityUniversal::CreateFriendlyServiceName() {
370 VLOG(2) << __func__;
371 if (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME &&
372 !cellular()->home_provider().GetName().empty()) {
373 return cellular()->home_provider().GetName();
374 }
375 if (!serving_operator_.GetName().empty()) {
376 return serving_operator_.GetName();
377 }
378 if (!carrier_.empty()) {
379 return carrier_;
380 }
381 if (!serving_operator_.GetCode().empty()) {
382 return "cellular_" + serving_operator_.GetCode();
383 }
384 return base::StringPrintf("GSMNetwork%u", friendly_service_name_id_++);
385}
386
387void CellularCapabilityUniversal::SetHomeProvider() {
388 VLOG(2) << __func__ << "(IMSI: " << imsi_
389 << " SPN: " << spn_ << ")";
390 // TODO(petkov): The test for NULL provider_db should be done by
391 // mobile_provider_lookup_best_match.
392 if (imsi_.empty() || !cellular()->provider_db()) {
393 return;
394 }
395 mobile_provider *provider =
396 mobile_provider_lookup_best_match(
397 cellular()->provider_db(), spn_.c_str(), imsi_.c_str());
398 if (!provider) {
399 VLOG(2) << "GSM provider not found.";
400 return;
401 }
402 home_provider_ = provider;
403 Cellular::Operator oper;
404 if (provider->networks) {
405 oper.SetCode(provider->networks[0]);
406 }
407 if (provider->country) {
408 oper.SetCountry(provider->country);
409 }
410 if (spn_.empty()) {
411 const char *name = mobile_provider_get_name(provider);
412 if (name) {
413 oper.SetName(name);
414 }
415 } else {
416 oper.SetName(spn_);
417 }
418 cellular()->set_home_provider(oper);
419 InitAPNList();
420}
421
422void CellularCapabilityUniversal::UpdateOperatorInfo() {
423 VLOG(2) << __func__;
424 const string &network_id = serving_operator_.GetCode();
425 if (!network_id.empty()) {
426 VLOG(2) << "Looking up network id: " << network_id;
427 mobile_provider *provider =
428 mobile_provider_lookup_by_network(cellular()->provider_db(),
429 network_id.c_str());
430 if (provider) {
431 const char *provider_name = mobile_provider_get_name(provider);
432 if (provider_name && *provider_name) {
433 serving_operator_.SetName(provider_name);
434 if (provider->country) {
435 serving_operator_.SetCountry(provider->country);
436 }
437 VLOG(2) << "Operator name: " << serving_operator_.GetName()
438 << ", country: " << serving_operator_.GetCountry();
439 }
440 } else {
441 VLOG(2) << "GSM provider not found.";
442 }
443 }
444 UpdateServingOperator();
445}
446
447void CellularCapabilityUniversal::UpdateServingOperator() {
448 VLOG(2) << __func__;
449 if (cellular()->service().get()) {
450 cellular()->service()->SetServingOperator(serving_operator_);
451 }
452}
453
454void CellularCapabilityUniversal::InitAPNList() {
455 VLOG(2) << __func__;
456 if (!home_provider_) {
457 return;
458 }
459 apn_list_.clear();
460 for (int i = 0; i < home_provider_->num_apns; ++i) {
461 Stringmap props;
462 mobile_apn *apn = home_provider_->apns[i];
463 if (apn->value) {
464 props[flimflam::kApnProperty] = apn->value;
465 }
466 if (apn->username) {
467 props[flimflam::kApnUsernameProperty] = apn->username;
468 }
469 if (apn->password) {
470 props[flimflam::kApnPasswordProperty] = apn->password;
471 }
472 // Find the first localized and non-localized name, if any.
473 const localized_name *lname = NULL;
474 const localized_name *name = NULL;
475 for (int j = 0; j < apn->num_names; ++j) {
476 if (apn->names[j]->lang) {
477 if (!lname) {
478 lname = apn->names[j];
479 }
480 } else if (!name) {
481 name = apn->names[j];
482 }
483 }
484 if (name) {
485 props[flimflam::kApnNameProperty] = name->name;
486 }
487 if (lname) {
488 props[flimflam::kApnLocalizedNameProperty] = lname->name;
489 props[flimflam::kApnLanguageProperty] = lname->lang;
490 }
491 apn_list_.push_back(props);
492 }
493 cellular()->adaptor()->EmitStringmapsChanged(
494 flimflam::kCellularApnListProperty, apn_list_);
495}
496
497// always called from an async context
498void CellularCapabilityUniversal::Register(const ResultCallback &callback) {
499 VLOG(2) << __func__ << " \"" << selected_network_ << "\"";
500 CHECK(!callback.is_null());
501 Error error;
502 ResultCallback cb = Bind(&CellularCapabilityUniversal::OnRegisterReply,
503 weak_ptr_factory_.GetWeakPtr(), callback);
504 modem_3gpp_proxy_->Register(selected_network_, &error, cb, kTimeoutRegister);
505 if (error.IsFailure())
506 callback.Run(error);
507}
508
509void CellularCapabilityUniversal::RegisterOnNetwork(
510 const string &network_id,
511 Error *error,
512 const ResultCallback &callback) {
513 VLOG(2) << __func__ << "(" << network_id << ")";
514 CHECK(error);
515 desired_network_ = network_id;
516 ResultCallback cb = Bind(&CellularCapabilityUniversal::OnRegisterReply,
517 weak_ptr_factory_.GetWeakPtr(), callback);
518 modem_3gpp_proxy_->Register(network_id, error, cb, kTimeoutRegister);
519}
520
521void CellularCapabilityUniversal::OnRegisterReply(
522 const ResultCallback &callback,
523 const Error &error) {
524 VLOG(2) << __func__ << "(" << error << ")";
525
526 if (error.IsSuccess()) {
527 selected_network_ = desired_network_;
528 desired_network_.clear();
529 callback.Run(error);
530 return;
531 }
532 // If registration on the desired network failed,
533 // try to register on the home network.
534 if (!desired_network_.empty()) {
535 desired_network_.clear();
536 selected_network_.clear();
537 LOG(INFO) << "Couldn't register on selected network, trying home network";
538 Register(callback);
539 return;
540 }
541 callback.Run(error);
542}
543
544bool CellularCapabilityUniversal::IsRegistered() {
545 return (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
546 registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING);
547}
548
549void CellularCapabilityUniversal::RequirePIN(
550 const std::string &pin, bool require,
551 Error *error, const ResultCallback &callback) {
552 CHECK(error);
553 sim_proxy_->EnablePin(pin, require, error, callback, kTimeoutDefault);
554}
555
556void CellularCapabilityUniversal::EnterPIN(const string &pin,
557 Error *error,
558 const ResultCallback &callback) {
559 CHECK(error);
560 sim_proxy_->SendPin(pin, error, callback, kTimeoutDefault);
561}
562
563void CellularCapabilityUniversal::UnblockPIN(const string &unblock_code,
564 const string &pin,
565 Error *error,
566 const ResultCallback &callback) {
567 CHECK(error);
568 sim_proxy_->SendPuk(unblock_code, pin, error, callback, kTimeoutDefault);
569}
570
571void CellularCapabilityUniversal::ChangePIN(
572 const string &old_pin, const string &new_pin,
573 Error *error, const ResultCallback &callback) {
574 CHECK(error);
575 sim_proxy_->ChangePin(old_pin, new_pin, error, callback, kTimeoutDefault);
576}
577
578void CellularCapabilityUniversal::Scan(Error *error,
579 const ResultCallback &callback) {
580 VLOG(2) << __func__;
581 // TODO(petkov): Defer scan requests if a scan is in progress already.
582 CHECK(error);
583 DBusPropertyMapsCallback cb = Bind(&CellularCapabilityUniversal::OnScanReply,
584 weak_ptr_factory_.GetWeakPtr(), callback);
585 modem_3gpp_proxy_->Scan(error, cb, kTimeoutScan);
586}
587
588void CellularCapabilityUniversal::OnScanReply(const ResultCallback &callback,
589 const ScanResults &results,
590 const Error &error) {
591 VLOG(2) << __func__;
592
593 // Error handling is weak. The current expectation is that on any
594 // error, found_networks_ should be cleared and a property change
595 // notification sent out.
596 //
597 // TODO(jglasgow): fix error handling
598 found_networks_.clear();
599 if (!error.IsFailure()) {
600 for (ScanResults::const_iterator it = results.begin();
601 it != results.end(); ++it) {
602 found_networks_.push_back(ParseScanResult(*it));
603 }
604 }
605 cellular()->adaptor()->EmitStringmapsChanged(flimflam::kFoundNetworksProperty,
606 found_networks_);
607 callback.Run(error);
608}
609
610Stringmap CellularCapabilityUniversal::ParseScanResult(
611 const ScanResult &result) {
612
613 static const char kStatusProperty[] = "status";
614 static const char kOperatorLongProperty[] = "operator-long";
615 static const char kOperatorShortProperty[] = "operator-short";
616 static const char kOperatorCodeProperty[] = "operator-code";
617 static const char kOperatorAccessTechnologyProperty[] = "access-technology";
618
619 /* ScanResults contain the following keys:
620
621 "status"
622 A MMModem3gppNetworkAvailability value representing network
623 availability status, given as an unsigned integer (signature "u").
624 This key will always be present.
625
626 "operator-long"
627 Long-format name of operator, given as a string value (signature
628 "s"). If the name is unknown, this field should not be present.
629
630 "operator-short"
631 Short-format name of operator, given as a string value
632 (signature "s"). If the name is unknown, this field should not
633 be present.
634
635 "operator-code"
636 Mobile code of the operator, given as a string value (signature
637 "s"). Returned in the format "MCCMNC", where MCC is the
638 three-digit ITU E.212 Mobile Country Code and MNC is the two- or
639 three-digit GSM Mobile Network Code. e.g. "31026" or "310260".
640
641 "access-technology"
642 A MMModemAccessTechnology value representing the generic access
643 technology used by this mobile network, given as an unsigned
644 integer (signature "u").
645 */
646 Stringmap parsed;
647
648 uint32 status;
649 if (DBusProperties::GetUint32(result, kStatusProperty, &status)) {
650 // numerical values are taken from 3GPP TS 27.007 Section 7.3.
651 static const char * const kStatusString[] = {
652 "unknown", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN
653 "available", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE
654 "current", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT
655 "forbidden", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN
656 };
657 parsed[flimflam::kStatusProperty] = kStatusString[status];
658 }
659
660 uint32 tech; // MMModemAccessTechnology
661 if (DBusProperties::GetUint32(result, kOperatorAccessTechnologyProperty,
662 &tech)) {
663 parsed[flimflam::kTechnologyProperty] = AccessTechnologyToString(tech);
664 }
665
666 string operator_long, operator_short, operator_code;
667 if (DBusProperties::GetString(result, kOperatorLongProperty, &operator_long))
668 parsed[flimflam::kLongNameProperty] = operator_long;
669 if (DBusProperties::GetString(result, kOperatorShortProperty,
670 &operator_short))
671 parsed[flimflam::kShortNameProperty] = operator_short;
672 if (DBusProperties::GetString(result, kOperatorCodeProperty, &operator_code))
673 parsed[flimflam::kNetworkIdProperty] = operator_code;
674
675 // If the long name is not available but the network ID is, look up the long
676 // name in the mobile provider database.
677 if ((!ContainsKey(parsed, flimflam::kLongNameProperty) ||
678 parsed[flimflam::kLongNameProperty].empty()) &&
679 ContainsKey(parsed, flimflam::kNetworkIdProperty)) {
680 mobile_provider *provider =
681 mobile_provider_lookup_by_network(
682 cellular()->provider_db(),
683 parsed[flimflam::kNetworkIdProperty].c_str());
684 if (provider) {
685 const char *long_name = mobile_provider_get_name(provider);
686 if (long_name && *long_name) {
687 parsed[flimflam::kLongNameProperty] = long_name;
688 }
689 }
690 }
691 return parsed;
692}
693
694void CellularCapabilityUniversal::SetAccessTechnologies(
695 uint32 access_technologies) {
696 access_technologies_ = access_technologies;
697 if (cellular()->service().get()) {
698 cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
699 }
700}
701
702string CellularCapabilityUniversal::GetNetworkTechnologyString() const {
703 // Order is imnportant. Return the highest speed technology
704 // TODO(jglasgow): change shill interfaces to a capability model
705
706 return AccessTechnologyToString(access_technologies_);
707}
708
709string CellularCapabilityUniversal::GetRoamingStateString() const {
710 switch (registration_state_) {
711 case MM_MODEM_3GPP_REGISTRATION_STATE_HOME:
712 return flimflam::kRoamingStateHome;
713 case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING:
714 return flimflam::kRoamingStateRoaming;
715 default:
716 break;
717 }
718 return flimflam::kRoamingStateUnknown;
719}
720
721void CellularCapabilityUniversal::GetSignalQuality() {
722 // TODO(jglasgow): implement
723 NOTIMPLEMENTED();
724}
725
726void CellularCapabilityUniversal::OnModemManagerPropertiesChanged(
727 const DBusPropertiesMap &properties) {
728
729 // TODO(jglasgow): When CreateDeviceFromModemProperties is fixed DCHECK
730 LOG(ERROR) << "Unexpected call to OnModemPropertiesChanged.";
731
732 uint32 access_technologies = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
733 if (DBusProperties::GetUint32(properties,
734 MM_MODEM_PROPERTY_ACCESSTECHNOLOGIES,
735 &access_technologies)) {
736 SetAccessTechnologies(access_technologies);
737 }
738 // Unlockrequired and SimLock
739 bool emit = false;
740
741 uint32_t lock_required; // This is really of type MMModemLock
742 if (DBusProperties::GetUint32(properties,
743 MM_MODEM_PROPERTY_UNLOCKREQUIRED,
744 &lock_required)) {
745 // TODO(jglasgow): set sim_lock_status_.lock_type
746 emit = true;
747 }
748 // TODO(jglasgow): Update PIN retries which are a{uu} and require parsing
749 // Get the property MM_MODEM_PROPERTY_UNLOCKRETRIES
750 // Set sim_lock_status_.retries_left
751
752 if (emit) {
753 cellular()->adaptor()->EmitKeyValueStoreChanged(
754 flimflam::kSIMLockStatusProperty, SimLockStatusToProperty(NULL));
755 }
756}
757
758void CellularCapabilityUniversal::OnDBusPropertiesChanged(
759 const string &interface,
760 const DBusPropertiesMap &changed_properties,
761 const vector<std::string> &invalidated_properties) {
762 // TODO(jglasgow): implement properties changed handling
763 NOTIMPLEMENTED();
764}
765
766void CellularCapabilityUniversal::OnModem3GPPPropertiesChanged(
767 const DBusPropertiesMap &properties) {
768 bool emit = false;
769 uint32 locks = 0;
770 if (DBusProperties::GetUint32(
771 properties, MM_MODEM_MODEM3GPP_PROPERTY_ENABLEDFACILITYLOCKS,
772 &locks)) {
773 sim_lock_status_.enabled = locks & MM_MODEM_3GPP_FACILITY_SIM;
774 emit = true;
775 }
776 // TODO(jglasgow): coordinate with changes to Modem properties
777 if (emit) {
778 cellular()->adaptor()->EmitKeyValueStoreChanged(
779 flimflam::kSIMLockStatusProperty, SimLockStatusToProperty(NULL));
780 }
781}
782
783void CellularCapabilityUniversal::OnNetworkModeSignal(uint32 /*mode*/) {
784 // TODO(petkov): Implement this.
785 NOTIMPLEMENTED();
786}
787
788void CellularCapabilityUniversal::On3GPPRegistrationChanged(
789 MMModem3gppRegistrationState state,
790 const string &operator_code,
791 const string &operator_name) {
792 VLOG(2) << __func__ << ": regstate=" << state
793 << ", opercode=" << operator_code
794 << ", opername=" << operator_name;
795 registration_state_ = state;
796 serving_operator_.SetCode(operator_code);
797 serving_operator_.SetName(operator_name);
798 UpdateOperatorInfo();
799 cellular()->HandleNewRegistrationState();
800}
801
802void CellularCapabilityUniversal::OnSignalQualityChanged(uint32 quality) {
803 cellular()->HandleNewSignalQuality(quality);
804}
805
806} // namespace shill