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