blob: f90819c7dee421170110bd29c479780af5323583 [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 Chan5c853ef2012-10-05 00:05:37 -070012#include <ModemManager/ModemManager.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),
Darin Petkovf508c822012-09-21 13:43:17 +0200128 provider_requires_roaming_(false),
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400129 scanning_supported_(true),
130 scanning_(false),
Ben Chanbd3aee82012-10-16 23:52:04 -0700131 scan_interval_(0),
132 sim_present_(false) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700133 SLOG(Cellular, 2) << "Cellular capability constructed: Universal";
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400134 PropertyStore *store = cellular->mutable_store();
135
136 store->RegisterConstString(flimflam::kCarrierProperty, &carrier_);
137 store->RegisterConstBool(flimflam::kSupportNetworkScanProperty,
138 &scanning_supported_);
139 store->RegisterConstString(flimflam::kEsnProperty, &esn_);
140 store->RegisterConstString(flimflam::kFirmwareRevisionProperty,
141 &firmware_revision_);
142 store->RegisterConstString(flimflam::kHardwareRevisionProperty,
143 &hardware_revision_);
144 store->RegisterConstString(flimflam::kImeiProperty, &imei_);
145 store->RegisterConstString(flimflam::kImsiProperty, &imsi_);
Ben Chana5e27082012-07-31 14:30:28 -0700146 store->RegisterConstString(flimflam::kIccidProperty, &sim_identifier_);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400147 store->RegisterConstString(flimflam::kManufacturerProperty, &manufacturer_);
148 store->RegisterConstString(flimflam::kMdnProperty, &mdn_);
149 store->RegisterConstString(flimflam::kMeidProperty, &meid_);
150 store->RegisterConstString(flimflam::kMinProperty, &min_);
151 store->RegisterConstString(flimflam::kModelIDProperty, &model_id_);
152 store->RegisterConstString(flimflam::kSelectedNetworkProperty,
153 &selected_network_);
154 store->RegisterConstStringmaps(flimflam::kFoundNetworksProperty,
155 &found_networks_);
Darin Petkovf508c822012-09-21 13:43:17 +0200156 store->RegisterConstBool(shill::kProviderRequiresRoamingProperty,
157 &provider_requires_roaming_);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400158 store->RegisterConstBool(flimflam::kScanningProperty, &scanning_);
159 store->RegisterUint16(flimflam::kScanIntervalProperty, &scan_interval_);
160 HelpRegisterDerivedKeyValueStore(
161 flimflam::kSIMLockStatusProperty,
162 &CellularCapabilityUniversal::SimLockStatusToProperty,
163 NULL);
Ben Chanbd3aee82012-10-16 23:52:04 -0700164 store->RegisterConstBool(shill::kSIMPresentProperty, &sim_present_);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400165 store->RegisterConstStringmaps(flimflam::kCellularApnListProperty,
166 &apn_list_);
167}
168
169KeyValueStore CellularCapabilityUniversal::SimLockStatusToProperty(
170 Error */*error*/) {
171 KeyValueStore status;
172 status.SetBool(flimflam::kSIMLockEnabledProperty, sim_lock_status_.enabled);
173 status.SetString(flimflam::kSIMLockTypeProperty, sim_lock_status_.lock_type);
174 status.SetUint(flimflam::kSIMLockRetriesLeftProperty,
175 sim_lock_status_.retries_left);
176 return status;
177}
178
179void CellularCapabilityUniversal::HelpRegisterDerivedKeyValueStore(
180 const string &name,
181 KeyValueStore(CellularCapabilityUniversal::*get)(Error *error),
182 void(CellularCapabilityUniversal::*set)(
183 const KeyValueStore &value, Error *error)) {
184 cellular()->mutable_store()->RegisterDerivedKeyValueStore(
185 name,
186 KeyValueStoreAccessor(
187 new CustomAccessor<CellularCapabilityUniversal, KeyValueStore>(
188 this, get, set)));
189}
190
191void CellularCapabilityUniversal::InitProxies() {
192 modem_3gpp_proxy_.reset(
193 proxy_factory()->CreateMM1ModemModem3gppProxy(cellular()->dbus_path(),
194 cellular()->dbus_owner()));
195 modem_cdma_proxy_.reset(
196 proxy_factory()->CreateMM1ModemModemCdmaProxy(cellular()->dbus_path(),
197 cellular()->dbus_owner()));
198 modem_proxy_.reset(
199 proxy_factory()->CreateMM1ModemProxy(cellular()->dbus_path(),
200 cellular()->dbus_owner()));
201 modem_simple_proxy_.reset(
202 proxy_factory()->CreateMM1ModemSimpleProxy(cellular()->dbus_path(),
203 cellular()->dbus_owner()));
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400204 modem_proxy_->set_state_changed_callback(
205 Bind(&CellularCapabilityUniversal::OnModemStateChangedSignal,
206 weak_ptr_factory_.GetWeakPtr()));
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400207 // Do not create a SIM proxy until the device is enabled because we
208 // do not yet know the object path of the sim object.
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400209 // TODO(jglasgow): register callbacks
210}
211
212void CellularCapabilityUniversal::StartModem(Error *error,
Jason Glasgowef965562012-04-10 16:12:35 -0400213 const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700214 SLOG(Cellular, 2) << __func__;
Jason Glasgowef965562012-04-10 16:12:35 -0400215 InitProxies();
216
Gary Moraine285a842012-08-15 08:23:57 -0700217 // ModemManager must be in the disabled state to accept the Enable command.
218 Cellular::ModemState state =
219 static_cast<Cellular::ModemState>(modem_proxy_->State());
220 if (state == Cellular::kModemStateDisabled) {
221 EnableModem(error, callback);
222 } else if (!cellular()->IsUnderlyingDeviceEnabled()) {
223 SLOG(Cellular, 2) << "Enabling of modem deferred because state is "
224 << state;
225 deferred_enable_modem_callback_ =
226 Bind(&CellularCapabilityUniversal::EnableModem,
227 weak_ptr_factory_.GetWeakPtr(), static_cast<Error *>(NULL),
228 callback);
229 } else {
Arman Uguray403fad42012-09-25 17:08:39 -0700230 // Call GetProperties() here to sync up with the modem state
231 GetProperties();
Gary Moraine285a842012-08-15 08:23:57 -0700232 callback.Run(*error);
233 }
234}
235
236void CellularCapabilityUniversal::EnableModem(Error *error,
237 const ResultCallback &callback) {
238 SLOG(Cellular, 2) << __func__;
Jason Glasgowef965562012-04-10 16:12:35 -0400239 CHECK(!callback.is_null());
Thieu Lee3b36592012-08-30 17:50:26 -0700240 Error local_error(Error::kOperationInitiated);
Jason Glasgowef965562012-04-10 16:12:35 -0400241 modem_proxy_->Enable(
242 true,
Gary Moraine285a842012-08-15 08:23:57 -0700243 &local_error,
Jason Glasgowef965562012-04-10 16:12:35 -0400244 Bind(&CellularCapabilityUniversal::Start_EnableModemCompleted,
245 weak_ptr_factory_.GetWeakPtr(), callback),
246 kTimeoutEnable);
Gary Moraine285a842012-08-15 08:23:57 -0700247 if (local_error.IsFailure()) {
248 SLOG(Cellular, 2) << __func__ << "Call to modem_proxy_->Enable() failed";
249 callback.Run(local_error);
250 }
251 if (error) {
252 error->CopyFrom(local_error);
253 }
Jason Glasgowef965562012-04-10 16:12:35 -0400254}
255
256void CellularCapabilityUniversal::Start_EnableModemCompleted(
257 const ResultCallback &callback, const Error &error) {
Eric Shienbrood9b1bd7b2012-04-20 16:42:26 -0400258 SLOG(Cellular, 2) << __func__ << ": " << error;
Jason Glasgowef965562012-04-10 16:12:35 -0400259 if (error.IsFailure()) {
260 callback.Run(error);
261 return;
262 }
263
264 // After modem is enabled, it should be possible to get properties
265 // TODO(jglasgow): handle errors from GetProperties
266 GetProperties();
Jason Glasgow4380f0d2012-05-03 18:05:04 -0400267 callback.Run(error);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400268}
269
270void CellularCapabilityUniversal::StopModem(Error *error,
Jason Glasgow02401cc2012-05-16 10:35:37 -0400271 const ResultCallback &callback) {
Jason Glasgowef965562012-04-10 16:12:35 -0400272 CHECK(!callback.is_null());
273 CHECK(error);
Thieu Led0012052012-07-25 16:09:09 -0700274 Cellular::ModemState state = cellular()->modem_state();
275 SLOG(Cellular, 2) << __func__ << "(" << state << ")";
Jason Glasgowef965562012-04-10 16:12:35 -0400276
Thieu Led0012052012-07-25 16:09:09 -0700277 if (cellular()->IsModemRegistered()) {
Jason Glasgow02401cc2012-05-16 10:35:37 -0400278 string all_bearers("/"); // "/" means all bearers. See Modemanager docs.
Jason Glasgowef965562012-04-10 16:12:35 -0400279 modem_simple_proxy_->Disconnect(
280 all_bearers,
281 error,
282 Bind(&CellularCapabilityUniversal::Stop_DisconnectCompleted,
283 weak_ptr_factory_.GetWeakPtr(), callback),
284 kTimeoutDefault);
285 if (error->IsFailure())
286 callback.Run(*error);
287 } else {
Jason Glasgowef965562012-04-10 16:12:35 -0400288 Closure task = Bind(&CellularCapabilityUniversal::Stop_Disable,
289 weak_ptr_factory_.GetWeakPtr(),
290 callback);
291 cellular()->dispatcher()->PostTask(task);
292 }
Gary Moraine285a842012-08-15 08:23:57 -0700293 deferred_enable_modem_callback_.Reset();
Jason Glasgowef965562012-04-10 16:12:35 -0400294}
295
296void CellularCapabilityUniversal::Stop_DisconnectCompleted(
297 const ResultCallback &callback, const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700298 SLOG(Cellular, 2) << __func__;
Jason Glasgowef965562012-04-10 16:12:35 -0400299
300 LOG_IF(ERROR, error.IsFailure()) << "Disconnect failed. Ignoring.";
301 Stop_Disable(callback);
302}
303
304void CellularCapabilityUniversal::Stop_Disable(const ResultCallback &callback) {
305 Error error;
306 modem_proxy_->Enable(
307 false, &error,
308 Bind(&CellularCapabilityUniversal::Stop_DisableCompleted,
309 weak_ptr_factory_.GetWeakPtr(), callback),
Jason Glasgow02401cc2012-05-16 10:35:37 -0400310 kTimeoutEnable);
Jason Glasgowef965562012-04-10 16:12:35 -0400311 if (error.IsFailure())
312 callback.Run(error);
313}
314
315void CellularCapabilityUniversal::Stop_DisableCompleted(
316 const ResultCallback &callback, const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700317 SLOG(Cellular, 2) << __func__;
Jason Glasgowef965562012-04-10 16:12:35 -0400318
319 if (error.IsSuccess())
320 ReleaseProxies();
321 callback.Run(error);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400322}
323
324void CellularCapabilityUniversal::Connect(const DBusPropertiesMap &properties,
325 Error *error,
326 const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700327 SLOG(Cellular, 2) << __func__;
Nathan Williamsb54974f2012-04-19 11:16:30 -0400328 DBusPathCallback cb = Bind(&CellularCapabilityUniversal::OnConnectReply,
329 weak_ptr_factory_.GetWeakPtr(),
330 callback);
331 modem_simple_proxy_->Connect(properties, error, cb, kTimeoutConnect);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400332}
333
334void CellularCapabilityUniversal::Disconnect(Error *error,
335 const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700336 SLOG(Cellular, 2) << __func__;
Thieu Le5d6864a2012-07-20 11:43:51 -0700337 if (bearer_path_.empty()) {
338 LOG(WARNING) << "In " << __func__ << "(): "
339 << "Ignoring attempt to disconnect without bearer";
Thieu Le3d275392012-07-20 15:32:58 -0700340 } else if (modem_simple_proxy_.get()) {
Thieu Le5d6864a2012-07-20 11:43:51 -0700341 modem_simple_proxy_->Disconnect(bearer_path_,
342 error,
343 callback,
344 kTimeoutDefault);
345 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400346}
347
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400348void CellularCapabilityUniversal::Activate(const string &carrier,
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400349 Error *error,
350 const ResultCallback &callback) {
351 OnUnsupportedOperation(__func__, error);
352}
353
354void CellularCapabilityUniversal::ReleaseProxies() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700355 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400356 modem_3gpp_proxy_.reset();
357 modem_cdma_proxy_.reset();
358 modem_proxy_.reset();
359 modem_simple_proxy_.reset();
360 sim_proxy_.reset();
361}
362
363void CellularCapabilityUniversal::OnServiceCreated() {
364 // If IMSI is available, base the service's storage identifier on it.
365 if (!imsi_.empty()) {
366 cellular()->service()->SetStorageIdentifier(
367 string(flimflam::kTypeCellular) + "_" +
368 cellular()->address() + "_" + imsi_);
369 }
370 cellular()->service()->SetActivationState(
371 flimflam::kActivationStateActivated);
372 UpdateServingOperator();
373}
374
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400375// Create the list of APNs to try, in the following order:
376// - last APN that resulted in a successful connection attempt on the
377// current network (if any)
378// - the APN, if any, that was set by the user
379// - the list of APNs found in the mobile broadband provider DB for the
380// home provider associated with the current SIM
381// - as a last resort, attempt to connect with no APN
382void CellularCapabilityUniversal::SetupApnTryList() {
383 apn_try_list_.clear();
384
385 DCHECK(cellular()->service().get());
386 const Stringmap *apn_info = cellular()->service()->GetLastGoodApn();
387 if (apn_info)
388 apn_try_list_.push_back(*apn_info);
389
390 apn_info = cellular()->service()->GetUserSpecifiedApn();
391 if (apn_info)
392 apn_try_list_.push_back(*apn_info);
393
394 apn_try_list_.insert(apn_try_list_.end(), apn_list_.begin(), apn_list_.end());
395}
396
397void CellularCapabilityUniversal::SetupConnectProperties(
398 DBusPropertiesMap *properties) {
399 SetupApnTryList();
400 FillConnectPropertyMap(properties);
401}
402
403void CellularCapabilityUniversal::FillConnectPropertyMap(
404 DBusPropertiesMap *properties) {
405
406 // TODO(jglasgow): Is this really needed anymore?
Jason Glasgow14521872012-05-07 19:12:15 -0400407 (*properties)[kConnectNumber].writer().append_string(
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400408 kPhoneNumber);
409
Jason Glasgow14521872012-05-07 19:12:15 -0400410 (*properties)[kConnectAllowRoaming].writer().append_bool(
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400411 AllowRoaming());
412
413 if (!apn_try_list_.empty()) {
414 // Leave the APN at the front of the list, so that it can be recorded
415 // if the connect attempt succeeds.
416 Stringmap apn_info = apn_try_list_.front();
Ben Chanfad4a0b2012-04-18 15:49:59 -0700417 SLOG(Cellular, 2) << __func__ << ": Using APN "
418 << apn_info[flimflam::kApnProperty];
Jason Glasgow14521872012-05-07 19:12:15 -0400419 (*properties)[kConnectApn].writer().append_string(
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400420 apn_info[flimflam::kApnProperty].c_str());
421 if (ContainsKey(apn_info, flimflam::kApnUsernameProperty))
Jason Glasgow14521872012-05-07 19:12:15 -0400422 (*properties)[kConnectUser].writer().append_string(
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400423 apn_info[flimflam::kApnUsernameProperty].c_str());
424 if (ContainsKey(apn_info, flimflam::kApnPasswordProperty))
Jason Glasgow14521872012-05-07 19:12:15 -0400425 (*properties)[kConnectPassword].writer().append_string(
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400426 apn_info[flimflam::kApnPasswordProperty].c_str());
427 }
428}
429
430void CellularCapabilityUniversal::OnConnectReply(const ResultCallback &callback,
Nathan Williamsb54974f2012-04-19 11:16:30 -0400431 const DBus::Path &path,
432 const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700433 SLOG(Cellular, 2) << __func__ << "(" << error << ")";
Nathan Williamsb54974f2012-04-19 11:16:30 -0400434
Jason Glasgow7234ec32012-05-23 16:01:21 -0400435 CellularServiceRefPtr service = cellular()->service();
436 if (!service) {
437 // The service could have been deleted before our Connect() request
438 // completes if the modem was enabled and then quickly disabled.
439 apn_try_list_.clear();
440 } else if (error.IsFailure()) {
441 service->ClearLastGoodApn();
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400442 // The APN that was just tried (and failed) is still at the
443 // front of the list, about to be removed. If the list is empty
444 // after that, try one last time without an APN. This may succeed
445 // with some modems in some cases.
Jason Glasgow14521872012-05-07 19:12:15 -0400446 if (RetriableConnectError(error) && !apn_try_list_.empty()) {
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400447 apn_try_list_.pop_front();
Ben Chanfad4a0b2012-04-18 15:49:59 -0700448 SLOG(Cellular, 2) << "Connect failed with invalid APN, "
449 << apn_try_list_.size() << " remaining APNs to try";
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400450 DBusPropertiesMap props;
451 FillConnectPropertyMap(&props);
452 Error error;
453 Connect(props, &error, callback);
454 return;
455 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400456 } else {
457 if (!apn_try_list_.empty()) {
Jason Glasgow7234ec32012-05-23 16:01:21 -0400458 service->SetLastGoodApn(apn_try_list_.front());
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400459 apn_try_list_.clear();
460 }
Nathan Williamsb54974f2012-04-19 11:16:30 -0400461 bearer_path_ = path;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400462 }
463
464 if (!callback.is_null())
465 callback.Run(error);
466}
467
468bool CellularCapabilityUniversal::AllowRoaming() {
Darin Petkovf508c822012-09-21 13:43:17 +0200469 return provider_requires_roaming_ || allow_roaming_property();
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400470}
471
Jason Glasgowef965562012-04-10 16:12:35 -0400472void CellularCapabilityUniversal::GetProperties() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700473 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400474
Jason Glasgowaf583282012-04-18 15:18:22 -0400475 scoped_ptr<DBusPropertiesProxyInterface> properties_proxy(
476 proxy_factory()->CreateDBusPropertiesProxy(cellular()->dbus_path(),
477 cellular()->dbus_owner()));
478 DBusPropertiesMap properties(
479 properties_proxy->GetAll(MM_DBUS_INTERFACE_MODEM));
480 OnModemPropertiesChanged(properties, vector<string>());
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400481
Jason Glasgowaf583282012-04-18 15:18:22 -0400482 properties = properties_proxy->GetAll(MM_DBUS_INTERFACE_MODEM_MODEM3GPP);
483 OnModem3GPPPropertiesChanged(properties, vector<string>());
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400484}
485
486string CellularCapabilityUniversal::CreateFriendlyServiceName() {
Darin Petkova4ca3c32012-08-17 16:05:24 +0200487 SLOG(Cellular, 2) << __func__ << ": " << GetRoamingStateString();
488 string name = serving_operator_.GetName();
489 string home_provider_name = cellular()->home_provider().GetName();
490 if (!name.empty()) {
491 // If roaming, try to show "<home-provider> | <serving-operator>", per 3GPP
492 // rules (TS 31.102 and annex A of 122.101).
493 if (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING &&
494 !home_provider_name.empty()) {
495 return home_provider_name + " | " + name;
496 }
497 return name;
498 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400499 if (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME &&
Darin Petkova4ca3c32012-08-17 16:05:24 +0200500 !home_provider_name.empty()) {
501 return home_provider_name;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400502 }
Darin Petkova4ca3c32012-08-17 16:05:24 +0200503 string serving_operator_code = serving_operator_.GetCode();
504 if (!serving_operator_code.empty()) {
505 return "cellular_" + serving_operator_code;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400506 }
507 return base::StringPrintf("GSMNetwork%u", friendly_service_name_id_++);
508}
509
510void CellularCapabilityUniversal::SetHomeProvider() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700511 SLOG(Cellular, 2) << __func__ << "(IMSI: " << imsi_
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400512 << " SPN: " << spn_ << ")";
513 // TODO(petkov): The test for NULL provider_db should be done by
514 // mobile_provider_lookup_best_match.
515 if (imsi_.empty() || !cellular()->provider_db()) {
516 return;
517 }
518 mobile_provider *provider =
519 mobile_provider_lookup_best_match(
520 cellular()->provider_db(), spn_.c_str(), imsi_.c_str());
521 if (!provider) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700522 SLOG(Cellular, 2) << "GSM provider not found.";
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400523 return;
524 }
Jason Glasgow4380f0d2012-05-03 18:05:04 -0400525
526 // Even if provider is the same as home_provider_, it is possible
527 // that the spn_ has changed. Run all the code below.
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400528 home_provider_ = provider;
Darin Petkovf508c822012-09-21 13:43:17 +0200529 provider_requires_roaming_ = home_provider_->requires_roaming;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400530 Cellular::Operator oper;
Darin Petkovb4fccd22012-08-10 11:59:26 +0200531 if (provider->networks && provider->networks[0]) {
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400532 oper.SetCode(provider->networks[0]);
533 }
534 if (provider->country) {
535 oper.SetCountry(provider->country);
536 }
537 if (spn_.empty()) {
538 const char *name = mobile_provider_get_name(provider);
539 if (name) {
540 oper.SetName(name);
541 }
542 } else {
543 oper.SetName(spn_);
544 }
545 cellular()->set_home_provider(oper);
Darin Petkova4ca3c32012-08-17 16:05:24 +0200546 SLOG(Cellular, 2) << "Home provider: " << oper.GetCode() << ", "
Darin Petkovf508c822012-09-21 13:43:17 +0200547 << oper.GetName() << ", " << oper.GetCountry()
548 << (provider_requires_roaming_ ? ", roaming required" : "");
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400549 InitAPNList();
550}
551
552void CellularCapabilityUniversal::UpdateOperatorInfo() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700553 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400554 const string &network_id = serving_operator_.GetCode();
555 if (!network_id.empty()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700556 SLOG(Cellular, 2) << "Looking up network id: " << network_id;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400557 mobile_provider *provider =
558 mobile_provider_lookup_by_network(cellular()->provider_db(),
559 network_id.c_str());
560 if (provider) {
Darin Petkova4ca3c32012-08-17 16:05:24 +0200561 if (serving_operator_.GetName().empty()) {
562 const char *provider_name = mobile_provider_get_name(provider);
563 if (provider_name && *provider_name) {
564 serving_operator_.SetName(provider_name);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400565 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400566 }
Darin Petkova4ca3c32012-08-17 16:05:24 +0200567 if (provider->country && *provider->country) {
568 serving_operator_.SetCountry(provider->country);
569 }
570 SLOG(Cellular, 2) << "Operator name: " << serving_operator_.GetName()
571 << ", country: " << serving_operator_.GetCountry();
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400572 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700573 SLOG(Cellular, 2) << "GSM provider not found.";
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400574 }
575 }
576 UpdateServingOperator();
577}
578
579void CellularCapabilityUniversal::UpdateServingOperator() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700580 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400581 if (cellular()->service().get()) {
582 cellular()->service()->SetServingOperator(serving_operator_);
583 }
584}
585
586void CellularCapabilityUniversal::InitAPNList() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700587 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400588 if (!home_provider_) {
589 return;
590 }
591 apn_list_.clear();
592 for (int i = 0; i < home_provider_->num_apns; ++i) {
593 Stringmap props;
594 mobile_apn *apn = home_provider_->apns[i];
595 if (apn->value) {
596 props[flimflam::kApnProperty] = apn->value;
597 }
598 if (apn->username) {
599 props[flimflam::kApnUsernameProperty] = apn->username;
600 }
601 if (apn->password) {
602 props[flimflam::kApnPasswordProperty] = apn->password;
603 }
604 // Find the first localized and non-localized name, if any.
605 const localized_name *lname = NULL;
606 const localized_name *name = NULL;
607 for (int j = 0; j < apn->num_names; ++j) {
608 if (apn->names[j]->lang) {
609 if (!lname) {
610 lname = apn->names[j];
611 }
612 } else if (!name) {
613 name = apn->names[j];
614 }
615 }
616 if (name) {
617 props[flimflam::kApnNameProperty] = name->name;
618 }
619 if (lname) {
620 props[flimflam::kApnLocalizedNameProperty] = lname->name;
621 props[flimflam::kApnLanguageProperty] = lname->lang;
622 }
623 apn_list_.push_back(props);
624 }
Darin Petkovdb6083f2012-08-16 12:50:23 +0200625 if (cellular()->adaptor()) {
626 cellular()->adaptor()->EmitStringmapsChanged(
627 flimflam::kCellularApnListProperty, apn_list_);
628 } else {
629 LOG(ERROR) << "Null RPC service adaptor.";
630 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400631}
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
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400685void CellularCapabilityUniversal::SetUnregistered(bool searching) {
686 // If we're already in some non-registered state, don't override that
687 if (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
688 registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) {
689 registration_state_ =
690 (searching ? MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING :
691 MM_MODEM_3GPP_REGISTRATION_STATE_IDLE);
692 }
693}
694
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400695void CellularCapabilityUniversal::RequirePIN(
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400696 const string &pin, bool require,
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400697 Error *error, const ResultCallback &callback) {
698 CHECK(error);
699 sim_proxy_->EnablePin(pin, require, error, callback, kTimeoutDefault);
700}
701
702void CellularCapabilityUniversal::EnterPIN(const string &pin,
703 Error *error,
704 const ResultCallback &callback) {
705 CHECK(error);
706 sim_proxy_->SendPin(pin, error, callback, kTimeoutDefault);
707}
708
709void CellularCapabilityUniversal::UnblockPIN(const string &unblock_code,
710 const string &pin,
711 Error *error,
712 const ResultCallback &callback) {
713 CHECK(error);
714 sim_proxy_->SendPuk(unblock_code, pin, error, callback, kTimeoutDefault);
715}
716
717void CellularCapabilityUniversal::ChangePIN(
718 const string &old_pin, const string &new_pin,
719 Error *error, const ResultCallback &callback) {
720 CHECK(error);
721 sim_proxy_->ChangePin(old_pin, new_pin, error, callback, kTimeoutDefault);
722}
723
724void CellularCapabilityUniversal::Scan(Error *error,
725 const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700726 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400727 CHECK(error);
Jason Glasgowcd0349c2012-05-03 23:32:15 -0400728 if (scanning_) {
729 Error::PopulateAndLog(error, Error::kInProgress, "Already scanning");
730 return;
731 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400732 DBusPropertyMapsCallback cb = Bind(&CellularCapabilityUniversal::OnScanReply,
733 weak_ptr_factory_.GetWeakPtr(), callback);
734 modem_3gpp_proxy_->Scan(error, cb, kTimeoutScan);
Jason Glasgowcd0349c2012-05-03 23:32:15 -0400735 if (!error->IsFailure()) {
736 scanning_ = true;
737 cellular()->adaptor()->EmitBoolChanged(flimflam::kScanningProperty,
738 scanning_);
739 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400740}
741
742void CellularCapabilityUniversal::OnScanReply(const ResultCallback &callback,
743 const ScanResults &results,
744 const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700745 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400746
747 // Error handling is weak. The current expectation is that on any
748 // error, found_networks_ should be cleared and a property change
749 // notification sent out.
750 //
751 // TODO(jglasgow): fix error handling
Jason Glasgowcd0349c2012-05-03 23:32:15 -0400752 scanning_ = false;
753 cellular()->adaptor()->EmitBoolChanged(flimflam::kScanningProperty,
754 scanning_);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400755 found_networks_.clear();
756 if (!error.IsFailure()) {
757 for (ScanResults::const_iterator it = results.begin();
758 it != results.end(); ++it) {
759 found_networks_.push_back(ParseScanResult(*it));
760 }
761 }
762 cellular()->adaptor()->EmitStringmapsChanged(flimflam::kFoundNetworksProperty,
763 found_networks_);
Gary Morainceba6aa2012-05-03 10:28:26 -0700764
765 // TODO(gmorain): This check for is_null() is a work-around because
766 // Cellular::Scan() passes a null callback. Instead: 1. Have Cellular::Scan()
767 // pass in a callback. 2. Have Cellular "own" the found_networks_ property
768 // 3. Have Cellular EmitStingMapsChanged() 4. Share the code between GSM and
769 // Universal.
770 if (!callback.is_null())
771 callback.Run(error);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400772}
773
774Stringmap CellularCapabilityUniversal::ParseScanResult(
775 const ScanResult &result) {
776
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400777 /* ScanResults contain the following keys:
778
779 "status"
780 A MMModem3gppNetworkAvailability value representing network
781 availability status, given as an unsigned integer (signature "u").
782 This key will always be present.
783
784 "operator-long"
785 Long-format name of operator, given as a string value (signature
786 "s"). If the name is unknown, this field should not be present.
787
788 "operator-short"
789 Short-format name of operator, given as a string value
790 (signature "s"). If the name is unknown, this field should not
791 be present.
792
793 "operator-code"
794 Mobile code of the operator, given as a string value (signature
795 "s"). Returned in the format "MCCMNC", where MCC is the
796 three-digit ITU E.212 Mobile Country Code and MNC is the two- or
797 three-digit GSM Mobile Network Code. e.g. "31026" or "310260".
798
799 "access-technology"
800 A MMModemAccessTechnology value representing the generic access
801 technology used by this mobile network, given as an unsigned
802 integer (signature "u").
803 */
804 Stringmap parsed;
805
806 uint32 status;
807 if (DBusProperties::GetUint32(result, kStatusProperty, &status)) {
808 // numerical values are taken from 3GPP TS 27.007 Section 7.3.
809 static const char * const kStatusString[] = {
810 "unknown", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN
811 "available", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE
812 "current", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT
813 "forbidden", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN
814 };
815 parsed[flimflam::kStatusProperty] = kStatusString[status];
816 }
817
818 uint32 tech; // MMModemAccessTechnology
819 if (DBusProperties::GetUint32(result, kOperatorAccessTechnologyProperty,
820 &tech)) {
821 parsed[flimflam::kTechnologyProperty] = AccessTechnologyToString(tech);
822 }
823
824 string operator_long, operator_short, operator_code;
825 if (DBusProperties::GetString(result, kOperatorLongProperty, &operator_long))
826 parsed[flimflam::kLongNameProperty] = operator_long;
827 if (DBusProperties::GetString(result, kOperatorShortProperty,
828 &operator_short))
829 parsed[flimflam::kShortNameProperty] = operator_short;
830 if (DBusProperties::GetString(result, kOperatorCodeProperty, &operator_code))
831 parsed[flimflam::kNetworkIdProperty] = operator_code;
832
833 // If the long name is not available but the network ID is, look up the long
834 // name in the mobile provider database.
835 if ((!ContainsKey(parsed, flimflam::kLongNameProperty) ||
836 parsed[flimflam::kLongNameProperty].empty()) &&
837 ContainsKey(parsed, flimflam::kNetworkIdProperty)) {
838 mobile_provider *provider =
839 mobile_provider_lookup_by_network(
840 cellular()->provider_db(),
841 parsed[flimflam::kNetworkIdProperty].c_str());
842 if (provider) {
843 const char *long_name = mobile_provider_get_name(provider);
844 if (long_name && *long_name) {
845 parsed[flimflam::kLongNameProperty] = long_name;
846 }
847 }
848 }
849 return parsed;
850}
851
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400852string CellularCapabilityUniversal::GetNetworkTechnologyString() const {
Jason Glasgow9f09aef2012-05-08 16:26:55 -0400853 // Order is important. Return the highest speed technology
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400854 // TODO(jglasgow): change shill interfaces to a capability model
855
856 return AccessTechnologyToString(access_technologies_);
857}
858
859string CellularCapabilityUniversal::GetRoamingStateString() const {
860 switch (registration_state_) {
861 case MM_MODEM_3GPP_REGISTRATION_STATE_HOME:
862 return flimflam::kRoamingStateHome;
863 case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING:
864 return flimflam::kRoamingStateRoaming;
865 default:
866 break;
867 }
868 return flimflam::kRoamingStateUnknown;
869}
870
871void CellularCapabilityUniversal::GetSignalQuality() {
Nathan Williams218cbcd2012-04-17 16:48:44 -0400872 // TODO(njw): Switch to asynchronous calls (crosbug.com/17583).
873 const DBus::Struct<unsigned int, bool> quality =
874 modem_proxy_->SignalQuality();
875 OnSignalQualityChanged(quality._1);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400876}
877
Jason Glasgow9f09aef2012-05-08 16:26:55 -0400878string CellularCapabilityUniversal::GetTypeString() const {
879 return AccessTechnologyToTechnologyFamily(access_technologies_);
880}
881
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400882void CellularCapabilityUniversal::OnModemPropertiesChanged(
883 const DBusPropertiesMap &properties,
884 const vector<string> &/* invalidated_properties */) {
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400885 // This solves a bootstrapping problem: If the modem is not yet
886 // enabled, there are no proxy objects associated with the capability
887 // object, so modem signals like StateChanged aren't seen. By monitoring
888 // changes to the State property via the ModemManager, we're able to
889 // get the initialization process started, which will result in the
890 // creation of the proxy objects.
891 //
892 // The first time we see the change to State (when the modem state
893 // is Unknown), we simply update the state, and rely on the Manager to
894 // enable the device when it is registered with the Manager. On subsequent
895 // changes to State, we need to explicitly enable the device ourselves.
896 int32 istate;
Jason Glasgowaf583282012-04-18 15:18:22 -0400897 if (DBusProperties::GetInt32(properties, MM_MODEM_PROPERTY_STATE, &istate)) {
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400898 Cellular::ModemState state = static_cast<Cellular::ModemState>(istate);
Jason Glasgowaf583282012-04-18 15:18:22 -0400899 OnModemStateChanged(state);
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400900 }
Jason Glasgowaf583282012-04-18 15:18:22 -0400901 string string_value;
Nathan Williamse9840802012-04-18 18:47:40 -0400902 if (DBusProperties::GetObjectPath(properties,
Jason Glasgowaf583282012-04-18 15:18:22 -0400903 MM_MODEM_PROPERTY_SIM, &string_value))
904 OnSimPathChanged(string_value);
905 uint32 uint_value;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400906 if (DBusProperties::GetUint32(properties,
Jason Glasgowaf583282012-04-18 15:18:22 -0400907 MM_MODEM_PROPERTY_MODEMCAPABILITIES,
908 &uint_value))
909 OnModemCapabilitesChanged(uint_value);
910 if (DBusProperties::GetUint32(properties,
911 MM_MODEM_PROPERTY_CURRENTCAPABILITIES,
912 &uint_value))
913 OnModemCurrentCapabilitiesChanged(uint_value);
914 // not needed: MM_MODEM_PROPERTY_MAXBEARERS
915 // not needed: MM_MODEM_PROPERTY_MAXACTIVEBEARERS
916 if (DBusProperties::GetString(properties,
917 MM_MODEM_PROPERTY_MANUFACTURER,
918 &string_value))
919 OnModemManufacturerChanged(string_value);
920 if (DBusProperties::GetString(properties,
921 MM_MODEM_PROPERTY_MODEL,
922 &string_value))
923 OnModemModelChanged(string_value);
924 if (DBusProperties::GetString(properties,
925 MM_MODEM_PROPERTY_REVISION,
926 &string_value))
927 OnModemRevisionChanged(string_value);
928 // not needed: MM_MODEM_PROPERTY_DEVICEIDENTIFIER
929 // not needed: MM_MODEM_PROPERTY_DEVICE
930 // not needed: MM_MODEM_PROPERTY_DRIVER
931 // not needed: MM_MODEM_PROPERTY_PLUGIN
932 // not needed: MM_MODEM_PROPERTY_EQUIPMENTIDENTIFIER
Nathan Williams218cbcd2012-04-17 16:48:44 -0400933
Jason Glasgowaf583282012-04-18 15:18:22 -0400934 // Unlock required and SimLock
935 bool locks_changed = false;
936 uint32_t unlock_required; // This is really of type MMModemLock
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400937 if (DBusProperties::GetUint32(properties,
938 MM_MODEM_PROPERTY_UNLOCKREQUIRED,
Jason Glasgowaf583282012-04-18 15:18:22 -0400939 &unlock_required)) {
940 locks_changed = true;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400941 }
Jason Glasgowaf583282012-04-18 15:18:22 -0400942 LockRetryData lock_retries;
943 DBusPropertiesMap::const_iterator it =
944 properties.find(MM_MODEM_PROPERTY_UNLOCKRETRIES);
945 if (it != properties.end()) {
946 lock_retries = it->second;
947 locks_changed = true;
948 }
949 if (locks_changed)
950 OnLockRetriesChanged(static_cast<MMModemLock>(unlock_required),
951 lock_retries);
952 if (DBusProperties::GetUint32(properties,
953 MM_MODEM_PROPERTY_ACCESSTECHNOLOGIES,
954 &uint_value))
955 OnAccessTechnologiesChanged(uint_value);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400956
Jason Glasgowaf583282012-04-18 15:18:22 -0400957 it = properties.find(MM_MODEM_PROPERTY_SIGNALQUALITY);
958 if (it != properties.end()) {
959 DBus::Struct<unsigned int, bool> quality = it->second;
960 OnSignalQualityChanged(quality._1);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400961 }
Jason Glasgowaf583282012-04-18 15:18:22 -0400962 vector<string> numbers;
963 if (DBusProperties::GetStrings(properties, MM_MODEM_PROPERTY_OWNNUMBERS,
964 &numbers)) {
965 string mdn;
966 if (numbers.size() > 0)
967 mdn = numbers[0];
968 OnMdnChanged(mdn);
969 }
970 if (DBusProperties::GetUint32(properties, MM_MODEM_PROPERTY_SUPPORTEDMODES,
971 &uint_value))
972 OnSupportedModesChanged(uint_value);
973 if (DBusProperties::GetUint32(properties, MM_MODEM_PROPERTY_ALLOWEDMODES,
974 &uint_value))
975 OnAllowedModesChanged(uint_value);
976 if (DBusProperties::GetUint32(properties, MM_MODEM_PROPERTY_PREFERREDMODE,
977 &uint_value))
978 OnPreferredModeChanged(static_cast<MMModemMode>(uint_value));
979 // au: MM_MODEM_PROPERTY_SUPPORTEDBANDS,
980 // au: MM_MODEM_PROPERTY_BANDS
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400981}
982
983void CellularCapabilityUniversal::OnDBusPropertiesChanged(
984 const string &interface,
985 const DBusPropertiesMap &changed_properties,
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400986 const vector<string> &invalidated_properties) {
Jason Glasgow7234ec32012-05-23 16:01:21 -0400987 SLOG(Cellular, 2) << __func__ << "(" << interface << ")";
Jason Glasgowef965562012-04-10 16:12:35 -0400988 if (interface == MM_DBUS_INTERFACE_MODEM) {
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400989 OnModemPropertiesChanged(changed_properties, invalidated_properties);
Jason Glasgowef965562012-04-10 16:12:35 -0400990 }
Jason Glasgowaf583282012-04-18 15:18:22 -0400991 if (interface == MM_DBUS_INTERFACE_MODEM_MODEM3GPP) {
992 OnModem3GPPPropertiesChanged(changed_properties, invalidated_properties);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400993 }
Jason Glasgowaf583282012-04-18 15:18:22 -0400994 if (interface == MM_DBUS_INTERFACE_SIM) {
995 OnSimPropertiesChanged(changed_properties, invalidated_properties);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400996 }
997}
998
Jason Glasgow14521872012-05-07 19:12:15 -0400999bool CellularCapabilityUniversal::RetriableConnectError(
1000 const Error &error) const {
1001 if (error.type() == Error::kInvalidApn)
1002 return true;
1003
1004 // modemmanager does not ever return kInvalidApn for E362 modems
1005 // with 1.41 firmware. It remains to be seem if this will change
1006 // with 3.x firmware.
1007 if ((model_id_ == kE362ModelId) && (error.type() == Error::kOperationFailed))
1008 return true;
1009
1010 return false;
1011}
1012
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001013void CellularCapabilityUniversal::OnNetworkModeSignal(uint32 /*mode*/) {
1014 // TODO(petkov): Implement this.
1015 NOTIMPLEMENTED();
1016}
1017
Jason Glasgowaf583282012-04-18 15:18:22 -04001018void CellularCapabilityUniversal::OnSimPathChanged(
1019 const string &sim_path) {
1020 if (sim_path == sim_path_)
1021 return;
1022
1023 mm1::SimProxyInterface *proxy = NULL;
1024 if (!sim_path.empty())
1025 proxy = proxy_factory()->CreateSimProxy(sim_path,
1026 cellular()->dbus_owner());
1027 sim_path_ = sim_path;
1028 sim_proxy_.reset(proxy);
1029
1030 if (sim_path.empty()) {
1031 // Clear all data about the sim
Jason Glasgow4380f0d2012-05-03 18:05:04 -04001032 imsi_ = "";
1033 spn_ = "";
Ben Chanbd3aee82012-10-16 23:52:04 -07001034 sim_present_ = false;
Jason Glasgowaf583282012-04-18 15:18:22 -04001035 OnSimIdentifierChanged("");
1036 OnOperatorIdChanged("");
Jason Glasgowaf583282012-04-18 15:18:22 -04001037 } else {
Ben Chanbd3aee82012-10-16 23:52:04 -07001038 sim_present_ = true;
Jason Glasgowaf583282012-04-18 15:18:22 -04001039 scoped_ptr<DBusPropertiesProxyInterface> properties_proxy(
1040 proxy_factory()->CreateDBusPropertiesProxy(sim_path,
1041 cellular()->dbus_owner()));
1042 // TODO(jglasgow): convert to async interface
1043 DBusPropertiesMap properties(
1044 properties_proxy->GetAll(MM_DBUS_INTERFACE_SIM));
1045 OnSimPropertiesChanged(properties, vector<string>());
1046 }
1047}
1048
1049void CellularCapabilityUniversal::OnModemCapabilitesChanged(
1050 uint32 capabilities) {
1051 capabilities_ = capabilities;
1052}
1053
1054void CellularCapabilityUniversal::OnModemCurrentCapabilitiesChanged(
1055 uint32 current_capabilities) {
1056 current_capabilities_ = current_capabilities;
1057}
1058
1059void CellularCapabilityUniversal::OnMdnChanged(
1060 const string &mdn) {
1061 mdn_ = mdn;
1062}
1063
1064void CellularCapabilityUniversal::OnModemManufacturerChanged(
1065 const string &manufacturer) {
1066 manufacturer_ = manufacturer;
1067}
1068
1069void CellularCapabilityUniversal::OnModemModelChanged(
1070 const string &model) {
1071 model_id_ = model;
1072}
1073
1074void CellularCapabilityUniversal::OnModemRevisionChanged(
1075 const string &revision) {
1076 firmware_revision_ = revision;
1077}
1078
1079void CellularCapabilityUniversal::OnModemStateChanged(
1080 Cellular::ModemState state) {
1081 Cellular::ModemState prev_modem_state = cellular()->modem_state();
1082 bool was_enabled = cellular()->IsUnderlyingDeviceEnabled();
1083 if (Cellular::IsEnabledModemState(state))
1084 cellular()->set_modem_state(state);
Gary Moraine285a842012-08-15 08:23:57 -07001085 SLOG(Cellular, 2) << __func__ << ": prev_modem_state: " << prev_modem_state
1086 << " was_enabled: " << was_enabled
1087 << " cellular state: "
1088 << cellular()->GetStateString(cellular()->state());
Jason Glasgowaf583282012-04-18 15:18:22 -04001089 if (prev_modem_state != Cellular::kModemStateUnknown &&
1090 prev_modem_state != Cellular::kModemStateEnabling &&
1091 !was_enabled &&
1092 cellular()->state() == Cellular::kStateDisabled &&
1093 cellular()->IsUnderlyingDeviceEnabled()) {
1094 cellular()->SetEnabled(true);
1095 }
1096}
1097
1098void CellularCapabilityUniversal::OnAccessTechnologiesChanged(
1099 uint32 access_technologies) {
Jason Glasgow9f09aef2012-05-08 16:26:55 -04001100 if (access_technologies_ != access_technologies) {
Jason Glasgowbad114b2012-05-21 15:24:16 -04001101 const string old_type_string(GetTypeString());
Jason Glasgow9f09aef2012-05-08 16:26:55 -04001102 access_technologies_ = access_technologies;
Jason Glasgowbad114b2012-05-21 15:24:16 -04001103 const string new_type_string(GetTypeString());
1104 if (new_type_string != old_type_string) {
1105 // TODO(jglasgow): address layering violation of emitting change
1106 // signal here for a property owned by Cellular.
1107 cellular()->adaptor()->EmitStringChanged(
1108 flimflam::kTechnologyFamilyProperty, new_type_string);
1109 }
Jason Glasgow9f09aef2012-05-08 16:26:55 -04001110 if (cellular()->service().get()) {
1111 cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
1112 }
Jason Glasgowaf583282012-04-18 15:18:22 -04001113 }
1114}
1115
1116void CellularCapabilityUniversal::OnSupportedModesChanged(
1117 uint32 supported_modes) {
1118 supported_modes_ = supported_modes;
1119}
1120
1121void CellularCapabilityUniversal::OnAllowedModesChanged(
1122 uint32 allowed_modes) {
1123 allowed_modes_ = allowed_modes;
1124}
1125
1126void CellularCapabilityUniversal::OnPreferredModeChanged(
1127 MMModemMode preferred_mode) {
1128 preferred_mode_ = preferred_mode;
1129}
1130
1131void CellularCapabilityUniversal::OnLockRetriesChanged(
1132 MMModemLock unlock_required,
1133 const LockRetryData &lock_retries) {
Jason Glasgowd4af81d2012-05-03 22:25:05 -04001134 switch (unlock_required) {
Jason Glasgowaf583282012-04-18 15:18:22 -04001135 case MM_MODEM_LOCK_SIM_PIN:
1136 sim_lock_status_.lock_type = "sim-pin";
1137 break;
1138 case MM_MODEM_LOCK_SIM_PUK:
1139 sim_lock_status_.lock_type = "sim-puk";
1140 break;
1141 default:
1142 sim_lock_status_.lock_type = "";
1143 break;
1144 }
1145 LockRetryData::const_iterator it = lock_retries.find(unlock_required);
1146 if (it != lock_retries.end()) {
1147 sim_lock_status_.retries_left = it->second;
1148 } else {
1149 // Unknown, use 999
1150 sim_lock_status_.retries_left = 999;
1151 }
1152 OnSimLockStatusChanged();
1153}
1154
1155void CellularCapabilityUniversal::OnSimLockStatusChanged() {
1156 cellular()->adaptor()->EmitKeyValueStoreChanged(
1157 flimflam::kSIMLockStatusProperty, SimLockStatusToProperty(NULL));
1158}
1159
1160void CellularCapabilityUniversal::OnModem3GPPPropertiesChanged(
1161 const DBusPropertiesMap &properties,
1162 const vector<string> &/* invalidated_properties */) {
1163 VLOG(2) << __func__;
1164 string imei;
1165 if (DBusProperties::GetString(properties,
1166 MM_MODEM_MODEM3GPP_PROPERTY_IMEI,
1167 &imei))
1168 OnImeiChanged(imei);
1169
1170 // Handle registration state changes as a single change
1171 string operator_code = serving_operator_.GetCode();
1172 string operator_name = serving_operator_.GetName();
1173 MMModem3gppRegistrationState state = registration_state_;
1174 bool registration_changed = false;
1175 uint32 uint_value;
1176 if (DBusProperties::GetUint32(properties,
1177 MM_MODEM_MODEM3GPP_PROPERTY_REGISTRATIONSTATE,
1178 &uint_value)) {
1179 state = static_cast<MMModem3gppRegistrationState>(uint_value);
1180 registration_changed = true;
1181 }
1182 if (DBusProperties::GetString(properties,
1183 MM_MODEM_MODEM3GPP_PROPERTY_OPERATORCODE,
1184 &operator_code))
1185 registration_changed = true;
1186 if (DBusProperties::GetString(properties,
1187 MM_MODEM_MODEM3GPP_PROPERTY_OPERATORNAME,
1188 &operator_name))
1189 registration_changed = true;
1190 if (registration_changed)
1191 On3GPPRegistrationChanged(state, operator_code, operator_name);
1192
1193 uint32 locks = 0;
1194 if (DBusProperties::GetUint32(
1195 properties, MM_MODEM_MODEM3GPP_PROPERTY_ENABLEDFACILITYLOCKS,
1196 &locks))
1197 OnFacilityLocksChanged(locks);
1198}
1199
1200void CellularCapabilityUniversal::OnImeiChanged(const string &imei) {
1201 imei_ = imei;
1202}
1203
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001204void CellularCapabilityUniversal::On3GPPRegistrationChanged(
1205 MMModem3gppRegistrationState state,
1206 const string &operator_code,
1207 const string &operator_name) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001208 SLOG(Cellular, 2) << __func__ << ": regstate=" << state
1209 << ", opercode=" << operator_code
1210 << ", opername=" << operator_name;
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001211 registration_state_ = state;
1212 serving_operator_.SetCode(operator_code);
1213 serving_operator_.SetName(operator_name);
1214 UpdateOperatorInfo();
1215 cellular()->HandleNewRegistrationState();
1216}
1217
Eric Shienbrood7fce52c2012-04-13 19:11:02 -04001218void CellularCapabilityUniversal::OnModemStateChangedSignal(
1219 int32 old_state, int32 new_state, uint32 reason) {
Eric Shienbrood9b1bd7b2012-04-20 16:42:26 -04001220 SLOG(Cellular, 2) << __func__ << "(" << old_state << ", " << new_state << ", "
1221 << reason << ")";
Eric Shienbrood7fce52c2012-04-13 19:11:02 -04001222 cellular()->OnModemStateChanged(static_cast<Cellular::ModemState>(old_state),
1223 static_cast<Cellular::ModemState>(new_state),
1224 reason);
Gary Moraine285a842012-08-15 08:23:57 -07001225 if (!deferred_enable_modem_callback_.is_null() &&
1226 (new_state == Cellular::kModemStateDisabled)) {
1227 SLOG(Cellular, 2) << "Enabling modem after deferring";
1228 deferred_enable_modem_callback_.Run();
1229 deferred_enable_modem_callback_.Reset();
1230 }
Eric Shienbrood7fce52c2012-04-13 19:11:02 -04001231}
1232
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001233void CellularCapabilityUniversal::OnSignalQualityChanged(uint32 quality) {
1234 cellular()->HandleNewSignalQuality(quality);
1235}
1236
Jason Glasgowaf583282012-04-18 15:18:22 -04001237void CellularCapabilityUniversal::OnFacilityLocksChanged(uint32 locks) {
1238 if (sim_lock_status_.enabled != (locks & MM_MODEM_3GPP_FACILITY_SIM)) {
1239 sim_lock_status_.enabled = locks & MM_MODEM_3GPP_FACILITY_SIM;
1240 OnSimLockStatusChanged();
1241 }
1242}
Jason Glasgowef965562012-04-10 16:12:35 -04001243
Jason Glasgowaf583282012-04-18 15:18:22 -04001244void CellularCapabilityUniversal::OnSimPropertiesChanged(
1245 const DBusPropertiesMap &props,
1246 const vector<string> &/* invalidated_properties */) {
1247 VLOG(2) << __func__;
1248 string value;
Jason Glasgow4380f0d2012-05-03 18:05:04 -04001249 bool must_update_home_provider = false;
Jason Glasgowaf583282012-04-18 15:18:22 -04001250 if (DBusProperties::GetString(props, MM_SIM_PROPERTY_SIMIDENTIFIER, &value))
1251 OnSimIdentifierChanged(value);
1252 if (DBusProperties::GetString(props, MM_SIM_PROPERTY_OPERATORIDENTIFIER,
1253 &value))
1254 OnOperatorIdChanged(value);
Jason Glasgow4380f0d2012-05-03 18:05:04 -04001255 if (DBusProperties::GetString(props, MM_SIM_PROPERTY_OPERATORNAME, &value)) {
1256 spn_ = value;
1257 must_update_home_provider = true;
1258 }
1259 if (DBusProperties::GetString(props, MM_SIM_PROPERTY_IMSI, &value)) {
1260 imsi_ = value;
1261 must_update_home_provider = true;
1262 }
Jason Glasgowaf583282012-04-18 15:18:22 -04001263 // TODO(jglasgow): May eventually want to get SPDI, etc
Jason Glasgowaf583282012-04-18 15:18:22 -04001264
Jason Glasgow4380f0d2012-05-03 18:05:04 -04001265 if (must_update_home_provider)
1266 SetHomeProvider();
Jason Glasgowaf583282012-04-18 15:18:22 -04001267}
1268
1269void CellularCapabilityUniversal::OnSimIdentifierChanged(const string &id) {
1270 sim_identifier_ = id;
1271}
1272
1273void CellularCapabilityUniversal::OnOperatorIdChanged(
1274 const string &operator_id) {
1275 operator_id_ = operator_id;
1276}
1277
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001278} // namespace shill