blob: e8485d8064b29ac5ca2ce8311fc6167957cc1827 [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) {
Nathan Williamsb54974f2012-04-19 11:16:30 -0400282 VLOG(2) << __func__;
283 DBusPathCallback cb = Bind(&CellularCapabilityUniversal::OnConnectReply,
284 weak_ptr_factory_.GetWeakPtr(),
285 callback);
286 modem_simple_proxy_->Connect(properties, error, cb, kTimeoutConnect);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400287}
288
289void CellularCapabilityUniversal::Disconnect(Error *error,
290 const ResultCallback &callback) {
Nathan Williamsb54974f2012-04-19 11:16:30 -0400291 VLOG(2) << __func__;
292 modem_simple_proxy_->Disconnect(bearer_path_,
293 error,
294 callback,
295 kTimeoutDefault);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400296}
297
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400298void CellularCapabilityUniversal::Activate(const string &carrier,
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400299 Error *error,
300 const ResultCallback &callback) {
301 OnUnsupportedOperation(__func__, error);
302}
303
304void CellularCapabilityUniversal::ReleaseProxies() {
305 VLOG(2) << __func__;
306 modem_3gpp_proxy_.reset();
307 modem_cdma_proxy_.reset();
308 modem_proxy_.reset();
309 modem_simple_proxy_.reset();
310 sim_proxy_.reset();
311}
312
313void CellularCapabilityUniversal::OnServiceCreated() {
314 // If IMSI is available, base the service's storage identifier on it.
315 if (!imsi_.empty()) {
316 cellular()->service()->SetStorageIdentifier(
317 string(flimflam::kTypeCellular) + "_" +
318 cellular()->address() + "_" + imsi_);
319 }
320 cellular()->service()->SetActivationState(
321 flimflam::kActivationStateActivated);
322 UpdateServingOperator();
323}
324
325void CellularCapabilityUniversal::UpdateStatus(
326 const DBusPropertiesMap &properties) {
327 if (ContainsKey(properties, kPropertyIMSI)) {
328 SetHomeProvider();
329 }
330}
331
332// Create the list of APNs to try, in the following order:
333// - last APN that resulted in a successful connection attempt on the
334// current network (if any)
335// - the APN, if any, that was set by the user
336// - the list of APNs found in the mobile broadband provider DB for the
337// home provider associated with the current SIM
338// - as a last resort, attempt to connect with no APN
339void CellularCapabilityUniversal::SetupApnTryList() {
340 apn_try_list_.clear();
341
342 DCHECK(cellular()->service().get());
343 const Stringmap *apn_info = cellular()->service()->GetLastGoodApn();
344 if (apn_info)
345 apn_try_list_.push_back(*apn_info);
346
347 apn_info = cellular()->service()->GetUserSpecifiedApn();
348 if (apn_info)
349 apn_try_list_.push_back(*apn_info);
350
351 apn_try_list_.insert(apn_try_list_.end(), apn_list_.begin(), apn_list_.end());
352}
353
354void CellularCapabilityUniversal::SetupConnectProperties(
355 DBusPropertiesMap *properties) {
356 SetupApnTryList();
357 FillConnectPropertyMap(properties);
358}
359
360void CellularCapabilityUniversal::FillConnectPropertyMap(
361 DBusPropertiesMap *properties) {
362
363 // TODO(jglasgow): Is this really needed anymore?
364 (*properties)[MM_MODEM_SIMPLE_CONNECT_NUMBER].writer().append_string(
365 kPhoneNumber);
366
367 (*properties)[MM_MODEM_SIMPLE_CONNECT_ALLOW_ROAMING].writer().append_bool(
368 AllowRoaming());
369
370 if (!apn_try_list_.empty()) {
371 // Leave the APN at the front of the list, so that it can be recorded
372 // if the connect attempt succeeds.
373 Stringmap apn_info = apn_try_list_.front();
374 VLOG(2) << __func__ << ": Using APN " << apn_info[flimflam::kApnProperty];
375 (*properties)[MM_MODEM_SIMPLE_CONNECT_APN].writer().append_string(
376 apn_info[flimflam::kApnProperty].c_str());
377 if (ContainsKey(apn_info, flimflam::kApnUsernameProperty))
378 (*properties)[MM_MODEM_SIMPLE_CONNECT_USER].writer().append_string(
379 apn_info[flimflam::kApnUsernameProperty].c_str());
380 if (ContainsKey(apn_info, flimflam::kApnPasswordProperty))
381 (*properties)[MM_MODEM_SIMPLE_CONNECT_PASSWORD].writer().append_string(
382 apn_info[flimflam::kApnPasswordProperty].c_str());
383 }
384}
385
386void CellularCapabilityUniversal::OnConnectReply(const ResultCallback &callback,
Nathan Williamsb54974f2012-04-19 11:16:30 -0400387 const DBus::Path &path,
388 const Error &error) {
389 VLOG(2) << __func__ << "(" << error << ")";
390
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400391 if (error.IsFailure()) {
392 cellular()->service()->ClearLastGoodApn();
393 // The APN that was just tried (and failed) is still at the
394 // front of the list, about to be removed. If the list is empty
395 // after that, try one last time without an APN. This may succeed
396 // with some modems in some cases.
397 if (error.type() == Error::kInvalidApn && !apn_try_list_.empty()) {
398 apn_try_list_.pop_front();
399 VLOG(2) << "Connect failed with invalid APN, " << apn_try_list_.size()
400 << " remaining APNs to try";
401 DBusPropertiesMap props;
402 FillConnectPropertyMap(&props);
403 Error error;
404 Connect(props, &error, callback);
405 return;
406 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400407 } else {
408 if (!apn_try_list_.empty()) {
409 cellular()->service()->SetLastGoodApn(apn_try_list_.front());
410 apn_try_list_.clear();
411 }
Nathan Williamsb54974f2012-04-19 11:16:30 -0400412 bearer_path_ = path;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400413 }
414
415 if (!callback.is_null())
416 callback.Run(error);
417}
418
419bool CellularCapabilityUniversal::AllowRoaming() {
420 bool requires_roaming =
421 home_provider_ ? home_provider_->requires_roaming : false;
422 return requires_roaming || allow_roaming_property();
423}
424
425void CellularCapabilityUniversal::GetRegistrationState() {
426 VLOG(2) << __func__;
427 string operator_code;
428 string operator_name;
429
430 const MMModem3gppRegistrationState state =
431 static_cast<MMModem3gppRegistrationState>(
432 modem_3gpp_proxy_->RegistrationState());
433 if (state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
434 state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) {
435 operator_code = modem_3gpp_proxy_->OperatorCode();
436 operator_name = modem_3gpp_proxy_->OperatorName();
437 }
438 On3GPPRegistrationChanged(state, operator_code, operator_name);
439}
440
Jason Glasgowef965562012-04-10 16:12:35 -0400441void CellularCapabilityUniversal::GetProperties() {
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400442 VLOG(2) << __func__;
443
444 // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
445 uint32 technologies = modem_proxy_->AccessTechnologies();
446 // TODO(jglasgow): figure out the most likely one that we are using....
447 SetAccessTechnologies(technologies);
448 VLOG(2) << "AccessTechnologies: " << technologies;
449
450 // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
451 uint32 locks = modem_3gpp_proxy_->EnabledFacilityLocks();
452 sim_lock_status_.enabled = locks & MM_MODEM_3GPP_FACILITY_SIM;
453 VLOG(2) << "GSM EnabledFacilityLocks: " << locks;
454
455 // TODO(jglasgow): Switch to asynchronous calls (crosbug.com/17583).
456 const DBus::Struct<unsigned int, bool> quality =
457 modem_proxy_->SignalQuality();
458 OnSignalQualityChanged(quality._1);
459
460 // TODO(jglasgow): Switch to asynchronous calls (crosbug.com/17583).
461 if (imei_.empty()) {
462 imei_ = modem_3gpp_proxy_->Imei();
463 }
Jason Glasgowef965562012-04-10 16:12:35 -0400464
465 string sim_path = modem_proxy_->Sim();
466 OnSimPathChanged(sim_path);
467
468 if (sim_proxy_.get()) {
469 if (imsi_.empty()) {
470 imsi_ = sim_proxy_->Imsi();
471 }
472 if (spn_.empty()) {
473 spn_ = sim_proxy_->OperatorName();
474 // TODO(jglasgow): May eventually want to get SPDI, etc
475 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400476 }
Nathan Williamsb0b66df2012-04-17 13:29:07 -0400477
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400478 if (mdn_.empty()) {
Nathan Williams4b7c2a82012-04-13 15:19:47 -0400479 // TODO(njw): Switch to asynchronous calls (crosbug.com/17583).
480 vector<string> numbers = modem_proxy_->OwnNumbers();
481 if (numbers.size() > 0)
482 mdn_ = numbers[0];
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400483 }
Nathan Williamsb0b66df2012-04-17 13:29:07 -0400484
485 if (model_id_.empty()) {
486 // TODO(njw): Switch to asynchronous calls (crosbug.com/17583).
487 model_id_ = modem_proxy_->Model();
488 }
489
490 if (manufacturer_.empty()) {
491 // TODO(njw): Switch to asynchronous calls (crosbug.com/17583).
492 manufacturer_ = modem_proxy_->Manufacturer();
493 }
494
495 if (firmware_revision_.empty()) {
496 // TODO(njw): Switch to asynchronous calls (crosbug.com/17583).
497 firmware_revision_ = modem_proxy_->Revision();
498 }
499
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400500 GetRegistrationState();
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400501}
502
503string CellularCapabilityUniversal::CreateFriendlyServiceName() {
504 VLOG(2) << __func__;
505 if (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME &&
506 !cellular()->home_provider().GetName().empty()) {
507 return cellular()->home_provider().GetName();
508 }
509 if (!serving_operator_.GetName().empty()) {
510 return serving_operator_.GetName();
511 }
512 if (!carrier_.empty()) {
513 return carrier_;
514 }
515 if (!serving_operator_.GetCode().empty()) {
516 return "cellular_" + serving_operator_.GetCode();
517 }
518 return base::StringPrintf("GSMNetwork%u", friendly_service_name_id_++);
519}
520
521void CellularCapabilityUniversal::SetHomeProvider() {
522 VLOG(2) << __func__ << "(IMSI: " << imsi_
523 << " SPN: " << spn_ << ")";
524 // TODO(petkov): The test for NULL provider_db should be done by
525 // mobile_provider_lookup_best_match.
526 if (imsi_.empty() || !cellular()->provider_db()) {
527 return;
528 }
529 mobile_provider *provider =
530 mobile_provider_lookup_best_match(
531 cellular()->provider_db(), spn_.c_str(), imsi_.c_str());
532 if (!provider) {
533 VLOG(2) << "GSM provider not found.";
534 return;
535 }
536 home_provider_ = provider;
537 Cellular::Operator oper;
538 if (provider->networks) {
539 oper.SetCode(provider->networks[0]);
540 }
541 if (provider->country) {
542 oper.SetCountry(provider->country);
543 }
544 if (spn_.empty()) {
545 const char *name = mobile_provider_get_name(provider);
546 if (name) {
547 oper.SetName(name);
548 }
549 } else {
550 oper.SetName(spn_);
551 }
552 cellular()->set_home_provider(oper);
553 InitAPNList();
554}
555
556void CellularCapabilityUniversal::UpdateOperatorInfo() {
557 VLOG(2) << __func__;
558 const string &network_id = serving_operator_.GetCode();
559 if (!network_id.empty()) {
560 VLOG(2) << "Looking up network id: " << network_id;
561 mobile_provider *provider =
562 mobile_provider_lookup_by_network(cellular()->provider_db(),
563 network_id.c_str());
564 if (provider) {
565 const char *provider_name = mobile_provider_get_name(provider);
566 if (provider_name && *provider_name) {
567 serving_operator_.SetName(provider_name);
568 if (provider->country) {
569 serving_operator_.SetCountry(provider->country);
570 }
571 VLOG(2) << "Operator name: " << serving_operator_.GetName()
572 << ", country: " << serving_operator_.GetCountry();
573 }
574 } else {
575 VLOG(2) << "GSM provider not found.";
576 }
577 }
578 UpdateServingOperator();
579}
580
581void CellularCapabilityUniversal::UpdateServingOperator() {
582 VLOG(2) << __func__;
583 if (cellular()->service().get()) {
584 cellular()->service()->SetServingOperator(serving_operator_);
585 }
586}
587
588void CellularCapabilityUniversal::InitAPNList() {
589 VLOG(2) << __func__;
590 if (!home_provider_) {
591 return;
592 }
593 apn_list_.clear();
594 for (int i = 0; i < home_provider_->num_apns; ++i) {
595 Stringmap props;
596 mobile_apn *apn = home_provider_->apns[i];
597 if (apn->value) {
598 props[flimflam::kApnProperty] = apn->value;
599 }
600 if (apn->username) {
601 props[flimflam::kApnUsernameProperty] = apn->username;
602 }
603 if (apn->password) {
604 props[flimflam::kApnPasswordProperty] = apn->password;
605 }
606 // Find the first localized and non-localized name, if any.
607 const localized_name *lname = NULL;
608 const localized_name *name = NULL;
609 for (int j = 0; j < apn->num_names; ++j) {
610 if (apn->names[j]->lang) {
611 if (!lname) {
612 lname = apn->names[j];
613 }
614 } else if (!name) {
615 name = apn->names[j];
616 }
617 }
618 if (name) {
619 props[flimflam::kApnNameProperty] = name->name;
620 }
621 if (lname) {
622 props[flimflam::kApnLocalizedNameProperty] = lname->name;
623 props[flimflam::kApnLanguageProperty] = lname->lang;
624 }
625 apn_list_.push_back(props);
626 }
627 cellular()->adaptor()->EmitStringmapsChanged(
628 flimflam::kCellularApnListProperty, apn_list_);
629}
630
631// always called from an async context
632void CellularCapabilityUniversal::Register(const ResultCallback &callback) {
633 VLOG(2) << __func__ << " \"" << selected_network_ << "\"";
634 CHECK(!callback.is_null());
635 Error error;
636 ResultCallback cb = Bind(&CellularCapabilityUniversal::OnRegisterReply,
637 weak_ptr_factory_.GetWeakPtr(), callback);
638 modem_3gpp_proxy_->Register(selected_network_, &error, cb, kTimeoutRegister);
639 if (error.IsFailure())
640 callback.Run(error);
641}
642
643void CellularCapabilityUniversal::RegisterOnNetwork(
644 const string &network_id,
645 Error *error,
646 const ResultCallback &callback) {
647 VLOG(2) << __func__ << "(" << network_id << ")";
648 CHECK(error);
649 desired_network_ = network_id;
650 ResultCallback cb = Bind(&CellularCapabilityUniversal::OnRegisterReply,
651 weak_ptr_factory_.GetWeakPtr(), callback);
652 modem_3gpp_proxy_->Register(network_id, error, cb, kTimeoutRegister);
653}
654
655void CellularCapabilityUniversal::OnRegisterReply(
656 const ResultCallback &callback,
657 const Error &error) {
658 VLOG(2) << __func__ << "(" << error << ")";
659
660 if (error.IsSuccess()) {
661 selected_network_ = desired_network_;
662 desired_network_.clear();
663 callback.Run(error);
664 return;
665 }
666 // If registration on the desired network failed,
667 // try to register on the home network.
668 if (!desired_network_.empty()) {
669 desired_network_.clear();
670 selected_network_.clear();
671 LOG(INFO) << "Couldn't register on selected network, trying home network";
672 Register(callback);
673 return;
674 }
675 callback.Run(error);
676}
677
678bool CellularCapabilityUniversal::IsRegistered() {
679 return (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
680 registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING);
681}
682
683void CellularCapabilityUniversal::RequirePIN(
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400684 const string &pin, bool require,
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400685 Error *error, const ResultCallback &callback) {
686 CHECK(error);
687 sim_proxy_->EnablePin(pin, require, error, callback, kTimeoutDefault);
688}
689
690void CellularCapabilityUniversal::EnterPIN(const string &pin,
691 Error *error,
692 const ResultCallback &callback) {
693 CHECK(error);
694 sim_proxy_->SendPin(pin, error, callback, kTimeoutDefault);
695}
696
697void CellularCapabilityUniversal::UnblockPIN(const string &unblock_code,
698 const string &pin,
699 Error *error,
700 const ResultCallback &callback) {
701 CHECK(error);
702 sim_proxy_->SendPuk(unblock_code, pin, error, callback, kTimeoutDefault);
703}
704
705void CellularCapabilityUniversal::ChangePIN(
706 const string &old_pin, const string &new_pin,
707 Error *error, const ResultCallback &callback) {
708 CHECK(error);
709 sim_proxy_->ChangePin(old_pin, new_pin, error, callback, kTimeoutDefault);
710}
711
712void CellularCapabilityUniversal::Scan(Error *error,
713 const ResultCallback &callback) {
714 VLOG(2) << __func__;
715 // TODO(petkov): Defer scan requests if a scan is in progress already.
716 CHECK(error);
717 DBusPropertyMapsCallback cb = Bind(&CellularCapabilityUniversal::OnScanReply,
718 weak_ptr_factory_.GetWeakPtr(), callback);
719 modem_3gpp_proxy_->Scan(error, cb, kTimeoutScan);
720}
721
722void CellularCapabilityUniversal::OnScanReply(const ResultCallback &callback,
723 const ScanResults &results,
724 const Error &error) {
725 VLOG(2) << __func__;
726
727 // Error handling is weak. The current expectation is that on any
728 // error, found_networks_ should be cleared and a property change
729 // notification sent out.
730 //
731 // TODO(jglasgow): fix error handling
732 found_networks_.clear();
733 if (!error.IsFailure()) {
734 for (ScanResults::const_iterator it = results.begin();
735 it != results.end(); ++it) {
736 found_networks_.push_back(ParseScanResult(*it));
737 }
738 }
739 cellular()->adaptor()->EmitStringmapsChanged(flimflam::kFoundNetworksProperty,
740 found_networks_);
741 callback.Run(error);
742}
743
744Stringmap CellularCapabilityUniversal::ParseScanResult(
745 const ScanResult &result) {
746
747 static const char kStatusProperty[] = "status";
748 static const char kOperatorLongProperty[] = "operator-long";
749 static const char kOperatorShortProperty[] = "operator-short";
750 static const char kOperatorCodeProperty[] = "operator-code";
751 static const char kOperatorAccessTechnologyProperty[] = "access-technology";
752
753 /* ScanResults contain the following keys:
754
755 "status"
756 A MMModem3gppNetworkAvailability value representing network
757 availability status, given as an unsigned integer (signature "u").
758 This key will always be present.
759
760 "operator-long"
761 Long-format name of operator, given as a string value (signature
762 "s"). If the name is unknown, this field should not be present.
763
764 "operator-short"
765 Short-format name of operator, given as a string value
766 (signature "s"). If the name is unknown, this field should not
767 be present.
768
769 "operator-code"
770 Mobile code of the operator, given as a string value (signature
771 "s"). Returned in the format "MCCMNC", where MCC is the
772 three-digit ITU E.212 Mobile Country Code and MNC is the two- or
773 three-digit GSM Mobile Network Code. e.g. "31026" or "310260".
774
775 "access-technology"
776 A MMModemAccessTechnology value representing the generic access
777 technology used by this mobile network, given as an unsigned
778 integer (signature "u").
779 */
780 Stringmap parsed;
781
782 uint32 status;
783 if (DBusProperties::GetUint32(result, kStatusProperty, &status)) {
784 // numerical values are taken from 3GPP TS 27.007 Section 7.3.
785 static const char * const kStatusString[] = {
786 "unknown", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN
787 "available", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE
788 "current", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT
789 "forbidden", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN
790 };
791 parsed[flimflam::kStatusProperty] = kStatusString[status];
792 }
793
794 uint32 tech; // MMModemAccessTechnology
795 if (DBusProperties::GetUint32(result, kOperatorAccessTechnologyProperty,
796 &tech)) {
797 parsed[flimflam::kTechnologyProperty] = AccessTechnologyToString(tech);
798 }
799
800 string operator_long, operator_short, operator_code;
801 if (DBusProperties::GetString(result, kOperatorLongProperty, &operator_long))
802 parsed[flimflam::kLongNameProperty] = operator_long;
803 if (DBusProperties::GetString(result, kOperatorShortProperty,
804 &operator_short))
805 parsed[flimflam::kShortNameProperty] = operator_short;
806 if (DBusProperties::GetString(result, kOperatorCodeProperty, &operator_code))
807 parsed[flimflam::kNetworkIdProperty] = operator_code;
808
809 // If the long name is not available but the network ID is, look up the long
810 // name in the mobile provider database.
811 if ((!ContainsKey(parsed, flimflam::kLongNameProperty) ||
812 parsed[flimflam::kLongNameProperty].empty()) &&
813 ContainsKey(parsed, flimflam::kNetworkIdProperty)) {
814 mobile_provider *provider =
815 mobile_provider_lookup_by_network(
816 cellular()->provider_db(),
817 parsed[flimflam::kNetworkIdProperty].c_str());
818 if (provider) {
819 const char *long_name = mobile_provider_get_name(provider);
820 if (long_name && *long_name) {
821 parsed[flimflam::kLongNameProperty] = long_name;
822 }
823 }
824 }
825 return parsed;
826}
827
828void CellularCapabilityUniversal::SetAccessTechnologies(
829 uint32 access_technologies) {
830 access_technologies_ = access_technologies;
831 if (cellular()->service().get()) {
832 cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
833 }
834}
835
836string CellularCapabilityUniversal::GetNetworkTechnologyString() const {
837 // Order is imnportant. Return the highest speed technology
838 // TODO(jglasgow): change shill interfaces to a capability model
839
840 return AccessTechnologyToString(access_technologies_);
841}
842
843string CellularCapabilityUniversal::GetRoamingStateString() const {
844 switch (registration_state_) {
845 case MM_MODEM_3GPP_REGISTRATION_STATE_HOME:
846 return flimflam::kRoamingStateHome;
847 case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING:
848 return flimflam::kRoamingStateRoaming;
849 default:
850 break;
851 }
852 return flimflam::kRoamingStateUnknown;
853}
854
855void CellularCapabilityUniversal::GetSignalQuality() {
Nathan Williams218cbcd2012-04-17 16:48:44 -0400856 // TODO(njw): Switch to asynchronous calls (crosbug.com/17583).
857 const DBus::Struct<unsigned int, bool> quality =
858 modem_proxy_->SignalQuality();
859 OnSignalQualityChanged(quality._1);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400860}
861
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400862void CellularCapabilityUniversal::OnModemPropertiesChanged(
863 const DBusPropertiesMap &properties,
864 const vector<string> &/* invalidated_properties */) {
865 string value;
Nathan Williamse9840802012-04-18 18:47:40 -0400866 if (DBusProperties::GetObjectPath(properties,
867 MM_MODEM_PROPERTY_SIM, &value))
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400868 OnSimPathChanged(value);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400869
870 uint32 access_technologies = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
871 if (DBusProperties::GetUint32(properties,
872 MM_MODEM_PROPERTY_ACCESSTECHNOLOGIES,
873 &access_technologies)) {
874 SetAccessTechnologies(access_technologies);
875 }
Nathan Williams218cbcd2012-04-17 16:48:44 -0400876
877 DBusPropertiesMap::const_iterator it =
878 properties.find(MM_MODEM_PROPERTY_SIGNALQUALITY);
879 if (it != properties.end()) {
880 DBus::Struct<unsigned int, bool> quality =
881 static_cast<DBus::Variant>(it->second);
882 OnSignalQualityChanged(quality._1);
883 }
884
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400885 // Unlockrequired and SimLock
886 bool emit = false;
887
888 uint32_t lock_required; // This is really of type MMModemLock
889 if (DBusProperties::GetUint32(properties,
890 MM_MODEM_PROPERTY_UNLOCKREQUIRED,
891 &lock_required)) {
892 // TODO(jglasgow): set sim_lock_status_.lock_type
893 emit = true;
894 }
895 // TODO(jglasgow): Update PIN retries which are a{uu} and require parsing
896 // Get the property MM_MODEM_PROPERTY_UNLOCKRETRIES
897 // Set sim_lock_status_.retries_left
898
899 if (emit) {
900 cellular()->adaptor()->EmitKeyValueStoreChanged(
901 flimflam::kSIMLockStatusProperty, SimLockStatusToProperty(NULL));
902 }
903}
904
905void CellularCapabilityUniversal::OnDBusPropertiesChanged(
906 const string &interface,
907 const DBusPropertiesMap &changed_properties,
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400908 const vector<string> &invalidated_properties) {
Jason Glasgowef965562012-04-10 16:12:35 -0400909 if (interface == MM_DBUS_INTERFACE_MODEM) {
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400910 OnModemPropertiesChanged(changed_properties, invalidated_properties);
Jason Glasgowef965562012-04-10 16:12:35 -0400911 }
912 // TODO(jglasgow): handle additional interfaces
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400913}
914
915void CellularCapabilityUniversal::OnModem3GPPPropertiesChanged(
916 const DBusPropertiesMap &properties) {
917 bool emit = false;
918 uint32 locks = 0;
919 if (DBusProperties::GetUint32(
920 properties, MM_MODEM_MODEM3GPP_PROPERTY_ENABLEDFACILITYLOCKS,
921 &locks)) {
922 sim_lock_status_.enabled = locks & MM_MODEM_3GPP_FACILITY_SIM;
923 emit = true;
924 }
925 // TODO(jglasgow): coordinate with changes to Modem properties
926 if (emit) {
927 cellular()->adaptor()->EmitKeyValueStoreChanged(
928 flimflam::kSIMLockStatusProperty, SimLockStatusToProperty(NULL));
929 }
930}
931
932void CellularCapabilityUniversal::OnNetworkModeSignal(uint32 /*mode*/) {
933 // TODO(petkov): Implement this.
934 NOTIMPLEMENTED();
935}
936
937void CellularCapabilityUniversal::On3GPPRegistrationChanged(
938 MMModem3gppRegistrationState state,
939 const string &operator_code,
940 const string &operator_name) {
941 VLOG(2) << __func__ << ": regstate=" << state
942 << ", opercode=" << operator_code
943 << ", opername=" << operator_name;
944 registration_state_ = state;
945 serving_operator_.SetCode(operator_code);
946 serving_operator_.SetName(operator_name);
947 UpdateOperatorInfo();
948 cellular()->HandleNewRegistrationState();
949}
950
951void CellularCapabilityUniversal::OnSignalQualityChanged(uint32 quality) {
952 cellular()->HandleNewSignalQuality(quality);
953}
954
Jason Glasgowef965562012-04-10 16:12:35 -0400955void CellularCapabilityUniversal::OnSimPathChanged(
956 const string &sim_path) {
957 if (sim_path == sim_path_)
958 return;
959
960 mm1::SimProxyInterface *proxy = NULL;
961 if (!sim_path.empty())
962 proxy = proxy_factory()->CreateSimProxy(sim_path,
963 cellular()->dbus_owner());
964 sim_path_ = sim_path;
965 sim_proxy_.reset(proxy);
966}
967
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400968} // namespace shill