blob: 61e7acc8b131dd2b59b073b1f67c3ee3f5314440 [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()) {
Nathan Williams4b7c2a82012-04-13 15:19:47 -0400468 // TODO(njw): Switch to asynchronous calls (crosbug.com/17583).
469 vector<string> numbers = modem_proxy_->OwnNumbers();
470 if (numbers.size() > 0)
471 mdn_ = numbers[0];
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400472 }
473 GetRegistrationState();
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400474}
475
476string CellularCapabilityUniversal::CreateFriendlyServiceName() {
477 VLOG(2) << __func__;
478 if (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME &&
479 !cellular()->home_provider().GetName().empty()) {
480 return cellular()->home_provider().GetName();
481 }
482 if (!serving_operator_.GetName().empty()) {
483 return serving_operator_.GetName();
484 }
485 if (!carrier_.empty()) {
486 return carrier_;
487 }
488 if (!serving_operator_.GetCode().empty()) {
489 return "cellular_" + serving_operator_.GetCode();
490 }
491 return base::StringPrintf("GSMNetwork%u", friendly_service_name_id_++);
492}
493
494void CellularCapabilityUniversal::SetHomeProvider() {
495 VLOG(2) << __func__ << "(IMSI: " << imsi_
496 << " SPN: " << spn_ << ")";
497 // TODO(petkov): The test for NULL provider_db should be done by
498 // mobile_provider_lookup_best_match.
499 if (imsi_.empty() || !cellular()->provider_db()) {
500 return;
501 }
502 mobile_provider *provider =
503 mobile_provider_lookup_best_match(
504 cellular()->provider_db(), spn_.c_str(), imsi_.c_str());
505 if (!provider) {
506 VLOG(2) << "GSM provider not found.";
507 return;
508 }
509 home_provider_ = provider;
510 Cellular::Operator oper;
511 if (provider->networks) {
512 oper.SetCode(provider->networks[0]);
513 }
514 if (provider->country) {
515 oper.SetCountry(provider->country);
516 }
517 if (spn_.empty()) {
518 const char *name = mobile_provider_get_name(provider);
519 if (name) {
520 oper.SetName(name);
521 }
522 } else {
523 oper.SetName(spn_);
524 }
525 cellular()->set_home_provider(oper);
526 InitAPNList();
527}
528
529void CellularCapabilityUniversal::UpdateOperatorInfo() {
530 VLOG(2) << __func__;
531 const string &network_id = serving_operator_.GetCode();
532 if (!network_id.empty()) {
533 VLOG(2) << "Looking up network id: " << network_id;
534 mobile_provider *provider =
535 mobile_provider_lookup_by_network(cellular()->provider_db(),
536 network_id.c_str());
537 if (provider) {
538 const char *provider_name = mobile_provider_get_name(provider);
539 if (provider_name && *provider_name) {
540 serving_operator_.SetName(provider_name);
541 if (provider->country) {
542 serving_operator_.SetCountry(provider->country);
543 }
544 VLOG(2) << "Operator name: " << serving_operator_.GetName()
545 << ", country: " << serving_operator_.GetCountry();
546 }
547 } else {
548 VLOG(2) << "GSM provider not found.";
549 }
550 }
551 UpdateServingOperator();
552}
553
554void CellularCapabilityUniversal::UpdateServingOperator() {
555 VLOG(2) << __func__;
556 if (cellular()->service().get()) {
557 cellular()->service()->SetServingOperator(serving_operator_);
558 }
559}
560
561void CellularCapabilityUniversal::InitAPNList() {
562 VLOG(2) << __func__;
563 if (!home_provider_) {
564 return;
565 }
566 apn_list_.clear();
567 for (int i = 0; i < home_provider_->num_apns; ++i) {
568 Stringmap props;
569 mobile_apn *apn = home_provider_->apns[i];
570 if (apn->value) {
571 props[flimflam::kApnProperty] = apn->value;
572 }
573 if (apn->username) {
574 props[flimflam::kApnUsernameProperty] = apn->username;
575 }
576 if (apn->password) {
577 props[flimflam::kApnPasswordProperty] = apn->password;
578 }
579 // Find the first localized and non-localized name, if any.
580 const localized_name *lname = NULL;
581 const localized_name *name = NULL;
582 for (int j = 0; j < apn->num_names; ++j) {
583 if (apn->names[j]->lang) {
584 if (!lname) {
585 lname = apn->names[j];
586 }
587 } else if (!name) {
588 name = apn->names[j];
589 }
590 }
591 if (name) {
592 props[flimflam::kApnNameProperty] = name->name;
593 }
594 if (lname) {
595 props[flimflam::kApnLocalizedNameProperty] = lname->name;
596 props[flimflam::kApnLanguageProperty] = lname->lang;
597 }
598 apn_list_.push_back(props);
599 }
600 cellular()->adaptor()->EmitStringmapsChanged(
601 flimflam::kCellularApnListProperty, apn_list_);
602}
603
604// always called from an async context
605void CellularCapabilityUniversal::Register(const ResultCallback &callback) {
606 VLOG(2) << __func__ << " \"" << selected_network_ << "\"";
607 CHECK(!callback.is_null());
608 Error error;
609 ResultCallback cb = Bind(&CellularCapabilityUniversal::OnRegisterReply,
610 weak_ptr_factory_.GetWeakPtr(), callback);
611 modem_3gpp_proxy_->Register(selected_network_, &error, cb, kTimeoutRegister);
612 if (error.IsFailure())
613 callback.Run(error);
614}
615
616void CellularCapabilityUniversal::RegisterOnNetwork(
617 const string &network_id,
618 Error *error,
619 const ResultCallback &callback) {
620 VLOG(2) << __func__ << "(" << network_id << ")";
621 CHECK(error);
622 desired_network_ = network_id;
623 ResultCallback cb = Bind(&CellularCapabilityUniversal::OnRegisterReply,
624 weak_ptr_factory_.GetWeakPtr(), callback);
625 modem_3gpp_proxy_->Register(network_id, error, cb, kTimeoutRegister);
626}
627
628void CellularCapabilityUniversal::OnRegisterReply(
629 const ResultCallback &callback,
630 const Error &error) {
631 VLOG(2) << __func__ << "(" << error << ")";
632
633 if (error.IsSuccess()) {
634 selected_network_ = desired_network_;
635 desired_network_.clear();
636 callback.Run(error);
637 return;
638 }
639 // If registration on the desired network failed,
640 // try to register on the home network.
641 if (!desired_network_.empty()) {
642 desired_network_.clear();
643 selected_network_.clear();
644 LOG(INFO) << "Couldn't register on selected network, trying home network";
645 Register(callback);
646 return;
647 }
648 callback.Run(error);
649}
650
651bool CellularCapabilityUniversal::IsRegistered() {
652 return (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
653 registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING);
654}
655
656void CellularCapabilityUniversal::RequirePIN(
657 const std::string &pin, bool require,
658 Error *error, const ResultCallback &callback) {
659 CHECK(error);
660 sim_proxy_->EnablePin(pin, require, error, callback, kTimeoutDefault);
661}
662
663void CellularCapabilityUniversal::EnterPIN(const string &pin,
664 Error *error,
665 const ResultCallback &callback) {
666 CHECK(error);
667 sim_proxy_->SendPin(pin, error, callback, kTimeoutDefault);
668}
669
670void CellularCapabilityUniversal::UnblockPIN(const string &unblock_code,
671 const string &pin,
672 Error *error,
673 const ResultCallback &callback) {
674 CHECK(error);
675 sim_proxy_->SendPuk(unblock_code, pin, error, callback, kTimeoutDefault);
676}
677
678void CellularCapabilityUniversal::ChangePIN(
679 const string &old_pin, const string &new_pin,
680 Error *error, const ResultCallback &callback) {
681 CHECK(error);
682 sim_proxy_->ChangePin(old_pin, new_pin, error, callback, kTimeoutDefault);
683}
684
685void CellularCapabilityUniversal::Scan(Error *error,
686 const ResultCallback &callback) {
687 VLOG(2) << __func__;
688 // TODO(petkov): Defer scan requests if a scan is in progress already.
689 CHECK(error);
690 DBusPropertyMapsCallback cb = Bind(&CellularCapabilityUniversal::OnScanReply,
691 weak_ptr_factory_.GetWeakPtr(), callback);
692 modem_3gpp_proxy_->Scan(error, cb, kTimeoutScan);
693}
694
695void CellularCapabilityUniversal::OnScanReply(const ResultCallback &callback,
696 const ScanResults &results,
697 const Error &error) {
698 VLOG(2) << __func__;
699
700 // Error handling is weak. The current expectation is that on any
701 // error, found_networks_ should be cleared and a property change
702 // notification sent out.
703 //
704 // TODO(jglasgow): fix error handling
705 found_networks_.clear();
706 if (!error.IsFailure()) {
707 for (ScanResults::const_iterator it = results.begin();
708 it != results.end(); ++it) {
709 found_networks_.push_back(ParseScanResult(*it));
710 }
711 }
712 cellular()->adaptor()->EmitStringmapsChanged(flimflam::kFoundNetworksProperty,
713 found_networks_);
714 callback.Run(error);
715}
716
717Stringmap CellularCapabilityUniversal::ParseScanResult(
718 const ScanResult &result) {
719
720 static const char kStatusProperty[] = "status";
721 static const char kOperatorLongProperty[] = "operator-long";
722 static const char kOperatorShortProperty[] = "operator-short";
723 static const char kOperatorCodeProperty[] = "operator-code";
724 static const char kOperatorAccessTechnologyProperty[] = "access-technology";
725
726 /* ScanResults contain the following keys:
727
728 "status"
729 A MMModem3gppNetworkAvailability value representing network
730 availability status, given as an unsigned integer (signature "u").
731 This key will always be present.
732
733 "operator-long"
734 Long-format name of operator, given as a string value (signature
735 "s"). If the name is unknown, this field should not be present.
736
737 "operator-short"
738 Short-format name of operator, given as a string value
739 (signature "s"). If the name is unknown, this field should not
740 be present.
741
742 "operator-code"
743 Mobile code of the operator, given as a string value (signature
744 "s"). Returned in the format "MCCMNC", where MCC is the
745 three-digit ITU E.212 Mobile Country Code and MNC is the two- or
746 three-digit GSM Mobile Network Code. e.g. "31026" or "310260".
747
748 "access-technology"
749 A MMModemAccessTechnology value representing the generic access
750 technology used by this mobile network, given as an unsigned
751 integer (signature "u").
752 */
753 Stringmap parsed;
754
755 uint32 status;
756 if (DBusProperties::GetUint32(result, kStatusProperty, &status)) {
757 // numerical values are taken from 3GPP TS 27.007 Section 7.3.
758 static const char * const kStatusString[] = {
759 "unknown", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN
760 "available", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE
761 "current", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT
762 "forbidden", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN
763 };
764 parsed[flimflam::kStatusProperty] = kStatusString[status];
765 }
766
767 uint32 tech; // MMModemAccessTechnology
768 if (DBusProperties::GetUint32(result, kOperatorAccessTechnologyProperty,
769 &tech)) {
770 parsed[flimflam::kTechnologyProperty] = AccessTechnologyToString(tech);
771 }
772
773 string operator_long, operator_short, operator_code;
774 if (DBusProperties::GetString(result, kOperatorLongProperty, &operator_long))
775 parsed[flimflam::kLongNameProperty] = operator_long;
776 if (DBusProperties::GetString(result, kOperatorShortProperty,
777 &operator_short))
778 parsed[flimflam::kShortNameProperty] = operator_short;
779 if (DBusProperties::GetString(result, kOperatorCodeProperty, &operator_code))
780 parsed[flimflam::kNetworkIdProperty] = operator_code;
781
782 // If the long name is not available but the network ID is, look up the long
783 // name in the mobile provider database.
784 if ((!ContainsKey(parsed, flimflam::kLongNameProperty) ||
785 parsed[flimflam::kLongNameProperty].empty()) &&
786 ContainsKey(parsed, flimflam::kNetworkIdProperty)) {
787 mobile_provider *provider =
788 mobile_provider_lookup_by_network(
789 cellular()->provider_db(),
790 parsed[flimflam::kNetworkIdProperty].c_str());
791 if (provider) {
792 const char *long_name = mobile_provider_get_name(provider);
793 if (long_name && *long_name) {
794 parsed[flimflam::kLongNameProperty] = long_name;
795 }
796 }
797 }
798 return parsed;
799}
800
801void CellularCapabilityUniversal::SetAccessTechnologies(
802 uint32 access_technologies) {
803 access_technologies_ = access_technologies;
804 if (cellular()->service().get()) {
805 cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
806 }
807}
808
809string CellularCapabilityUniversal::GetNetworkTechnologyString() const {
810 // Order is imnportant. Return the highest speed technology
811 // TODO(jglasgow): change shill interfaces to a capability model
812
813 return AccessTechnologyToString(access_technologies_);
814}
815
816string CellularCapabilityUniversal::GetRoamingStateString() const {
817 switch (registration_state_) {
818 case MM_MODEM_3GPP_REGISTRATION_STATE_HOME:
819 return flimflam::kRoamingStateHome;
820 case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING:
821 return flimflam::kRoamingStateRoaming;
822 default:
823 break;
824 }
825 return flimflam::kRoamingStateUnknown;
826}
827
828void CellularCapabilityUniversal::GetSignalQuality() {
829 // TODO(jglasgow): implement
830 NOTIMPLEMENTED();
831}
832
833void CellularCapabilityUniversal::OnModemManagerPropertiesChanged(
834 const DBusPropertiesMap &properties) {
835
836 // TODO(jglasgow): When CreateDeviceFromModemProperties is fixed DCHECK
837 LOG(ERROR) << "Unexpected call to OnModemPropertiesChanged.";
838
839 uint32 access_technologies = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
840 if (DBusProperties::GetUint32(properties,
841 MM_MODEM_PROPERTY_ACCESSTECHNOLOGIES,
842 &access_technologies)) {
843 SetAccessTechnologies(access_technologies);
844 }
845 // Unlockrequired and SimLock
846 bool emit = false;
847
848 uint32_t lock_required; // This is really of type MMModemLock
849 if (DBusProperties::GetUint32(properties,
850 MM_MODEM_PROPERTY_UNLOCKREQUIRED,
851 &lock_required)) {
852 // TODO(jglasgow): set sim_lock_status_.lock_type
853 emit = true;
854 }
855 // TODO(jglasgow): Update PIN retries which are a{uu} and require parsing
856 // Get the property MM_MODEM_PROPERTY_UNLOCKRETRIES
857 // Set sim_lock_status_.retries_left
858
859 if (emit) {
860 cellular()->adaptor()->EmitKeyValueStoreChanged(
861 flimflam::kSIMLockStatusProperty, SimLockStatusToProperty(NULL));
862 }
863}
864
865void CellularCapabilityUniversal::OnDBusPropertiesChanged(
866 const string &interface,
867 const DBusPropertiesMap &changed_properties,
868 const vector<std::string> &invalidated_properties) {
Jason Glasgowef965562012-04-10 16:12:35 -0400869 if (interface == MM_DBUS_INTERFACE_MODEM) {
870 string value;
871 if (DBusProperties::GetString(changed_properties,
872 MM_MODEM_PROPERTY_SIM, &value))
873 OnSimPathChanged(value);
874 // TODO(jglasgow): handle additional properties on the modem interface
875 }
876 // TODO(jglasgow): handle additional interfaces
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400877}
878
879void CellularCapabilityUniversal::OnModem3GPPPropertiesChanged(
880 const DBusPropertiesMap &properties) {
881 bool emit = false;
882 uint32 locks = 0;
883 if (DBusProperties::GetUint32(
884 properties, MM_MODEM_MODEM3GPP_PROPERTY_ENABLEDFACILITYLOCKS,
885 &locks)) {
886 sim_lock_status_.enabled = locks & MM_MODEM_3GPP_FACILITY_SIM;
887 emit = true;
888 }
889 // TODO(jglasgow): coordinate with changes to Modem properties
890 if (emit) {
891 cellular()->adaptor()->EmitKeyValueStoreChanged(
892 flimflam::kSIMLockStatusProperty, SimLockStatusToProperty(NULL));
893 }
894}
895
896void CellularCapabilityUniversal::OnNetworkModeSignal(uint32 /*mode*/) {
897 // TODO(petkov): Implement this.
898 NOTIMPLEMENTED();
899}
900
901void CellularCapabilityUniversal::On3GPPRegistrationChanged(
902 MMModem3gppRegistrationState state,
903 const string &operator_code,
904 const string &operator_name) {
905 VLOG(2) << __func__ << ": regstate=" << state
906 << ", opercode=" << operator_code
907 << ", opername=" << operator_name;
908 registration_state_ = state;
909 serving_operator_.SetCode(operator_code);
910 serving_operator_.SetName(operator_name);
911 UpdateOperatorInfo();
912 cellular()->HandleNewRegistrationState();
913}
914
915void CellularCapabilityUniversal::OnSignalQualityChanged(uint32 quality) {
916 cellular()->HandleNewSignalQuality(quality);
917}
918
Jason Glasgowef965562012-04-10 16:12:35 -0400919void CellularCapabilityUniversal::OnSimPathChanged(
920 const string &sim_path) {
921 if (sim_path == sim_path_)
922 return;
923
924 mm1::SimProxyInterface *proxy = NULL;
925 if (!sim_path.empty())
926 proxy = proxy_factory()->CreateSimProxy(sim_path,
927 cellular()->dbus_owner());
928 sim_path_ = sim_path;
929 sim_proxy_.reset(proxy);
930}
931
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400932} // namespace shill