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