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