blob: afc95a5a6ca7ef98aa9b0401aef1804e784eba19 [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>
Jason Glasgow82f9ab32012-04-04 14:27:19 -04008#include <base/stl_util.h>
Jason Glasgow82f9ab32012-04-04 14:27:19 -04009#include <base/stringprintf.h>
10#include <chromeos/dbus/service_constants.h>
11#include <mobile_provider.h>
Ben Chanf6120e92012-06-28 18:56:17 -070012#include <ModemManager/ModemManager-names.h>
Jason Glasgow82f9ab32012-04-04 14:27:19 -040013
14#include <string>
15#include <vector>
16
17#include "shill/adaptor_interfaces.h"
18#include "shill/cellular_service.h"
Jason Glasgowaf583282012-04-18 15:18:22 -040019#include "shill/dbus_properties_proxy_interface.h"
Jason Glasgow82f9ab32012-04-04 14:27:19 -040020#include "shill/error.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070021#include "shill/logging.h"
Jason Glasgow82f9ab32012-04-04 14:27:19 -040022#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
Jason Glasgow82f9ab32012-04-04 14:27:19 -040029using base::Bind;
Jason Glasgowef965562012-04-10 16:12:35 -040030using base::Closure;
Jason Glasgow82f9ab32012-04-04 14:27:19 -040031using std::string;
32using std::vector;
33
34namespace shill {
35
36// static
Jason Glasgow14521872012-05-07 19:12:15 -040037const char CellularCapabilityUniversal::kConnectPin[] = "pin";
38const char CellularCapabilityUniversal::kConnectOperatorId[] = "operator-id";
39const char CellularCapabilityUniversal::kConnectBands[] = "bands";
40const char CellularCapabilityUniversal::kConnectAllowedModes[] =
41 "allowed-modes";
42const char CellularCapabilityUniversal::kConnectPreferredMode[] =
43 "preferred-mode";
44const char CellularCapabilityUniversal::kConnectApn[] = "apn";
45const char CellularCapabilityUniversal::kConnectIPType[] = "ip-type";
46const char CellularCapabilityUniversal::kConnectUser[] = "user";
47const char CellularCapabilityUniversal::kConnectPassword[] = "password";
48const char CellularCapabilityUniversal::kConnectNumber[] = "number";
49const char CellularCapabilityUniversal::kConnectAllowRoaming[] =
50 "allow-roaming";
51const char CellularCapabilityUniversal::kConnectRMProtocol[] = "rm-protocol";
Jason Glasgowcd0349c2012-05-03 23:32:15 -040052const char CellularCapabilityUniversal::kStatusProperty[] = "status";
53const char CellularCapabilityUniversal::kOperatorLongProperty[] =
54 "operator-long";
55const char CellularCapabilityUniversal::kOperatorShortProperty[] =
56 "operator-short";
57const char CellularCapabilityUniversal::kOperatorCodeProperty[] =
58 "operator-code";
59const char CellularCapabilityUniversal::kOperatorAccessTechnologyProperty[] =
60 "access-technology";
Jason Glasgow14521872012-05-07 19:12:15 -040061const char CellularCapabilityUniversal::kE362ModelId[] = "E362 WWAN";
62unsigned int CellularCapabilityUniversal::friendly_service_name_id_ = 0;
63
Jason Glasgow82f9ab32012-04-04 14:27:19 -040064
65static const char kPhoneNumber[] = "*99#";
66
67static string AccessTechnologyToString(uint32 access_technologies) {
68 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_LTE)
69 return flimflam::kNetworkTechnologyLte;
70 if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 |
71 MM_MODEM_ACCESS_TECHNOLOGY_EVDOA |
72 MM_MODEM_ACCESS_TECHNOLOGY_EVDOB))
73 return flimflam::kNetworkTechnologyEvdo;
74 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_1XRTT)
75 return flimflam::kNetworkTechnology1Xrtt;
76 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS)
77 return flimflam::kNetworkTechnologyHspaPlus;
78 if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_HSPA |
79 MM_MODEM_ACCESS_TECHNOLOGY_HSUPA |
80 MM_MODEM_ACCESS_TECHNOLOGY_HSDPA))
81 return flimflam::kNetworkTechnologyHspa;
82 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_UMTS)
83 return flimflam::kNetworkTechnologyUmts;
84 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_EDGE)
85 return flimflam::kNetworkTechnologyEdge;
86 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_GPRS)
87 return flimflam::kNetworkTechnologyGprs;
88 if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT |
89 MM_MODEM_ACCESS_TECHNOLOGY_GSM))
90 return flimflam::kNetworkTechnologyGsm;
91 return "";
92}
93
Jason Glasgow9f09aef2012-05-08 16:26:55 -040094static string AccessTechnologyToTechnologyFamily(uint32 access_technologies) {
95 if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_LTE |
96 MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS |
97 MM_MODEM_ACCESS_TECHNOLOGY_HSPA |
98 MM_MODEM_ACCESS_TECHNOLOGY_HSUPA |
99 MM_MODEM_ACCESS_TECHNOLOGY_HSDPA |
100 MM_MODEM_ACCESS_TECHNOLOGY_UMTS |
101 MM_MODEM_ACCESS_TECHNOLOGY_EDGE |
102 MM_MODEM_ACCESS_TECHNOLOGY_GPRS |
103 MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT |
104 MM_MODEM_ACCESS_TECHNOLOGY_GSM))
105 return flimflam::kTechnologyFamilyGsm;
106 if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 |
107 MM_MODEM_ACCESS_TECHNOLOGY_EVDOA |
108 MM_MODEM_ACCESS_TECHNOLOGY_EVDOB |
109 MM_MODEM_ACCESS_TECHNOLOGY_1XRTT))
110 return flimflam::kTechnologyFamilyCdma;
111 return "";
112}
113
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400114CellularCapabilityUniversal::CellularCapabilityUniversal(
115 Cellular *cellular,
116 ProxyFactory *proxy_factory)
117 : CellularCapability(cellular, proxy_factory),
118 weak_ptr_factory_(this),
119 registration_state_(MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN),
120 cdma_registration_state_(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN),
Darin Petkove636c692012-05-31 10:22:17 +0200121 capabilities_(MM_MODEM_CAPABILITY_NONE),
122 current_capabilities_(MM_MODEM_CAPABILITY_NONE),
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400123 access_technologies_(MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN),
Jason Glasgowaf583282012-04-18 15:18:22 -0400124 supported_modes_(MM_MODEM_MODE_NONE),
125 allowed_modes_(MM_MODEM_MODE_NONE),
126 preferred_mode_(MM_MODEM_MODE_NONE),
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400127 home_provider_(NULL),
128 scanning_supported_(true),
129 scanning_(false),
130 scan_interval_(0) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700131 SLOG(Cellular, 2) << "Cellular capability constructed: Universal";
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400132 PropertyStore *store = cellular->mutable_store();
133
134 store->RegisterConstString(flimflam::kCarrierProperty, &carrier_);
135 store->RegisterConstBool(flimflam::kSupportNetworkScanProperty,
136 &scanning_supported_);
137 store->RegisterConstString(flimflam::kEsnProperty, &esn_);
138 store->RegisterConstString(flimflam::kFirmwareRevisionProperty,
139 &firmware_revision_);
140 store->RegisterConstString(flimflam::kHardwareRevisionProperty,
141 &hardware_revision_);
142 store->RegisterConstString(flimflam::kImeiProperty, &imei_);
143 store->RegisterConstString(flimflam::kImsiProperty, &imsi_);
Ben Chana5e27082012-07-31 14:30:28 -0700144 store->RegisterConstString(flimflam::kIccidProperty, &sim_identifier_);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400145 store->RegisterConstString(flimflam::kManufacturerProperty, &manufacturer_);
146 store->RegisterConstString(flimflam::kMdnProperty, &mdn_);
147 store->RegisterConstString(flimflam::kMeidProperty, &meid_);
148 store->RegisterConstString(flimflam::kMinProperty, &min_);
149 store->RegisterConstString(flimflam::kModelIDProperty, &model_id_);
150 store->RegisterConstString(flimflam::kSelectedNetworkProperty,
151 &selected_network_);
152 store->RegisterConstStringmaps(flimflam::kFoundNetworksProperty,
153 &found_networks_);
154 store->RegisterConstBool(flimflam::kScanningProperty, &scanning_);
155 store->RegisterUint16(flimflam::kScanIntervalProperty, &scan_interval_);
156 HelpRegisterDerivedKeyValueStore(
157 flimflam::kSIMLockStatusProperty,
158 &CellularCapabilityUniversal::SimLockStatusToProperty,
159 NULL);
160 store->RegisterConstStringmaps(flimflam::kCellularApnListProperty,
161 &apn_list_);
162}
163
164KeyValueStore CellularCapabilityUniversal::SimLockStatusToProperty(
165 Error */*error*/) {
166 KeyValueStore status;
167 status.SetBool(flimflam::kSIMLockEnabledProperty, sim_lock_status_.enabled);
168 status.SetString(flimflam::kSIMLockTypeProperty, sim_lock_status_.lock_type);
169 status.SetUint(flimflam::kSIMLockRetriesLeftProperty,
170 sim_lock_status_.retries_left);
171 return status;
172}
173
174void CellularCapabilityUniversal::HelpRegisterDerivedKeyValueStore(
175 const string &name,
176 KeyValueStore(CellularCapabilityUniversal::*get)(Error *error),
177 void(CellularCapabilityUniversal::*set)(
178 const KeyValueStore &value, Error *error)) {
179 cellular()->mutable_store()->RegisterDerivedKeyValueStore(
180 name,
181 KeyValueStoreAccessor(
182 new CustomAccessor<CellularCapabilityUniversal, KeyValueStore>(
183 this, get, set)));
184}
185
186void CellularCapabilityUniversal::InitProxies() {
187 modem_3gpp_proxy_.reset(
188 proxy_factory()->CreateMM1ModemModem3gppProxy(cellular()->dbus_path(),
189 cellular()->dbus_owner()));
190 modem_cdma_proxy_.reset(
191 proxy_factory()->CreateMM1ModemModemCdmaProxy(cellular()->dbus_path(),
192 cellular()->dbus_owner()));
193 modem_proxy_.reset(
194 proxy_factory()->CreateMM1ModemProxy(cellular()->dbus_path(),
195 cellular()->dbus_owner()));
196 modem_simple_proxy_.reset(
197 proxy_factory()->CreateMM1ModemSimpleProxy(cellular()->dbus_path(),
198 cellular()->dbus_owner()));
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400199 modem_proxy_->set_state_changed_callback(
200 Bind(&CellularCapabilityUniversal::OnModemStateChangedSignal,
201 weak_ptr_factory_.GetWeakPtr()));
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400202 // Do not create a SIM proxy until the device is enabled because we
203 // do not yet know the object path of the sim object.
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400204 // TODO(jglasgow): register callbacks
205}
206
207void CellularCapabilityUniversal::StartModem(Error *error,
Jason Glasgowef965562012-04-10 16:12:35 -0400208 const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700209 SLOG(Cellular, 2) << __func__;
Jason Glasgowef965562012-04-10 16:12:35 -0400210 InitProxies();
211
Gary Moraine285a842012-08-15 08:23:57 -0700212 // ModemManager must be in the disabled state to accept the Enable command.
213 Cellular::ModemState state =
214 static_cast<Cellular::ModemState>(modem_proxy_->State());
215 if (state == Cellular::kModemStateDisabled) {
216 EnableModem(error, callback);
217 } else if (!cellular()->IsUnderlyingDeviceEnabled()) {
218 SLOG(Cellular, 2) << "Enabling of modem deferred because state is "
219 << state;
220 deferred_enable_modem_callback_ =
221 Bind(&CellularCapabilityUniversal::EnableModem,
222 weak_ptr_factory_.GetWeakPtr(), static_cast<Error *>(NULL),
223 callback);
224 } else {
225 callback.Run(*error);
226 }
227}
228
229void CellularCapabilityUniversal::EnableModem(Error *error,
230 const ResultCallback &callback) {
231 SLOG(Cellular, 2) << __func__;
Jason Glasgowef965562012-04-10 16:12:35 -0400232 CHECK(!callback.is_null());
Thieu Lee3b36592012-08-30 17:50:26 -0700233 Error local_error(Error::kOperationInitiated);
Jason Glasgowef965562012-04-10 16:12:35 -0400234 modem_proxy_->Enable(
235 true,
Gary Moraine285a842012-08-15 08:23:57 -0700236 &local_error,
Jason Glasgowef965562012-04-10 16:12:35 -0400237 Bind(&CellularCapabilityUniversal::Start_EnableModemCompleted,
238 weak_ptr_factory_.GetWeakPtr(), callback),
239 kTimeoutEnable);
Gary Moraine285a842012-08-15 08:23:57 -0700240 if (local_error.IsFailure()) {
241 SLOG(Cellular, 2) << __func__ << "Call to modem_proxy_->Enable() failed";
242 callback.Run(local_error);
243 }
244 if (error) {
245 error->CopyFrom(local_error);
246 }
Jason Glasgowef965562012-04-10 16:12:35 -0400247}
248
249void CellularCapabilityUniversal::Start_EnableModemCompleted(
250 const ResultCallback &callback, const Error &error) {
Eric Shienbrood9b1bd7b2012-04-20 16:42:26 -0400251 SLOG(Cellular, 2) << __func__ << ": " << error;
Jason Glasgowef965562012-04-10 16:12:35 -0400252 if (error.IsFailure()) {
253 callback.Run(error);
254 return;
255 }
256
257 // After modem is enabled, it should be possible to get properties
258 // TODO(jglasgow): handle errors from GetProperties
259 GetProperties();
Jason Glasgow4380f0d2012-05-03 18:05:04 -0400260 callback.Run(error);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400261}
262
263void CellularCapabilityUniversal::StopModem(Error *error,
Jason Glasgow02401cc2012-05-16 10:35:37 -0400264 const ResultCallback &callback) {
Jason Glasgowef965562012-04-10 16:12:35 -0400265 CHECK(!callback.is_null());
266 CHECK(error);
Thieu Led0012052012-07-25 16:09:09 -0700267 Cellular::ModemState state = cellular()->modem_state();
268 SLOG(Cellular, 2) << __func__ << "(" << state << ")";
Jason Glasgowef965562012-04-10 16:12:35 -0400269
Thieu Led0012052012-07-25 16:09:09 -0700270 if (cellular()->IsModemRegistered()) {
Jason Glasgow02401cc2012-05-16 10:35:37 -0400271 string all_bearers("/"); // "/" means all bearers. See Modemanager docs.
Jason Glasgowef965562012-04-10 16:12:35 -0400272 modem_simple_proxy_->Disconnect(
273 all_bearers,
274 error,
275 Bind(&CellularCapabilityUniversal::Stop_DisconnectCompleted,
276 weak_ptr_factory_.GetWeakPtr(), callback),
277 kTimeoutDefault);
278 if (error->IsFailure())
279 callback.Run(*error);
280 } else {
Jason Glasgowef965562012-04-10 16:12:35 -0400281 Closure task = Bind(&CellularCapabilityUniversal::Stop_Disable,
282 weak_ptr_factory_.GetWeakPtr(),
283 callback);
284 cellular()->dispatcher()->PostTask(task);
285 }
Gary Moraine285a842012-08-15 08:23:57 -0700286 deferred_enable_modem_callback_.Reset();
Jason Glasgowef965562012-04-10 16:12:35 -0400287}
288
289void CellularCapabilityUniversal::Stop_DisconnectCompleted(
290 const ResultCallback &callback, const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700291 SLOG(Cellular, 2) << __func__;
Jason Glasgowef965562012-04-10 16:12:35 -0400292
293 LOG_IF(ERROR, error.IsFailure()) << "Disconnect failed. Ignoring.";
294 Stop_Disable(callback);
295}
296
297void CellularCapabilityUniversal::Stop_Disable(const ResultCallback &callback) {
298 Error error;
299 modem_proxy_->Enable(
300 false, &error,
301 Bind(&CellularCapabilityUniversal::Stop_DisableCompleted,
302 weak_ptr_factory_.GetWeakPtr(), callback),
Jason Glasgow02401cc2012-05-16 10:35:37 -0400303 kTimeoutEnable);
Jason Glasgowef965562012-04-10 16:12:35 -0400304 if (error.IsFailure())
305 callback.Run(error);
306}
307
308void CellularCapabilityUniversal::Stop_DisableCompleted(
309 const ResultCallback &callback, const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700310 SLOG(Cellular, 2) << __func__;
Jason Glasgowef965562012-04-10 16:12:35 -0400311
312 if (error.IsSuccess())
313 ReleaseProxies();
314 callback.Run(error);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400315}
316
317void CellularCapabilityUniversal::Connect(const DBusPropertiesMap &properties,
318 Error *error,
319 const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700320 SLOG(Cellular, 2) << __func__;
Nathan Williamsb54974f2012-04-19 11:16:30 -0400321 DBusPathCallback cb = Bind(&CellularCapabilityUniversal::OnConnectReply,
322 weak_ptr_factory_.GetWeakPtr(),
323 callback);
324 modem_simple_proxy_->Connect(properties, error, cb, kTimeoutConnect);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400325}
326
327void CellularCapabilityUniversal::Disconnect(Error *error,
328 const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700329 SLOG(Cellular, 2) << __func__;
Thieu Le5d6864a2012-07-20 11:43:51 -0700330 if (bearer_path_.empty()) {
331 LOG(WARNING) << "In " << __func__ << "(): "
332 << "Ignoring attempt to disconnect without bearer";
Thieu Le3d275392012-07-20 15:32:58 -0700333 } else if (modem_simple_proxy_.get()) {
Thieu Le5d6864a2012-07-20 11:43:51 -0700334 modem_simple_proxy_->Disconnect(bearer_path_,
335 error,
336 callback,
337 kTimeoutDefault);
338 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400339}
340
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400341void CellularCapabilityUniversal::Activate(const string &carrier,
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400342 Error *error,
343 const ResultCallback &callback) {
344 OnUnsupportedOperation(__func__, error);
345}
346
347void CellularCapabilityUniversal::ReleaseProxies() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700348 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400349 modem_3gpp_proxy_.reset();
350 modem_cdma_proxy_.reset();
351 modem_proxy_.reset();
352 modem_simple_proxy_.reset();
353 sim_proxy_.reset();
354}
355
356void CellularCapabilityUniversal::OnServiceCreated() {
357 // If IMSI is available, base the service's storage identifier on it.
358 if (!imsi_.empty()) {
359 cellular()->service()->SetStorageIdentifier(
360 string(flimflam::kTypeCellular) + "_" +
361 cellular()->address() + "_" + imsi_);
362 }
363 cellular()->service()->SetActivationState(
364 flimflam::kActivationStateActivated);
365 UpdateServingOperator();
366}
367
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400368// Create the list of APNs to try, in the following order:
369// - last APN that resulted in a successful connection attempt on the
370// current network (if any)
371// - the APN, if any, that was set by the user
372// - the list of APNs found in the mobile broadband provider DB for the
373// home provider associated with the current SIM
374// - as a last resort, attempt to connect with no APN
375void CellularCapabilityUniversal::SetupApnTryList() {
376 apn_try_list_.clear();
377
378 DCHECK(cellular()->service().get());
379 const Stringmap *apn_info = cellular()->service()->GetLastGoodApn();
380 if (apn_info)
381 apn_try_list_.push_back(*apn_info);
382
383 apn_info = cellular()->service()->GetUserSpecifiedApn();
384 if (apn_info)
385 apn_try_list_.push_back(*apn_info);
386
387 apn_try_list_.insert(apn_try_list_.end(), apn_list_.begin(), apn_list_.end());
388}
389
390void CellularCapabilityUniversal::SetupConnectProperties(
391 DBusPropertiesMap *properties) {
392 SetupApnTryList();
393 FillConnectPropertyMap(properties);
394}
395
396void CellularCapabilityUniversal::FillConnectPropertyMap(
397 DBusPropertiesMap *properties) {
398
399 // TODO(jglasgow): Is this really needed anymore?
Jason Glasgow14521872012-05-07 19:12:15 -0400400 (*properties)[kConnectNumber].writer().append_string(
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400401 kPhoneNumber);
402
Jason Glasgow14521872012-05-07 19:12:15 -0400403 (*properties)[kConnectAllowRoaming].writer().append_bool(
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400404 AllowRoaming());
405
406 if (!apn_try_list_.empty()) {
407 // Leave the APN at the front of the list, so that it can be recorded
408 // if the connect attempt succeeds.
409 Stringmap apn_info = apn_try_list_.front();
Ben Chanfad4a0b2012-04-18 15:49:59 -0700410 SLOG(Cellular, 2) << __func__ << ": Using APN "
411 << apn_info[flimflam::kApnProperty];
Jason Glasgow14521872012-05-07 19:12:15 -0400412 (*properties)[kConnectApn].writer().append_string(
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400413 apn_info[flimflam::kApnProperty].c_str());
414 if (ContainsKey(apn_info, flimflam::kApnUsernameProperty))
Jason Glasgow14521872012-05-07 19:12:15 -0400415 (*properties)[kConnectUser].writer().append_string(
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400416 apn_info[flimflam::kApnUsernameProperty].c_str());
417 if (ContainsKey(apn_info, flimflam::kApnPasswordProperty))
Jason Glasgow14521872012-05-07 19:12:15 -0400418 (*properties)[kConnectPassword].writer().append_string(
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400419 apn_info[flimflam::kApnPasswordProperty].c_str());
420 }
421}
422
423void CellularCapabilityUniversal::OnConnectReply(const ResultCallback &callback,
Nathan Williamsb54974f2012-04-19 11:16:30 -0400424 const DBus::Path &path,
425 const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700426 SLOG(Cellular, 2) << __func__ << "(" << error << ")";
Nathan Williamsb54974f2012-04-19 11:16:30 -0400427
Jason Glasgow7234ec32012-05-23 16:01:21 -0400428 CellularServiceRefPtr service = cellular()->service();
429 if (!service) {
430 // The service could have been deleted before our Connect() request
431 // completes if the modem was enabled and then quickly disabled.
432 apn_try_list_.clear();
433 } else if (error.IsFailure()) {
434 service->ClearLastGoodApn();
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400435 // The APN that was just tried (and failed) is still at the
436 // front of the list, about to be removed. If the list is empty
437 // after that, try one last time without an APN. This may succeed
438 // with some modems in some cases.
Jason Glasgow14521872012-05-07 19:12:15 -0400439 if (RetriableConnectError(error) && !apn_try_list_.empty()) {
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400440 apn_try_list_.pop_front();
Ben Chanfad4a0b2012-04-18 15:49:59 -0700441 SLOG(Cellular, 2) << "Connect failed with invalid APN, "
442 << apn_try_list_.size() << " remaining APNs to try";
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400443 DBusPropertiesMap props;
444 FillConnectPropertyMap(&props);
445 Error error;
446 Connect(props, &error, callback);
447 return;
448 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400449 } else {
450 if (!apn_try_list_.empty()) {
Jason Glasgow7234ec32012-05-23 16:01:21 -0400451 service->SetLastGoodApn(apn_try_list_.front());
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400452 apn_try_list_.clear();
453 }
Nathan Williamsb54974f2012-04-19 11:16:30 -0400454 bearer_path_ = path;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400455 }
456
457 if (!callback.is_null())
458 callback.Run(error);
459}
460
461bool CellularCapabilityUniversal::AllowRoaming() {
462 bool requires_roaming =
463 home_provider_ ? home_provider_->requires_roaming : false;
464 return requires_roaming || allow_roaming_property();
465}
466
Jason Glasgowef965562012-04-10 16:12:35 -0400467void CellularCapabilityUniversal::GetProperties() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700468 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400469
Jason Glasgowaf583282012-04-18 15:18:22 -0400470 scoped_ptr<DBusPropertiesProxyInterface> properties_proxy(
471 proxy_factory()->CreateDBusPropertiesProxy(cellular()->dbus_path(),
472 cellular()->dbus_owner()));
473 DBusPropertiesMap properties(
474 properties_proxy->GetAll(MM_DBUS_INTERFACE_MODEM));
475 OnModemPropertiesChanged(properties, vector<string>());
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400476
Jason Glasgowaf583282012-04-18 15:18:22 -0400477 properties = properties_proxy->GetAll(MM_DBUS_INTERFACE_MODEM_MODEM3GPP);
478 OnModem3GPPPropertiesChanged(properties, vector<string>());
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400479}
480
481string CellularCapabilityUniversal::CreateFriendlyServiceName() {
Darin Petkova4ca3c32012-08-17 16:05:24 +0200482 SLOG(Cellular, 2) << __func__ << ": " << GetRoamingStateString();
483 string name = serving_operator_.GetName();
484 string home_provider_name = cellular()->home_provider().GetName();
485 if (!name.empty()) {
486 // If roaming, try to show "<home-provider> | <serving-operator>", per 3GPP
487 // rules (TS 31.102 and annex A of 122.101).
488 if (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING &&
489 !home_provider_name.empty()) {
490 return home_provider_name + " | " + name;
491 }
492 return name;
493 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400494 if (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME &&
Darin Petkova4ca3c32012-08-17 16:05:24 +0200495 !home_provider_name.empty()) {
496 return home_provider_name;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400497 }
Darin Petkova4ca3c32012-08-17 16:05:24 +0200498 string serving_operator_code = serving_operator_.GetCode();
499 if (!serving_operator_code.empty()) {
500 return "cellular_" + serving_operator_code;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400501 }
502 return base::StringPrintf("GSMNetwork%u", friendly_service_name_id_++);
503}
504
505void CellularCapabilityUniversal::SetHomeProvider() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700506 SLOG(Cellular, 2) << __func__ << "(IMSI: " << imsi_
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400507 << " SPN: " << spn_ << ")";
508 // TODO(petkov): The test for NULL provider_db should be done by
509 // mobile_provider_lookup_best_match.
510 if (imsi_.empty() || !cellular()->provider_db()) {
511 return;
512 }
513 mobile_provider *provider =
514 mobile_provider_lookup_best_match(
515 cellular()->provider_db(), spn_.c_str(), imsi_.c_str());
516 if (!provider) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700517 SLOG(Cellular, 2) << "GSM provider not found.";
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400518 return;
519 }
Jason Glasgow4380f0d2012-05-03 18:05:04 -0400520
521 // Even if provider is the same as home_provider_, it is possible
522 // that the spn_ has changed. Run all the code below.
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400523 home_provider_ = provider;
Jason Glasgow4380f0d2012-05-03 18:05:04 -0400524
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400525 Cellular::Operator oper;
Darin Petkovb4fccd22012-08-10 11:59:26 +0200526 if (provider->networks && provider->networks[0]) {
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400527 oper.SetCode(provider->networks[0]);
528 }
529 if (provider->country) {
530 oper.SetCountry(provider->country);
531 }
532 if (spn_.empty()) {
533 const char *name = mobile_provider_get_name(provider);
534 if (name) {
535 oper.SetName(name);
536 }
537 } else {
538 oper.SetName(spn_);
539 }
540 cellular()->set_home_provider(oper);
Darin Petkova4ca3c32012-08-17 16:05:24 +0200541 SLOG(Cellular, 2) << "Home provider: " << oper.GetCode() << ", "
542 << oper.GetName() << ", " << oper.GetCountry();
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400543 InitAPNList();
544}
545
546void CellularCapabilityUniversal::UpdateOperatorInfo() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700547 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400548 const string &network_id = serving_operator_.GetCode();
549 if (!network_id.empty()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700550 SLOG(Cellular, 2) << "Looking up network id: " << network_id;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400551 mobile_provider *provider =
552 mobile_provider_lookup_by_network(cellular()->provider_db(),
553 network_id.c_str());
554 if (provider) {
Darin Petkova4ca3c32012-08-17 16:05:24 +0200555 if (serving_operator_.GetName().empty()) {
556 const char *provider_name = mobile_provider_get_name(provider);
557 if (provider_name && *provider_name) {
558 serving_operator_.SetName(provider_name);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400559 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400560 }
Darin Petkova4ca3c32012-08-17 16:05:24 +0200561 if (provider->country && *provider->country) {
562 serving_operator_.SetCountry(provider->country);
563 }
564 SLOG(Cellular, 2) << "Operator name: " << serving_operator_.GetName()
565 << ", country: " << serving_operator_.GetCountry();
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400566 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700567 SLOG(Cellular, 2) << "GSM provider not found.";
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400568 }
569 }
570 UpdateServingOperator();
571}
572
573void CellularCapabilityUniversal::UpdateServingOperator() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700574 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400575 if (cellular()->service().get()) {
576 cellular()->service()->SetServingOperator(serving_operator_);
577 }
578}
579
580void CellularCapabilityUniversal::InitAPNList() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700581 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400582 if (!home_provider_) {
583 return;
584 }
585 apn_list_.clear();
586 for (int i = 0; i < home_provider_->num_apns; ++i) {
587 Stringmap props;
588 mobile_apn *apn = home_provider_->apns[i];
589 if (apn->value) {
590 props[flimflam::kApnProperty] = apn->value;
591 }
592 if (apn->username) {
593 props[flimflam::kApnUsernameProperty] = apn->username;
594 }
595 if (apn->password) {
596 props[flimflam::kApnPasswordProperty] = apn->password;
597 }
598 // Find the first localized and non-localized name, if any.
599 const localized_name *lname = NULL;
600 const localized_name *name = NULL;
601 for (int j = 0; j < apn->num_names; ++j) {
602 if (apn->names[j]->lang) {
603 if (!lname) {
604 lname = apn->names[j];
605 }
606 } else if (!name) {
607 name = apn->names[j];
608 }
609 }
610 if (name) {
611 props[flimflam::kApnNameProperty] = name->name;
612 }
613 if (lname) {
614 props[flimflam::kApnLocalizedNameProperty] = lname->name;
615 props[flimflam::kApnLanguageProperty] = lname->lang;
616 }
617 apn_list_.push_back(props);
618 }
Darin Petkovdb6083f2012-08-16 12:50:23 +0200619 if (cellular()->adaptor()) {
620 cellular()->adaptor()->EmitStringmapsChanged(
621 flimflam::kCellularApnListProperty, apn_list_);
622 } else {
623 LOG(ERROR) << "Null RPC service adaptor.";
624 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400625}
626
627// always called from an async context
628void CellularCapabilityUniversal::Register(const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700629 SLOG(Cellular, 2) << __func__ << " \"" << selected_network_ << "\"";
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400630 CHECK(!callback.is_null());
631 Error error;
632 ResultCallback cb = Bind(&CellularCapabilityUniversal::OnRegisterReply,
633 weak_ptr_factory_.GetWeakPtr(), callback);
634 modem_3gpp_proxy_->Register(selected_network_, &error, cb, kTimeoutRegister);
635 if (error.IsFailure())
636 callback.Run(error);
637}
638
639void CellularCapabilityUniversal::RegisterOnNetwork(
640 const string &network_id,
641 Error *error,
642 const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700643 SLOG(Cellular, 2) << __func__ << "(" << network_id << ")";
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400644 CHECK(error);
645 desired_network_ = network_id;
646 ResultCallback cb = Bind(&CellularCapabilityUniversal::OnRegisterReply,
647 weak_ptr_factory_.GetWeakPtr(), callback);
648 modem_3gpp_proxy_->Register(network_id, error, cb, kTimeoutRegister);
649}
650
651void CellularCapabilityUniversal::OnRegisterReply(
652 const ResultCallback &callback,
653 const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700654 SLOG(Cellular, 2) << __func__ << "(" << error << ")";
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400655
656 if (error.IsSuccess()) {
657 selected_network_ = desired_network_;
658 desired_network_.clear();
659 callback.Run(error);
660 return;
661 }
662 // If registration on the desired network failed,
663 // try to register on the home network.
664 if (!desired_network_.empty()) {
665 desired_network_.clear();
666 selected_network_.clear();
667 LOG(INFO) << "Couldn't register on selected network, trying home network";
668 Register(callback);
669 return;
670 }
671 callback.Run(error);
672}
673
674bool CellularCapabilityUniversal::IsRegistered() {
675 return (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
676 registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING);
677}
678
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400679void CellularCapabilityUniversal::SetUnregistered(bool searching) {
680 // If we're already in some non-registered state, don't override that
681 if (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
682 registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) {
683 registration_state_ =
684 (searching ? MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING :
685 MM_MODEM_3GPP_REGISTRATION_STATE_IDLE);
686 }
687}
688
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400689void CellularCapabilityUniversal::RequirePIN(
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400690 const string &pin, bool require,
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400691 Error *error, const ResultCallback &callback) {
692 CHECK(error);
693 sim_proxy_->EnablePin(pin, require, error, callback, kTimeoutDefault);
694}
695
696void CellularCapabilityUniversal::EnterPIN(const string &pin,
697 Error *error,
698 const ResultCallback &callback) {
699 CHECK(error);
700 sim_proxy_->SendPin(pin, error, callback, kTimeoutDefault);
701}
702
703void CellularCapabilityUniversal::UnblockPIN(const string &unblock_code,
704 const string &pin,
705 Error *error,
706 const ResultCallback &callback) {
707 CHECK(error);
708 sim_proxy_->SendPuk(unblock_code, pin, error, callback, kTimeoutDefault);
709}
710
711void CellularCapabilityUniversal::ChangePIN(
712 const string &old_pin, const string &new_pin,
713 Error *error, const ResultCallback &callback) {
714 CHECK(error);
715 sim_proxy_->ChangePin(old_pin, new_pin, error, callback, kTimeoutDefault);
716}
717
718void CellularCapabilityUniversal::Scan(Error *error,
719 const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700720 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400721 CHECK(error);
Jason Glasgowcd0349c2012-05-03 23:32:15 -0400722 if (scanning_) {
723 Error::PopulateAndLog(error, Error::kInProgress, "Already scanning");
724 return;
725 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400726 DBusPropertyMapsCallback cb = Bind(&CellularCapabilityUniversal::OnScanReply,
727 weak_ptr_factory_.GetWeakPtr(), callback);
728 modem_3gpp_proxy_->Scan(error, cb, kTimeoutScan);
Jason Glasgowcd0349c2012-05-03 23:32:15 -0400729 if (!error->IsFailure()) {
730 scanning_ = true;
731 cellular()->adaptor()->EmitBoolChanged(flimflam::kScanningProperty,
732 scanning_);
733 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400734}
735
736void CellularCapabilityUniversal::OnScanReply(const ResultCallback &callback,
737 const ScanResults &results,
738 const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700739 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400740
741 // Error handling is weak. The current expectation is that on any
742 // error, found_networks_ should be cleared and a property change
743 // notification sent out.
744 //
745 // TODO(jglasgow): fix error handling
Jason Glasgowcd0349c2012-05-03 23:32:15 -0400746 scanning_ = false;
747 cellular()->adaptor()->EmitBoolChanged(flimflam::kScanningProperty,
748 scanning_);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400749 found_networks_.clear();
750 if (!error.IsFailure()) {
751 for (ScanResults::const_iterator it = results.begin();
752 it != results.end(); ++it) {
753 found_networks_.push_back(ParseScanResult(*it));
754 }
755 }
756 cellular()->adaptor()->EmitStringmapsChanged(flimflam::kFoundNetworksProperty,
757 found_networks_);
Gary Morainceba6aa2012-05-03 10:28:26 -0700758
759 // TODO(gmorain): This check for is_null() is a work-around because
760 // Cellular::Scan() passes a null callback. Instead: 1. Have Cellular::Scan()
761 // pass in a callback. 2. Have Cellular "own" the found_networks_ property
762 // 3. Have Cellular EmitStingMapsChanged() 4. Share the code between GSM and
763 // Universal.
764 if (!callback.is_null())
765 callback.Run(error);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400766}
767
768Stringmap CellularCapabilityUniversal::ParseScanResult(
769 const ScanResult &result) {
770
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400771 /* ScanResults contain the following keys:
772
773 "status"
774 A MMModem3gppNetworkAvailability value representing network
775 availability status, given as an unsigned integer (signature "u").
776 This key will always be present.
777
778 "operator-long"
779 Long-format name of operator, given as a string value (signature
780 "s"). If the name is unknown, this field should not be present.
781
782 "operator-short"
783 Short-format name of operator, given as a string value
784 (signature "s"). If the name is unknown, this field should not
785 be present.
786
787 "operator-code"
788 Mobile code of the operator, given as a string value (signature
789 "s"). Returned in the format "MCCMNC", where MCC is the
790 three-digit ITU E.212 Mobile Country Code and MNC is the two- or
791 three-digit GSM Mobile Network Code. e.g. "31026" or "310260".
792
793 "access-technology"
794 A MMModemAccessTechnology value representing the generic access
795 technology used by this mobile network, given as an unsigned
796 integer (signature "u").
797 */
798 Stringmap parsed;
799
800 uint32 status;
801 if (DBusProperties::GetUint32(result, kStatusProperty, &status)) {
802 // numerical values are taken from 3GPP TS 27.007 Section 7.3.
803 static const char * const kStatusString[] = {
804 "unknown", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN
805 "available", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE
806 "current", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT
807 "forbidden", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN
808 };
809 parsed[flimflam::kStatusProperty] = kStatusString[status];
810 }
811
812 uint32 tech; // MMModemAccessTechnology
813 if (DBusProperties::GetUint32(result, kOperatorAccessTechnologyProperty,
814 &tech)) {
815 parsed[flimflam::kTechnologyProperty] = AccessTechnologyToString(tech);
816 }
817
818 string operator_long, operator_short, operator_code;
819 if (DBusProperties::GetString(result, kOperatorLongProperty, &operator_long))
820 parsed[flimflam::kLongNameProperty] = operator_long;
821 if (DBusProperties::GetString(result, kOperatorShortProperty,
822 &operator_short))
823 parsed[flimflam::kShortNameProperty] = operator_short;
824 if (DBusProperties::GetString(result, kOperatorCodeProperty, &operator_code))
825 parsed[flimflam::kNetworkIdProperty] = operator_code;
826
827 // If the long name is not available but the network ID is, look up the long
828 // name in the mobile provider database.
829 if ((!ContainsKey(parsed, flimflam::kLongNameProperty) ||
830 parsed[flimflam::kLongNameProperty].empty()) &&
831 ContainsKey(parsed, flimflam::kNetworkIdProperty)) {
832 mobile_provider *provider =
833 mobile_provider_lookup_by_network(
834 cellular()->provider_db(),
835 parsed[flimflam::kNetworkIdProperty].c_str());
836 if (provider) {
837 const char *long_name = mobile_provider_get_name(provider);
838 if (long_name && *long_name) {
839 parsed[flimflam::kLongNameProperty] = long_name;
840 }
841 }
842 }
843 return parsed;
844}
845
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400846string CellularCapabilityUniversal::GetNetworkTechnologyString() const {
Jason Glasgow9f09aef2012-05-08 16:26:55 -0400847 // Order is important. Return the highest speed technology
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400848 // TODO(jglasgow): change shill interfaces to a capability model
849
850 return AccessTechnologyToString(access_technologies_);
851}
852
853string CellularCapabilityUniversal::GetRoamingStateString() const {
854 switch (registration_state_) {
855 case MM_MODEM_3GPP_REGISTRATION_STATE_HOME:
856 return flimflam::kRoamingStateHome;
857 case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING:
858 return flimflam::kRoamingStateRoaming;
859 default:
860 break;
861 }
862 return flimflam::kRoamingStateUnknown;
863}
864
865void CellularCapabilityUniversal::GetSignalQuality() {
Nathan Williams218cbcd2012-04-17 16:48:44 -0400866 // TODO(njw): Switch to asynchronous calls (crosbug.com/17583).
867 const DBus::Struct<unsigned int, bool> quality =
868 modem_proxy_->SignalQuality();
869 OnSignalQualityChanged(quality._1);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400870}
871
Jason Glasgow9f09aef2012-05-08 16:26:55 -0400872string CellularCapabilityUniversal::GetTypeString() const {
873 return AccessTechnologyToTechnologyFamily(access_technologies_);
874}
875
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400876void CellularCapabilityUniversal::OnModemPropertiesChanged(
877 const DBusPropertiesMap &properties,
878 const vector<string> &/* invalidated_properties */) {
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400879 // This solves a bootstrapping problem: If the modem is not yet
880 // enabled, there are no proxy objects associated with the capability
881 // object, so modem signals like StateChanged aren't seen. By monitoring
882 // changes to the State property via the ModemManager, we're able to
883 // get the initialization process started, which will result in the
884 // creation of the proxy objects.
885 //
886 // The first time we see the change to State (when the modem state
887 // is Unknown), we simply update the state, and rely on the Manager to
888 // enable the device when it is registered with the Manager. On subsequent
889 // changes to State, we need to explicitly enable the device ourselves.
890 int32 istate;
Jason Glasgowaf583282012-04-18 15:18:22 -0400891 if (DBusProperties::GetInt32(properties, MM_MODEM_PROPERTY_STATE, &istate)) {
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400892 Cellular::ModemState state = static_cast<Cellular::ModemState>(istate);
Jason Glasgowaf583282012-04-18 15:18:22 -0400893 OnModemStateChanged(state);
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400894 }
Jason Glasgowaf583282012-04-18 15:18:22 -0400895 string string_value;
Nathan Williamse9840802012-04-18 18:47:40 -0400896 if (DBusProperties::GetObjectPath(properties,
Jason Glasgowaf583282012-04-18 15:18:22 -0400897 MM_MODEM_PROPERTY_SIM, &string_value))
898 OnSimPathChanged(string_value);
899 uint32 uint_value;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400900 if (DBusProperties::GetUint32(properties,
Jason Glasgowaf583282012-04-18 15:18:22 -0400901 MM_MODEM_PROPERTY_MODEMCAPABILITIES,
902 &uint_value))
903 OnModemCapabilitesChanged(uint_value);
904 if (DBusProperties::GetUint32(properties,
905 MM_MODEM_PROPERTY_CURRENTCAPABILITIES,
906 &uint_value))
907 OnModemCurrentCapabilitiesChanged(uint_value);
908 // not needed: MM_MODEM_PROPERTY_MAXBEARERS
909 // not needed: MM_MODEM_PROPERTY_MAXACTIVEBEARERS
910 if (DBusProperties::GetString(properties,
911 MM_MODEM_PROPERTY_MANUFACTURER,
912 &string_value))
913 OnModemManufacturerChanged(string_value);
914 if (DBusProperties::GetString(properties,
915 MM_MODEM_PROPERTY_MODEL,
916 &string_value))
917 OnModemModelChanged(string_value);
918 if (DBusProperties::GetString(properties,
919 MM_MODEM_PROPERTY_REVISION,
920 &string_value))
921 OnModemRevisionChanged(string_value);
922 // not needed: MM_MODEM_PROPERTY_DEVICEIDENTIFIER
923 // not needed: MM_MODEM_PROPERTY_DEVICE
924 // not needed: MM_MODEM_PROPERTY_DRIVER
925 // not needed: MM_MODEM_PROPERTY_PLUGIN
926 // not needed: MM_MODEM_PROPERTY_EQUIPMENTIDENTIFIER
Nathan Williams218cbcd2012-04-17 16:48:44 -0400927
Jason Glasgowaf583282012-04-18 15:18:22 -0400928 // Unlock required and SimLock
929 bool locks_changed = false;
930 uint32_t unlock_required; // This is really of type MMModemLock
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400931 if (DBusProperties::GetUint32(properties,
932 MM_MODEM_PROPERTY_UNLOCKREQUIRED,
Jason Glasgowaf583282012-04-18 15:18:22 -0400933 &unlock_required)) {
934 locks_changed = true;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400935 }
Jason Glasgowaf583282012-04-18 15:18:22 -0400936 LockRetryData lock_retries;
937 DBusPropertiesMap::const_iterator it =
938 properties.find(MM_MODEM_PROPERTY_UNLOCKRETRIES);
939 if (it != properties.end()) {
940 lock_retries = it->second;
941 locks_changed = true;
942 }
943 if (locks_changed)
944 OnLockRetriesChanged(static_cast<MMModemLock>(unlock_required),
945 lock_retries);
946 if (DBusProperties::GetUint32(properties,
947 MM_MODEM_PROPERTY_ACCESSTECHNOLOGIES,
948 &uint_value))
949 OnAccessTechnologiesChanged(uint_value);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400950
Jason Glasgowaf583282012-04-18 15:18:22 -0400951 it = properties.find(MM_MODEM_PROPERTY_SIGNALQUALITY);
952 if (it != properties.end()) {
953 DBus::Struct<unsigned int, bool> quality = it->second;
954 OnSignalQualityChanged(quality._1);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400955 }
Jason Glasgowaf583282012-04-18 15:18:22 -0400956 vector<string> numbers;
957 if (DBusProperties::GetStrings(properties, MM_MODEM_PROPERTY_OWNNUMBERS,
958 &numbers)) {
959 string mdn;
960 if (numbers.size() > 0)
961 mdn = numbers[0];
962 OnMdnChanged(mdn);
963 }
964 if (DBusProperties::GetUint32(properties, MM_MODEM_PROPERTY_SUPPORTEDMODES,
965 &uint_value))
966 OnSupportedModesChanged(uint_value);
967 if (DBusProperties::GetUint32(properties, MM_MODEM_PROPERTY_ALLOWEDMODES,
968 &uint_value))
969 OnAllowedModesChanged(uint_value);
970 if (DBusProperties::GetUint32(properties, MM_MODEM_PROPERTY_PREFERREDMODE,
971 &uint_value))
972 OnPreferredModeChanged(static_cast<MMModemMode>(uint_value));
973 // au: MM_MODEM_PROPERTY_SUPPORTEDBANDS,
974 // au: MM_MODEM_PROPERTY_BANDS
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400975}
976
977void CellularCapabilityUniversal::OnDBusPropertiesChanged(
978 const string &interface,
979 const DBusPropertiesMap &changed_properties,
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400980 const vector<string> &invalidated_properties) {
Jason Glasgow7234ec32012-05-23 16:01:21 -0400981 SLOG(Cellular, 2) << __func__ << "(" << interface << ")";
Jason Glasgowef965562012-04-10 16:12:35 -0400982 if (interface == MM_DBUS_INTERFACE_MODEM) {
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400983 OnModemPropertiesChanged(changed_properties, invalidated_properties);
Jason Glasgowef965562012-04-10 16:12:35 -0400984 }
Jason Glasgowaf583282012-04-18 15:18:22 -0400985 if (interface == MM_DBUS_INTERFACE_MODEM_MODEM3GPP) {
986 OnModem3GPPPropertiesChanged(changed_properties, invalidated_properties);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400987 }
Jason Glasgowaf583282012-04-18 15:18:22 -0400988 if (interface == MM_DBUS_INTERFACE_SIM) {
989 OnSimPropertiesChanged(changed_properties, invalidated_properties);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400990 }
991}
992
Jason Glasgow14521872012-05-07 19:12:15 -0400993bool CellularCapabilityUniversal::RetriableConnectError(
994 const Error &error) const {
995 if (error.type() == Error::kInvalidApn)
996 return true;
997
998 // modemmanager does not ever return kInvalidApn for E362 modems
999 // with 1.41 firmware. It remains to be seem if this will change
1000 // with 3.x firmware.
1001 if ((model_id_ == kE362ModelId) && (error.type() == Error::kOperationFailed))
1002 return true;
1003
1004 return false;
1005}
1006
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001007void CellularCapabilityUniversal::OnNetworkModeSignal(uint32 /*mode*/) {
1008 // TODO(petkov): Implement this.
1009 NOTIMPLEMENTED();
1010}
1011
Jason Glasgowaf583282012-04-18 15:18:22 -04001012void CellularCapabilityUniversal::OnSimPathChanged(
1013 const string &sim_path) {
1014 if (sim_path == sim_path_)
1015 return;
1016
1017 mm1::SimProxyInterface *proxy = NULL;
1018 if (!sim_path.empty())
1019 proxy = proxy_factory()->CreateSimProxy(sim_path,
1020 cellular()->dbus_owner());
1021 sim_path_ = sim_path;
1022 sim_proxy_.reset(proxy);
1023
1024 if (sim_path.empty()) {
1025 // Clear all data about the sim
Jason Glasgow4380f0d2012-05-03 18:05:04 -04001026 imsi_ = "";
1027 spn_ = "";
Jason Glasgowaf583282012-04-18 15:18:22 -04001028 OnSimIdentifierChanged("");
1029 OnOperatorIdChanged("");
Jason Glasgowaf583282012-04-18 15:18:22 -04001030 } else {
1031 scoped_ptr<DBusPropertiesProxyInterface> properties_proxy(
1032 proxy_factory()->CreateDBusPropertiesProxy(sim_path,
1033 cellular()->dbus_owner()));
1034 // TODO(jglasgow): convert to async interface
1035 DBusPropertiesMap properties(
1036 properties_proxy->GetAll(MM_DBUS_INTERFACE_SIM));
1037 OnSimPropertiesChanged(properties, vector<string>());
1038 }
1039}
1040
1041void CellularCapabilityUniversal::OnModemCapabilitesChanged(
1042 uint32 capabilities) {
1043 capabilities_ = capabilities;
1044}
1045
1046void CellularCapabilityUniversal::OnModemCurrentCapabilitiesChanged(
1047 uint32 current_capabilities) {
1048 current_capabilities_ = current_capabilities;
1049}
1050
1051void CellularCapabilityUniversal::OnMdnChanged(
1052 const string &mdn) {
1053 mdn_ = mdn;
1054}
1055
1056void CellularCapabilityUniversal::OnModemManufacturerChanged(
1057 const string &manufacturer) {
1058 manufacturer_ = manufacturer;
1059}
1060
1061void CellularCapabilityUniversal::OnModemModelChanged(
1062 const string &model) {
1063 model_id_ = model;
1064}
1065
1066void CellularCapabilityUniversal::OnModemRevisionChanged(
1067 const string &revision) {
1068 firmware_revision_ = revision;
1069}
1070
1071void CellularCapabilityUniversal::OnModemStateChanged(
1072 Cellular::ModemState state) {
1073 Cellular::ModemState prev_modem_state = cellular()->modem_state();
1074 bool was_enabled = cellular()->IsUnderlyingDeviceEnabled();
1075 if (Cellular::IsEnabledModemState(state))
1076 cellular()->set_modem_state(state);
Gary Moraine285a842012-08-15 08:23:57 -07001077 SLOG(Cellular, 2) << __func__ << ": prev_modem_state: " << prev_modem_state
1078 << " was_enabled: " << was_enabled
1079 << " cellular state: "
1080 << cellular()->GetStateString(cellular()->state());
Jason Glasgowaf583282012-04-18 15:18:22 -04001081 if (prev_modem_state != Cellular::kModemStateUnknown &&
1082 prev_modem_state != Cellular::kModemStateEnabling &&
1083 !was_enabled &&
1084 cellular()->state() == Cellular::kStateDisabled &&
1085 cellular()->IsUnderlyingDeviceEnabled()) {
1086 cellular()->SetEnabled(true);
1087 }
1088}
1089
1090void CellularCapabilityUniversal::OnAccessTechnologiesChanged(
1091 uint32 access_technologies) {
Jason Glasgow9f09aef2012-05-08 16:26:55 -04001092 if (access_technologies_ != access_technologies) {
Jason Glasgowbad114b2012-05-21 15:24:16 -04001093 const string old_type_string(GetTypeString());
Jason Glasgow9f09aef2012-05-08 16:26:55 -04001094 access_technologies_ = access_technologies;
Jason Glasgowbad114b2012-05-21 15:24:16 -04001095 const string new_type_string(GetTypeString());
1096 if (new_type_string != old_type_string) {
1097 // TODO(jglasgow): address layering violation of emitting change
1098 // signal here for a property owned by Cellular.
1099 cellular()->adaptor()->EmitStringChanged(
1100 flimflam::kTechnologyFamilyProperty, new_type_string);
1101 }
Jason Glasgow9f09aef2012-05-08 16:26:55 -04001102 if (cellular()->service().get()) {
1103 cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
1104 }
Jason Glasgowaf583282012-04-18 15:18:22 -04001105 }
1106}
1107
1108void CellularCapabilityUniversal::OnSupportedModesChanged(
1109 uint32 supported_modes) {
1110 supported_modes_ = supported_modes;
1111}
1112
1113void CellularCapabilityUniversal::OnAllowedModesChanged(
1114 uint32 allowed_modes) {
1115 allowed_modes_ = allowed_modes;
1116}
1117
1118void CellularCapabilityUniversal::OnPreferredModeChanged(
1119 MMModemMode preferred_mode) {
1120 preferred_mode_ = preferred_mode;
1121}
1122
1123void CellularCapabilityUniversal::OnLockRetriesChanged(
1124 MMModemLock unlock_required,
1125 const LockRetryData &lock_retries) {
Jason Glasgowd4af81d2012-05-03 22:25:05 -04001126 switch (unlock_required) {
Jason Glasgowaf583282012-04-18 15:18:22 -04001127 case MM_MODEM_LOCK_SIM_PIN:
1128 sim_lock_status_.lock_type = "sim-pin";
1129 break;
1130 case MM_MODEM_LOCK_SIM_PUK:
1131 sim_lock_status_.lock_type = "sim-puk";
1132 break;
1133 default:
1134 sim_lock_status_.lock_type = "";
1135 break;
1136 }
1137 LockRetryData::const_iterator it = lock_retries.find(unlock_required);
1138 if (it != lock_retries.end()) {
1139 sim_lock_status_.retries_left = it->second;
1140 } else {
1141 // Unknown, use 999
1142 sim_lock_status_.retries_left = 999;
1143 }
1144 OnSimLockStatusChanged();
1145}
1146
1147void CellularCapabilityUniversal::OnSimLockStatusChanged() {
1148 cellular()->adaptor()->EmitKeyValueStoreChanged(
1149 flimflam::kSIMLockStatusProperty, SimLockStatusToProperty(NULL));
1150}
1151
1152void CellularCapabilityUniversal::OnModem3GPPPropertiesChanged(
1153 const DBusPropertiesMap &properties,
1154 const vector<string> &/* invalidated_properties */) {
1155 VLOG(2) << __func__;
1156 string imei;
1157 if (DBusProperties::GetString(properties,
1158 MM_MODEM_MODEM3GPP_PROPERTY_IMEI,
1159 &imei))
1160 OnImeiChanged(imei);
1161
1162 // Handle registration state changes as a single change
1163 string operator_code = serving_operator_.GetCode();
1164 string operator_name = serving_operator_.GetName();
1165 MMModem3gppRegistrationState state = registration_state_;
1166 bool registration_changed = false;
1167 uint32 uint_value;
1168 if (DBusProperties::GetUint32(properties,
1169 MM_MODEM_MODEM3GPP_PROPERTY_REGISTRATIONSTATE,
1170 &uint_value)) {
1171 state = static_cast<MMModem3gppRegistrationState>(uint_value);
1172 registration_changed = true;
1173 }
1174 if (DBusProperties::GetString(properties,
1175 MM_MODEM_MODEM3GPP_PROPERTY_OPERATORCODE,
1176 &operator_code))
1177 registration_changed = true;
1178 if (DBusProperties::GetString(properties,
1179 MM_MODEM_MODEM3GPP_PROPERTY_OPERATORNAME,
1180 &operator_name))
1181 registration_changed = true;
1182 if (registration_changed)
1183 On3GPPRegistrationChanged(state, operator_code, operator_name);
1184
1185 uint32 locks = 0;
1186 if (DBusProperties::GetUint32(
1187 properties, MM_MODEM_MODEM3GPP_PROPERTY_ENABLEDFACILITYLOCKS,
1188 &locks))
1189 OnFacilityLocksChanged(locks);
1190}
1191
1192void CellularCapabilityUniversal::OnImeiChanged(const string &imei) {
1193 imei_ = imei;
1194}
1195
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001196void CellularCapabilityUniversal::On3GPPRegistrationChanged(
1197 MMModem3gppRegistrationState state,
1198 const string &operator_code,
1199 const string &operator_name) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001200 SLOG(Cellular, 2) << __func__ << ": regstate=" << state
1201 << ", opercode=" << operator_code
1202 << ", opername=" << operator_name;
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001203 registration_state_ = state;
1204 serving_operator_.SetCode(operator_code);
1205 serving_operator_.SetName(operator_name);
1206 UpdateOperatorInfo();
1207 cellular()->HandleNewRegistrationState();
1208}
1209
Eric Shienbrood7fce52c2012-04-13 19:11:02 -04001210void CellularCapabilityUniversal::OnModemStateChangedSignal(
1211 int32 old_state, int32 new_state, uint32 reason) {
Eric Shienbrood9b1bd7b2012-04-20 16:42:26 -04001212 SLOG(Cellular, 2) << __func__ << "(" << old_state << ", " << new_state << ", "
1213 << reason << ")";
Eric Shienbrood7fce52c2012-04-13 19:11:02 -04001214 cellular()->OnModemStateChanged(static_cast<Cellular::ModemState>(old_state),
1215 static_cast<Cellular::ModemState>(new_state),
1216 reason);
Gary Moraine285a842012-08-15 08:23:57 -07001217 if (!deferred_enable_modem_callback_.is_null() &&
1218 (new_state == Cellular::kModemStateDisabled)) {
1219 SLOG(Cellular, 2) << "Enabling modem after deferring";
1220 deferred_enable_modem_callback_.Run();
1221 deferred_enable_modem_callback_.Reset();
1222 }
Eric Shienbrood7fce52c2012-04-13 19:11:02 -04001223}
1224
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001225void CellularCapabilityUniversal::OnSignalQualityChanged(uint32 quality) {
1226 cellular()->HandleNewSignalQuality(quality);
1227}
1228
Jason Glasgowaf583282012-04-18 15:18:22 -04001229void CellularCapabilityUniversal::OnFacilityLocksChanged(uint32 locks) {
1230 if (sim_lock_status_.enabled != (locks & MM_MODEM_3GPP_FACILITY_SIM)) {
1231 sim_lock_status_.enabled = locks & MM_MODEM_3GPP_FACILITY_SIM;
1232 OnSimLockStatusChanged();
1233 }
1234}
Jason Glasgowef965562012-04-10 16:12:35 -04001235
Jason Glasgowaf583282012-04-18 15:18:22 -04001236void CellularCapabilityUniversal::OnSimPropertiesChanged(
1237 const DBusPropertiesMap &props,
1238 const vector<string> &/* invalidated_properties */) {
1239 VLOG(2) << __func__;
1240 string value;
Jason Glasgow4380f0d2012-05-03 18:05:04 -04001241 bool must_update_home_provider = false;
Jason Glasgowaf583282012-04-18 15:18:22 -04001242 if (DBusProperties::GetString(props, MM_SIM_PROPERTY_SIMIDENTIFIER, &value))
1243 OnSimIdentifierChanged(value);
1244 if (DBusProperties::GetString(props, MM_SIM_PROPERTY_OPERATORIDENTIFIER,
1245 &value))
1246 OnOperatorIdChanged(value);
Jason Glasgow4380f0d2012-05-03 18:05:04 -04001247 if (DBusProperties::GetString(props, MM_SIM_PROPERTY_OPERATORNAME, &value)) {
1248 spn_ = value;
1249 must_update_home_provider = true;
1250 }
1251 if (DBusProperties::GetString(props, MM_SIM_PROPERTY_IMSI, &value)) {
1252 imsi_ = value;
1253 must_update_home_provider = true;
1254 }
Jason Glasgowaf583282012-04-18 15:18:22 -04001255 // TODO(jglasgow): May eventually want to get SPDI, etc
Jason Glasgowaf583282012-04-18 15:18:22 -04001256
Jason Glasgow4380f0d2012-05-03 18:05:04 -04001257 if (must_update_home_provider)
1258 SetHomeProvider();
Jason Glasgowaf583282012-04-18 15:18:22 -04001259}
1260
1261void CellularCapabilityUniversal::OnSimIdentifierChanged(const string &id) {
1262 sim_identifier_ = id;
1263}
1264
1265void CellularCapabilityUniversal::OnOperatorIdChanged(
1266 const string &operator_id) {
1267 operator_id_ = operator_id;
1268}
1269
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001270} // namespace shill