blob: 6bb186018c148201d125e798b65287870a071f4a [file] [log] [blame]
Arman Uguray72fab6a2013-01-10 19:32:42 -08001// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
Jason Glasgow82f9ab32012-04-04 14:27:19 -04002// 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>
Ben Chan6d0d1e72012-11-06 21:19:28 -080010#include <base/string_util.h>
Jason Glasgow82f9ab32012-04-04 14:27:19 -040011#include <chromeos/dbus/service_constants.h>
12#include <mobile_provider.h>
Ben Chan5c853ef2012-10-05 00:05:37 -070013#include <ModemManager/ModemManager.h>
Jason Glasgow82f9ab32012-04-04 14:27:19 -040014
15#include <string>
16#include <vector>
17
18#include "shill/adaptor_interfaces.h"
Ben Chan15786032012-11-04 21:28:02 -080019#include "shill/cellular_operator_info.h"
Jason Glasgow82f9ab32012-04-04 14:27:19 -040020#include "shill/cellular_service.h"
Jason Glasgowaf583282012-04-18 15:18:22 -040021#include "shill/dbus_properties_proxy_interface.h"
Jason Glasgow82f9ab32012-04-04 14:27:19 -040022#include "shill/error.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070023#include "shill/logging.h"
Arman Uguray41cc6342013-03-29 16:34:39 -070024#include "shill/pending_activation_store.h"
Jason Glasgow82f9ab32012-04-04 14:27:19 -040025#include "shill/property_accessor.h"
26#include "shill/proxy_factory.h"
27
28#ifdef MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN
29#error "Do not include mm-modem.h"
30#endif
31
Jason Glasgow82f9ab32012-04-04 14:27:19 -040032using base::Bind;
Jason Glasgowef965562012-04-10 16:12:35 -040033using base::Closure;
Jason Glasgow82f9ab32012-04-04 14:27:19 -040034using std::string;
35using std::vector;
36
37namespace shill {
38
39// static
Jason Glasgow14521872012-05-07 19:12:15 -040040const char CellularCapabilityUniversal::kConnectPin[] = "pin";
41const char CellularCapabilityUniversal::kConnectOperatorId[] = "operator-id";
Jason Glasgow14521872012-05-07 19:12:15 -040042const char CellularCapabilityUniversal::kConnectApn[] = "apn";
43const char CellularCapabilityUniversal::kConnectIPType[] = "ip-type";
44const char CellularCapabilityUniversal::kConnectUser[] = "user";
45const char CellularCapabilityUniversal::kConnectPassword[] = "password";
46const char CellularCapabilityUniversal::kConnectNumber[] = "number";
47const char CellularCapabilityUniversal::kConnectAllowRoaming[] =
48 "allow-roaming";
49const char CellularCapabilityUniversal::kConnectRMProtocol[] = "rm-protocol";
Arman Uguraya14941d2013-04-12 16:58:26 -070050const int64
Prathmesh Prabhu8f6479f2013-05-15 12:56:13 -070051CellularCapabilityUniversal::kActivationRegistrationTimeoutMilliseconds =
52 20000;
53const int64
Arman Uguray1361c032013-02-11 17:53:39 -080054CellularCapabilityUniversal::kDefaultScanningOrSearchingTimeoutMilliseconds =
55 60000;
Arman Ugurayf6366ac2013-06-12 16:02:28 -070056const int64 CellularCapabilityUniversal::kEnterPinTimeoutMilliseconds = 20000;
Arman Uguraya14941d2013-04-12 16:58:26 -070057const int64
Prathmesh Prabhu8f6479f2013-05-15 12:56:13 -070058CellularCapabilityUniversal::kRegistrationDroppedUpdateTimeoutMilliseconds =
59 15000;
Arman Uguray2717a102013-01-29 23:36:06 -080060const char CellularCapabilityUniversal::kGenericServiceNamePrefix[] =
61 "Mobile Network";
Arman Uguray6552f8c2013-02-12 15:33:18 -080062const char CellularCapabilityUniversal::kRootPath[] = "/";
Jason Glasgowcd0349c2012-05-03 23:32:15 -040063const char CellularCapabilityUniversal::kStatusProperty[] = "status";
64const char CellularCapabilityUniversal::kOperatorLongProperty[] =
65 "operator-long";
66const char CellularCapabilityUniversal::kOperatorShortProperty[] =
67 "operator-short";
68const char CellularCapabilityUniversal::kOperatorCodeProperty[] =
69 "operator-code";
70const char CellularCapabilityUniversal::kOperatorAccessTechnologyProperty[] =
71 "access-technology";
mukesh agrawal9da07772013-05-15 14:15:17 -070072const char CellularCapabilityUniversal::kIpConfigPropertyMethod[] = "method";
Jason Glasgow14521872012-05-07 19:12:15 -040073const char CellularCapabilityUniversal::kE362ModelId[] = "E362 WWAN";
Thieu Le2cac2942013-03-05 18:41:08 -080074const int CellularCapabilityUniversal::kSetPowerStateTimeoutMilliseconds =
75 20000;
Jason Glasgow14521872012-05-07 19:12:15 -040076unsigned int CellularCapabilityUniversal::friendly_service_name_id_ = 0;
77
Jason Glasgow82f9ab32012-04-04 14:27:19 -040078
79static const char kPhoneNumber[] = "*99#";
80
81static string AccessTechnologyToString(uint32 access_technologies) {
82 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_LTE)
83 return flimflam::kNetworkTechnologyLte;
84 if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 |
85 MM_MODEM_ACCESS_TECHNOLOGY_EVDOA |
86 MM_MODEM_ACCESS_TECHNOLOGY_EVDOB))
87 return flimflam::kNetworkTechnologyEvdo;
88 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_1XRTT)
89 return flimflam::kNetworkTechnology1Xrtt;
90 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS)
91 return flimflam::kNetworkTechnologyHspaPlus;
92 if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_HSPA |
93 MM_MODEM_ACCESS_TECHNOLOGY_HSUPA |
94 MM_MODEM_ACCESS_TECHNOLOGY_HSDPA))
95 return flimflam::kNetworkTechnologyHspa;
96 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_UMTS)
97 return flimflam::kNetworkTechnologyUmts;
98 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_EDGE)
99 return flimflam::kNetworkTechnologyEdge;
100 if (access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_GPRS)
101 return flimflam::kNetworkTechnologyGprs;
102 if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT |
103 MM_MODEM_ACCESS_TECHNOLOGY_GSM))
104 return flimflam::kNetworkTechnologyGsm;
105 return "";
106}
107
Jason Glasgow9f09aef2012-05-08 16:26:55 -0400108static string AccessTechnologyToTechnologyFamily(uint32 access_technologies) {
109 if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_LTE |
110 MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS |
111 MM_MODEM_ACCESS_TECHNOLOGY_HSPA |
112 MM_MODEM_ACCESS_TECHNOLOGY_HSUPA |
113 MM_MODEM_ACCESS_TECHNOLOGY_HSDPA |
114 MM_MODEM_ACCESS_TECHNOLOGY_UMTS |
115 MM_MODEM_ACCESS_TECHNOLOGY_EDGE |
116 MM_MODEM_ACCESS_TECHNOLOGY_GPRS |
117 MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT |
118 MM_MODEM_ACCESS_TECHNOLOGY_GSM))
119 return flimflam::kTechnologyFamilyGsm;
120 if (access_technologies & (MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 |
Ben Chan6d0d1e72012-11-06 21:19:28 -0800121 MM_MODEM_ACCESS_TECHNOLOGY_EVDOA |
122 MM_MODEM_ACCESS_TECHNOLOGY_EVDOB |
Jason Glasgow9f09aef2012-05-08 16:26:55 -0400123 MM_MODEM_ACCESS_TECHNOLOGY_1XRTT))
124 return flimflam::kTechnologyFamilyCdma;
125 return "";
126}
127
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400128CellularCapabilityUniversal::CellularCapabilityUniversal(
129 Cellular *cellular,
Thieu Lece4483e2013-01-23 15:12:03 -0800130 ProxyFactory *proxy_factory,
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700131 ModemInfo *modem_info)
132 : CellularCapability(cellular, proxy_factory, modem_info),
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400133 weak_ptr_factory_(this),
134 registration_state_(MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN),
Darin Petkove636c692012-05-31 10:22:17 +0200135 current_capabilities_(MM_MODEM_CAPABILITY_NONE),
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400136 access_technologies_(MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN),
137 home_provider_(NULL),
Darin Petkovf508c822012-09-21 13:43:17 +0200138 provider_requires_roaming_(false),
Ben Chan5d0d32c2013-01-08 02:05:29 -0800139 resetting_(false),
Ben Chanfcca27b2013-01-22 15:03:44 -0800140 scanning_supported_(false),
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400141 scanning_(false),
Ben Chan8a2c01e2013-01-23 10:09:14 -0800142 scanning_or_searching_(false),
Ben Chanbd3aee82012-10-16 23:52:04 -0700143 scan_interval_(0),
Arman Uguray1361c032013-02-11 17:53:39 -0800144 sim_present_(false),
Arman Ugurayc7b15602013-02-16 00:56:18 -0800145 reset_done_(false),
Arman Uguray1361c032013-02-11 17:53:39 -0800146 scanning_or_searching_timeout_milliseconds_(
Arman Uguraya14941d2013-04-12 16:58:26 -0700147 kDefaultScanningOrSearchingTimeoutMilliseconds),
148 activation_registration_timeout_milliseconds_(
Prathmesh Prabhu8f6479f2013-05-15 12:56:13 -0700149 kActivationRegistrationTimeoutMilliseconds),
150 registration_dropped_update_timeout_milliseconds_(
151 kRegistrationDroppedUpdateTimeoutMilliseconds) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700152 SLOG(Cellular, 2) << "Cellular capability constructed: Universal";
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400153 PropertyStore *store = cellular->mutable_store();
154
155 store->RegisterConstString(flimflam::kCarrierProperty, &carrier_);
156 store->RegisterConstBool(flimflam::kSupportNetworkScanProperty,
157 &scanning_supported_);
158 store->RegisterConstString(flimflam::kEsnProperty, &esn_);
159 store->RegisterConstString(flimflam::kFirmwareRevisionProperty,
160 &firmware_revision_);
161 store->RegisterConstString(flimflam::kHardwareRevisionProperty,
162 &hardware_revision_);
163 store->RegisterConstString(flimflam::kImeiProperty, &imei_);
164 store->RegisterConstString(flimflam::kImsiProperty, &imsi_);
Ben Chana5e27082012-07-31 14:30:28 -0700165 store->RegisterConstString(flimflam::kIccidProperty, &sim_identifier_);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400166 store->RegisterConstString(flimflam::kManufacturerProperty, &manufacturer_);
167 store->RegisterConstString(flimflam::kMdnProperty, &mdn_);
168 store->RegisterConstString(flimflam::kMeidProperty, &meid_);
169 store->RegisterConstString(flimflam::kMinProperty, &min_);
170 store->RegisterConstString(flimflam::kModelIDProperty, &model_id_);
171 store->RegisterConstString(flimflam::kSelectedNetworkProperty,
172 &selected_network_);
173 store->RegisterConstStringmaps(flimflam::kFoundNetworksProperty,
174 &found_networks_);
Darin Petkovf508c822012-09-21 13:43:17 +0200175 store->RegisterConstBool(shill::kProviderRequiresRoamingProperty,
176 &provider_requires_roaming_);
Ben Chan8a2c01e2013-01-23 10:09:14 -0800177 store->RegisterConstBool(flimflam::kScanningProperty,
178 &scanning_or_searching_);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400179 store->RegisterUint16(flimflam::kScanIntervalProperty, &scan_interval_);
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700180 HelpRegisterConstDerivedKeyValueStore(
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400181 flimflam::kSIMLockStatusProperty,
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700182 &CellularCapabilityUniversal::SimLockStatusToProperty);
Thieu Le550247c2013-04-30 17:16:43 -0700183 store->RegisterConstString(shill::kSIMOperatorIdProperty, &operator_id_);
Ben Chanbd3aee82012-10-16 23:52:04 -0700184 store->RegisterConstBool(shill::kSIMPresentProperty, &sim_present_);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400185 store->RegisterConstStringmaps(flimflam::kCellularApnListProperty,
186 &apn_list_);
187}
188
189KeyValueStore CellularCapabilityUniversal::SimLockStatusToProperty(
190 Error */*error*/) {
191 KeyValueStore status;
Arman Ugurayc7e63af2013-06-13 17:07:32 -0700192 string lock_type;
193 switch (sim_lock_status_.lock_type) {
194 case MM_MODEM_LOCK_SIM_PIN:
195 lock_type = "sim-pin";
196 break;
197 case MM_MODEM_LOCK_SIM_PUK:
198 lock_type = "sim-puk";
199 break;
200 default:
201 lock_type = "";
202 break;
203 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400204 status.SetBool(flimflam::kSIMLockEnabledProperty, sim_lock_status_.enabled);
Arman Ugurayc7e63af2013-06-13 17:07:32 -0700205 status.SetString(flimflam::kSIMLockTypeProperty, lock_type);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400206 status.SetUint(flimflam::kSIMLockRetriesLeftProperty,
207 sim_lock_status_.retries_left);
208 return status;
209}
210
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700211void CellularCapabilityUniversal::HelpRegisterConstDerivedKeyValueStore(
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400212 const string &name,
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700213 KeyValueStore(CellularCapabilityUniversal::*get)(Error *error)) {
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400214 cellular()->mutable_store()->RegisterDerivedKeyValueStore(
215 name,
216 KeyValueStoreAccessor(
217 new CustomAccessor<CellularCapabilityUniversal, KeyValueStore>(
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700218 this, get, NULL)));
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400219}
220
221void CellularCapabilityUniversal::InitProxies() {
222 modem_3gpp_proxy_.reset(
223 proxy_factory()->CreateMM1ModemModem3gppProxy(cellular()->dbus_path(),
224 cellular()->dbus_owner()));
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400225 modem_proxy_.reset(
226 proxy_factory()->CreateMM1ModemProxy(cellular()->dbus_path(),
227 cellular()->dbus_owner()));
228 modem_simple_proxy_.reset(
229 proxy_factory()->CreateMM1ModemSimpleProxy(cellular()->dbus_path(),
230 cellular()->dbus_owner()));
Arman Uguray6e5639f2012-11-15 20:30:19 -0800231
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400232 modem_proxy_->set_state_changed_callback(
233 Bind(&CellularCapabilityUniversal::OnModemStateChangedSignal,
234 weak_ptr_factory_.GetWeakPtr()));
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400235 // Do not create a SIM proxy until the device is enabled because we
236 // do not yet know the object path of the sim object.
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400237 // TODO(jglasgow): register callbacks
238}
239
240void CellularCapabilityUniversal::StartModem(Error *error,
Jason Glasgowef965562012-04-10 16:12:35 -0400241 const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700242 SLOG(Cellular, 2) << __func__;
Jason Glasgowef965562012-04-10 16:12:35 -0400243 InitProxies();
244
Gary Moraine285a842012-08-15 08:23:57 -0700245 // ModemManager must be in the disabled state to accept the Enable command.
246 Cellular::ModemState state =
247 static_cast<Cellular::ModemState>(modem_proxy_->State());
248 if (state == Cellular::kModemStateDisabled) {
249 EnableModem(error, callback);
250 } else if (!cellular()->IsUnderlyingDeviceEnabled()) {
251 SLOG(Cellular, 2) << "Enabling of modem deferred because state is "
252 << state;
253 deferred_enable_modem_callback_ =
254 Bind(&CellularCapabilityUniversal::EnableModem,
255 weak_ptr_factory_.GetWeakPtr(), static_cast<Error *>(NULL),
256 callback);
257 } else {
Thieu Leb9c05e02013-03-04 14:09:32 -0800258 // This request cannot be completed synchronously here because a method
259 // reply requires a continuation tag that is not created until the message
260 // handler returns, see DBus-C++ ObjectAdapter::handle_message().
261 cellular()->dispatcher()->PostTask(
262 Bind(&CellularCapabilityUniversal::Start_ModemAlreadyEnabled,
263 weak_ptr_factory_.GetWeakPtr(), callback));
Gary Moraine285a842012-08-15 08:23:57 -0700264 }
265}
266
267void CellularCapabilityUniversal::EnableModem(Error *error,
268 const ResultCallback &callback) {
269 SLOG(Cellular, 2) << __func__;
Jason Glasgowef965562012-04-10 16:12:35 -0400270 CHECK(!callback.is_null());
Thieu Lee3b36592012-08-30 17:50:26 -0700271 Error local_error(Error::kOperationInitiated);
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700272 modem_info()->metrics()->NotifyDeviceEnableStarted(
273 cellular()->interface_index());
Jason Glasgowef965562012-04-10 16:12:35 -0400274 modem_proxy_->Enable(
275 true,
Gary Moraine285a842012-08-15 08:23:57 -0700276 &local_error,
Jason Glasgowef965562012-04-10 16:12:35 -0400277 Bind(&CellularCapabilityUniversal::Start_EnableModemCompleted,
278 weak_ptr_factory_.GetWeakPtr(), callback),
279 kTimeoutEnable);
Gary Moraine285a842012-08-15 08:23:57 -0700280 if (local_error.IsFailure()) {
281 SLOG(Cellular, 2) << __func__ << "Call to modem_proxy_->Enable() failed";
Gary Moraine285a842012-08-15 08:23:57 -0700282 }
283 if (error) {
284 error->CopyFrom(local_error);
285 }
Jason Glasgowef965562012-04-10 16:12:35 -0400286}
287
Thieu Leb9c05e02013-03-04 14:09:32 -0800288void CellularCapabilityUniversal::Start_ModemAlreadyEnabled(
289 const ResultCallback &callback) {
290 // Call GetProperties() here to sync up with the modem state
291 GetProperties();
292 callback.Run(Error());
293}
294
Jason Glasgowef965562012-04-10 16:12:35 -0400295void CellularCapabilityUniversal::Start_EnableModemCompleted(
296 const ResultCallback &callback, const Error &error) {
Eric Shienbrood9b1bd7b2012-04-20 16:42:26 -0400297 SLOG(Cellular, 2) << __func__ << ": " << error;
Jason Glasgowef965562012-04-10 16:12:35 -0400298 if (error.IsFailure()) {
299 callback.Run(error);
300 return;
301 }
302
303 // After modem is enabled, it should be possible to get properties
304 // TODO(jglasgow): handle errors from GetProperties
305 GetProperties();
Thieu Le18c11072013-01-28 17:21:37 -0800306 // We expect the modem to start scanning after it has been enabled.
307 // Change this if this behavior is no longer the case in the future.
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700308 modem_info()->metrics()->NotifyDeviceEnableFinished(
309 cellular()->interface_index());
310 modem_info()->metrics()->NotifyDeviceScanStarted(
311 cellular()->interface_index());
Thieu Le18c11072013-01-28 17:21:37 -0800312 callback.Run(error);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400313}
314
315void CellularCapabilityUniversal::StopModem(Error *error,
Jason Glasgow02401cc2012-05-16 10:35:37 -0400316 const ResultCallback &callback) {
Jason Glasgowef965562012-04-10 16:12:35 -0400317 CHECK(!callback.is_null());
318 CHECK(error);
Prathmesh Prabhu8f6479f2013-05-15 12:56:13 -0700319 // If there is an outstanding registration change, simply ignore it since
320 // the service will be destroyed anyway.
321 if (!registration_dropped_update_callback_.IsCancelled()) {
322 registration_dropped_update_callback_.Cancel();
323 SLOG(Cellular, 2) << __func__ << " Cancelled delayed deregister.";
324 }
325
Thieu Led0012052012-07-25 16:09:09 -0700326 Cellular::ModemState state = cellular()->modem_state();
327 SLOG(Cellular, 2) << __func__ << "(" << state << ")";
Jason Glasgowef965562012-04-10 16:12:35 -0400328
Thieu Led0012052012-07-25 16:09:09 -0700329 if (cellular()->IsModemRegistered()) {
Jason Glasgow02401cc2012-05-16 10:35:37 -0400330 string all_bearers("/"); // "/" means all bearers. See Modemanager docs.
Jason Glasgowef965562012-04-10 16:12:35 -0400331 modem_simple_proxy_->Disconnect(
332 all_bearers,
333 error,
334 Bind(&CellularCapabilityUniversal::Stop_DisconnectCompleted,
335 weak_ptr_factory_.GetWeakPtr(), callback),
Thieu Le049adb52012-11-12 17:14:51 -0800336 kTimeoutDisconnect);
Jason Glasgowef965562012-04-10 16:12:35 -0400337 if (error->IsFailure())
338 callback.Run(*error);
339 } else {
Jason Glasgowef965562012-04-10 16:12:35 -0400340 Closure task = Bind(&CellularCapabilityUniversal::Stop_Disable,
341 weak_ptr_factory_.GetWeakPtr(),
342 callback);
343 cellular()->dispatcher()->PostTask(task);
344 }
Gary Moraine285a842012-08-15 08:23:57 -0700345 deferred_enable_modem_callback_.Reset();
Jason Glasgowef965562012-04-10 16:12:35 -0400346}
347
348void CellularCapabilityUniversal::Stop_DisconnectCompleted(
349 const ResultCallback &callback, const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700350 SLOG(Cellular, 2) << __func__;
Jason Glasgowef965562012-04-10 16:12:35 -0400351
352 LOG_IF(ERROR, error.IsFailure()) << "Disconnect failed. Ignoring.";
353 Stop_Disable(callback);
354}
355
356void CellularCapabilityUniversal::Stop_Disable(const ResultCallback &callback) {
357 Error error;
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700358 modem_info()->metrics()->NotifyDeviceDisableStarted(
359 cellular()->interface_index());
Jason Glasgowef965562012-04-10 16:12:35 -0400360 modem_proxy_->Enable(
361 false, &error,
362 Bind(&CellularCapabilityUniversal::Stop_DisableCompleted,
363 weak_ptr_factory_.GetWeakPtr(), callback),
Jason Glasgow02401cc2012-05-16 10:35:37 -0400364 kTimeoutEnable);
Jason Glasgowef965562012-04-10 16:12:35 -0400365 if (error.IsFailure())
366 callback.Run(error);
367}
368
369void CellularCapabilityUniversal::Stop_DisableCompleted(
370 const ResultCallback &callback, const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700371 SLOG(Cellular, 2) << __func__;
Jason Glasgowef965562012-04-10 16:12:35 -0400372
Thieu Lea2519bf2013-01-23 16:51:54 -0800373 if (error.IsSuccess()) {
Arman Ugurayee464d32013-02-13 17:14:36 -0800374 // The modem has been successfully disabled, but we still need to power it
375 // down.
376 Stop_PowerDown(callback);
377 } else {
378 // An error occurred; terminate the disable sequence.
379 callback.Run(error);
Thieu Lea2519bf2013-01-23 16:51:54 -0800380 }
Arman Ugurayee464d32013-02-13 17:14:36 -0800381}
382
383void CellularCapabilityUniversal::Stop_PowerDown(
384 const ResultCallback &callback) {
385 SLOG(Cellular, 2) << __func__;
386 Error error;
387 modem_proxy_->SetPowerState(
388 MM_MODEM_POWER_STATE_LOW,
389 &error,
390 Bind(&CellularCapabilityUniversal::Stop_PowerDownCompleted,
391 weak_ptr_factory_.GetWeakPtr(), callback),
Thieu Le2cac2942013-03-05 18:41:08 -0800392 kSetPowerStateTimeoutMilliseconds);
Arman Ugurayee464d32013-02-13 17:14:36 -0800393
394 if (error.IsFailure())
395 // This really shouldn't happen, but if it does, report success,
396 // because a stop initiated power down is only called if the
397 // modem was successfully disabled, but the failure of this
398 // operation should still be propagated up as a successful disable.
399 Stop_PowerDownCompleted(callback, error);
400}
401
402void CellularCapabilityUniversal::Stop_PowerDownCompleted(
403 const ResultCallback &callback,
404 const Error &error) {
405 SLOG(Cellular, 2) << __func__;
406
407 if (error.IsFailure())
408 SLOG(Cellular, 2) << "Ignoring error returned by SetPowerState: " << error;
409
410 // Since the disable succeeded, if power down fails, we currently fail
411 // silently, i.e. we need to report the disable operation as having
412 // succeeded.
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700413 modem_info()->metrics()->NotifyDeviceDisableFinished(
414 cellular()->interface_index());
Arman Ugurayee464d32013-02-13 17:14:36 -0800415 ReleaseProxies();
416 callback.Run(Error());
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400417}
418
419void CellularCapabilityUniversal::Connect(const DBusPropertiesMap &properties,
420 Error *error,
421 const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700422 SLOG(Cellular, 2) << __func__;
Nathan Williamsb54974f2012-04-19 11:16:30 -0400423 DBusPathCallback cb = Bind(&CellularCapabilityUniversal::OnConnectReply,
424 weak_ptr_factory_.GetWeakPtr(),
425 callback);
426 modem_simple_proxy_->Connect(properties, error, cb, kTimeoutConnect);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400427}
428
429void CellularCapabilityUniversal::Disconnect(Error *error,
430 const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700431 SLOG(Cellular, 2) << __func__;
Prathmesh Prabhu8f6479f2013-05-15 12:56:13 -0700432 // If a deferred registration loss request exists, process it.
433 if (!registration_dropped_update_callback_.IsCancelled()) {
434 registration_dropped_update_callback_.callback().Run();
435 DCHECK(cellular()->state() != Cellular::kStateConnected &&
436 cellular()->state() != Cellular::kStateLinked);
437 SLOG(Cellular, 1) << "Processed deferred registration loss before "
438 << "disconnect request.";
439 }
Thieu Le5d6864a2012-07-20 11:43:51 -0700440 if (bearer_path_.empty()) {
441 LOG(WARNING) << "In " << __func__ << "(): "
442 << "Ignoring attempt to disconnect without bearer";
Thieu Le3d275392012-07-20 15:32:58 -0700443 } else if (modem_simple_proxy_.get()) {
Thieu Le5d6864a2012-07-20 11:43:51 -0700444 modem_simple_proxy_->Disconnect(bearer_path_,
445 error,
446 callback,
Thieu Le049adb52012-11-12 17:14:51 -0800447 kTimeoutDisconnect);
Thieu Le5d6864a2012-07-20 11:43:51 -0700448 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400449}
450
Christopher Wiley8a468902012-11-30 11:52:38 -0800451void CellularCapabilityUniversal::DisconnectCleanup() {
452 SLOG(Cellular, 2) << __func__;
453}
454
Jason Glasgow4c0724a2012-04-17 15:47:40 -0400455void CellularCapabilityUniversal::Activate(const string &carrier,
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400456 Error *error,
457 const ResultCallback &callback) {
458 OnUnsupportedOperation(__func__, error);
459}
460
Arman Uguraya14941d2013-04-12 16:58:26 -0700461void CellularCapabilityUniversal::OnActivationWaitForRegisterTimeout() {
462 SLOG(Cellular, 2) << __func__;
463 if (sim_identifier_.empty() ||
Arman Uguray41cc6342013-03-29 16:34:39 -0700464 modem_info()->pending_activation_store()->GetActivationState(
465 PendingActivationStore::kIdentifierICCID,
466 sim_identifier_) == PendingActivationStore::kStateActivated) {
Arman Uguraya14941d2013-04-12 16:58:26 -0700467 SLOG(Cellular, 2) << "Modem is already scheduled to be reset.";
468 return;
469 }
470 if (IsMdnValid()) {
471 SLOG(Cellular, 2) << "MDN is valid. Already activated.";
472 return;
473 }
474 if (reset_done_) {
475 SLOG(Cellular, 2) << "Already done with reset.";
476 return;
477 }
478
479 // Still not activated after timeout. Reset the modem.
480 SLOG(Cellular, 2) << "Still not registered after timeout. Reset directly "
481 << "to update MDN.";
Arman Uguray41cc6342013-03-29 16:34:39 -0700482 modem_info()->pending_activation_store()->SetActivationState(
483 PendingActivationStore::kIdentifierICCID,
Arman Uguraya14941d2013-04-12 16:58:26 -0700484 sim_identifier_,
Arman Uguray41cc6342013-03-29 16:34:39 -0700485 PendingActivationStore::kStatePendingTimeout);
Arman Uguraya14941d2013-04-12 16:58:26 -0700486 ResetAfterActivation();
487}
488
Arman Ugurayc7b15602013-02-16 00:56:18 -0800489void CellularCapabilityUniversal::CompleteActivation(Error *error) {
490 SLOG(Cellular, 2) << __func__;
491
492 // Persist the ICCID as "Pending Activation".
493 // We're assuming that when this function gets called, |sim_identifier_| will
494 // be non-empty. We still check here that is non-empty, though something is
495 // wrong if it is empty.
496 if (IsMdnValid()) {
497 SLOG(Cellular, 2) << "Already acquired a valid MDN. Already activated.";
498 return;
499 }
Arman Ugurayefea6e02013-02-21 13:28:04 -0800500
Arman Ugurayc7b15602013-02-16 00:56:18 -0800501 if (sim_identifier_.empty()) {
502 SLOG(Cellular, 2) << "SIM identifier not available. Nothing to do.";
503 return;
504 }
Arman Ugurayefea6e02013-02-21 13:28:04 -0800505
506 // There should be a cellular service at this point.
507 if (cellular()->service().get())
508 cellular()->service()->SetActivationState(
509 flimflam::kActivationStateActivating);
Arman Uguray41cc6342013-03-29 16:34:39 -0700510 modem_info()->pending_activation_store()->SetActivationState(
511 PendingActivationStore::kIdentifierICCID,
Arman Ugurayc7b15602013-02-16 00:56:18 -0800512 sim_identifier_,
Arman Uguray41cc6342013-03-29 16:34:39 -0700513 PendingActivationStore::kStatePending);
Arman Uguraya14941d2013-04-12 16:58:26 -0700514
515 activation_wait_for_registration_callback_.Reset(
516 Bind(&CellularCapabilityUniversal::OnActivationWaitForRegisterTimeout,
517 weak_ptr_factory_.GetWeakPtr()));
518 cellular()->dispatcher()->PostDelayedTask(
519 activation_wait_for_registration_callback_.callback(),
520 activation_registration_timeout_milliseconds_);
Arman Ugurayc7b15602013-02-16 00:56:18 -0800521}
522
523void CellularCapabilityUniversal::ResetAfterActivation() {
524 SLOG(Cellular, 2) << __func__;
525
526 // Here the initial call to Reset might fail in rare cases. Simply ignore.
527 Error error;
528 ResultCallback callback = Bind(
529 &CellularCapabilityUniversal::OnResetAfterActivationReply,
530 weak_ptr_factory_.GetWeakPtr());
531 Reset(&error, callback);
532 if (error.IsFailure())
533 SLOG(Cellular, 2) << "Failed to reset after activation.";
534}
535
536void CellularCapabilityUniversal::OnResetAfterActivationReply(
537 const Error &error) {
538 SLOG(Cellular, 2) << __func__;
539 if (error.IsFailure()) {
540 SLOG(Cellular, 2) << "Failed to reset after activation. Try again later.";
541 // TODO(armansito): Maybe post a delayed reset task?
542 return;
543 }
544 reset_done_ = true;
Arman Uguraya14941d2013-04-12 16:58:26 -0700545 activation_wait_for_registration_callback_.Cancel();
Arman Uguray0a3e2792013-01-17 16:31:50 -0800546 UpdatePendingActivationState();
Arman Ugurayc7b15602013-02-16 00:56:18 -0800547}
548
Arman Uguray0a3e2792013-01-17 16:31:50 -0800549void CellularCapabilityUniversal::UpdatePendingActivationState() {
Arman Ugurayc7b15602013-02-16 00:56:18 -0800550 SLOG(Cellular, 2) << __func__;
551
552 bool registered =
553 registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME;
554
555 // If we have a valid MDN, the service is activated. Always try to remove
556 // the ICCID from persistence.
557 bool got_mdn = IsMdnValid();
558 if (got_mdn && !sim_identifier_.empty())
Arman Uguray41cc6342013-03-29 16:34:39 -0700559 modem_info()->pending_activation_store()->RemoveEntry(
560 PendingActivationStore::kIdentifierICCID,
561 sim_identifier_);
Arman Ugurayc7b15602013-02-16 00:56:18 -0800562
Arman Ugurayefea6e02013-02-21 13:28:04 -0800563 CellularServiceRefPtr service = cellular()->service();
564
565 if (!service.get())
566 return;
567
568 if (service->activation_state() == flimflam::kActivationStateActivated)
Arman Ugurayc7b15602013-02-16 00:56:18 -0800569 // Either no service or already activated. Nothing to do.
570 return;
571
572 // If we have a valid MDN or we can connect to the network, then the service
573 // is activated.
574 if (got_mdn || cellular()->state() == Cellular::kStateConnected ||
575 cellular()->state() == Cellular::kStateLinked) {
576 SLOG(Cellular, 2) << "Marking service as activated.";
Arman Ugurayefea6e02013-02-21 13:28:04 -0800577 service->SetActivationState(flimflam::kActivationStateActivated);
Arman Ugurayc7b15602013-02-16 00:56:18 -0800578 return;
579 }
580
581 // If the ICCID is not available, the following logic can be delayed until it
582 // becomes available.
Arman Ugurayefea6e02013-02-21 13:28:04 -0800583 if (sim_identifier_.empty())
584 return;
585
Arman Uguray41cc6342013-03-29 16:34:39 -0700586 PendingActivationStore::State state =
587 modem_info()->pending_activation_store()->GetActivationState(
588 PendingActivationStore::kIdentifierICCID,
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700589 sim_identifier_);
Arman Ugurayefea6e02013-02-21 13:28:04 -0800590 switch (state) {
Arman Uguray41cc6342013-03-29 16:34:39 -0700591 case PendingActivationStore::kStatePending:
Arman Ugurayefea6e02013-02-21 13:28:04 -0800592 // Always mark the service as activating here, as the ICCID could have
593 // been unavailable earlier.
594 service->SetActivationState(flimflam::kActivationStateActivating);
595 if (reset_done_) {
596 SLOG(Cellular, 2) << "Post-payment activation reset complete.";
Arman Uguray41cc6342013-03-29 16:34:39 -0700597 modem_info()->pending_activation_store()->SetActivationState(
598 PendingActivationStore::kIdentifierICCID,
Arman Ugurayefea6e02013-02-21 13:28:04 -0800599 sim_identifier_,
Arman Uguray41cc6342013-03-29 16:34:39 -0700600 PendingActivationStore::kStateActivated);
Arman Ugurayefea6e02013-02-21 13:28:04 -0800601 } else if (registered) {
602 SLOG(Cellular, 2) << "Resetting modem for activation.";
Arman Uguraya14941d2013-04-12 16:58:26 -0700603 activation_wait_for_registration_callback_.Cancel();
Arman Ugurayefea6e02013-02-21 13:28:04 -0800604 ResetAfterActivation();
605 }
606 break;
Arman Uguray41cc6342013-03-29 16:34:39 -0700607 case PendingActivationStore::kStateActivated:
Arman Ugurayefea6e02013-02-21 13:28:04 -0800608 if (registered) {
609 // Trigger auto connect here.
610 SLOG(Cellular, 2) << "Modem has been reset at least once, try to "
611 << "autoconnect to force MDN to update.";
612 service->AutoConnect();
613 }
614 break;
Arman Uguray41cc6342013-03-29 16:34:39 -0700615 case PendingActivationStore::kStatePendingTimeout:
Arman Uguraya14941d2013-04-12 16:58:26 -0700616 SLOG(Cellular, 2) << "Modem failed to register within timeout, but has "
617 << "been reset at least once.";
618 if (registered) {
619 SLOG(Cellular, 2) << "Registered to network, marking as activated.";
Arman Uguray41cc6342013-03-29 16:34:39 -0700620 modem_info()->pending_activation_store()->SetActivationState(
621 PendingActivationStore::kIdentifierICCID,
Arman Uguraya14941d2013-04-12 16:58:26 -0700622 sim_identifier_,
Arman Uguray41cc6342013-03-29 16:34:39 -0700623 PendingActivationStore::kStateActivated);
Arman Uguraya14941d2013-04-12 16:58:26 -0700624 service->SetActivationState(flimflam::kActivationStateActivated);
625 }
626 break;
Arman Uguray41cc6342013-03-29 16:34:39 -0700627 case PendingActivationStore::kStateUnknown:
Arman Ugurayefea6e02013-02-21 13:28:04 -0800628 // No entry exists for this ICCID. Nothing to do.
629 break;
630 default:
631 NOTREACHED();
Arman Ugurayc7b15602013-02-16 00:56:18 -0800632 }
633}
634
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400635void CellularCapabilityUniversal::ReleaseProxies() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700636 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400637 modem_3gpp_proxy_.reset();
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400638 modem_proxy_.reset();
639 modem_simple_proxy_.reset();
640 sim_proxy_.reset();
641}
642
Arman Ugurayc9533572013-01-22 17:34:20 -0800643void CellularCapabilityUniversal::UpdateStorageIdentifier() {
644 if (!cellular()->service().get())
645 return;
646
647 // Lookup the unique identifier assigned to the current network and base the
648 // service's storage identifier on it.
649 const string prefix = "cellular_" + cellular()->address() + "_";
650 string storage_id;
651 if (!operator_id_.empty()) {
652 const CellularOperatorInfo::CellularOperator *provider =
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700653 modem_info()->cellular_operator_info()->GetCellularOperatorByMCCMNC(
654 operator_id_);
Arman Ugurayc9533572013-01-22 17:34:20 -0800655 if (provider && !provider->identifier().empty()) {
656 storage_id = prefix + provider->identifier();
657 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400658 }
Arman Ugurayc9533572013-01-22 17:34:20 -0800659 // If the above didn't work, append IMSI, if available.
660 if (storage_id.empty() && !imsi_.empty()) {
661 storage_id = prefix + imsi_;
662 }
663 if (!storage_id.empty()) {
664 cellular()->service()->SetStorageIdentifier(storage_id);
665 }
666}
667
Arman Ugurayefea6e02013-02-21 13:28:04 -0800668void CellularCapabilityUniversal::UpdateServiceActivationState() {
669 if (!cellular()->service().get())
670 return;
Ben Chan3d6de0e2012-12-10 12:01:34 -0800671 bool activation_required = IsServiceActivationRequired();
Arman Uguray6bb252d2013-05-15 14:29:53 -0700672 string activation_state;
Arman Uguray41cc6342013-03-29 16:34:39 -0700673 PendingActivationStore::State state =
674 modem_info()->pending_activation_store()->GetActivationState(
675 PendingActivationStore::kIdentifierICCID,
Arman Uguraya14941d2013-04-12 16:58:26 -0700676 sim_identifier_);
Arman Ugurayefea6e02013-02-21 13:28:04 -0800677 if (!sim_identifier_.empty() &&
Arman Uguray41cc6342013-03-29 16:34:39 -0700678 (state == PendingActivationStore::kStatePending ||
679 state == PendingActivationStore::kStatePendingTimeout))
Arman Ugurayefea6e02013-02-21 13:28:04 -0800680 activation_state = flimflam::kActivationStateActivating;
681 else if (activation_required)
682 activation_state = flimflam::kActivationStateNotActivated;
Arman Uguray6bb252d2013-05-15 14:29:53 -0700683 else {
684 activation_state = flimflam::kActivationStateActivated;
685
686 // Mark an activated service for auto-connect by default. Since data from
687 // the user profile will be loaded after the call to OnServiceCreated, this
688 // property will be corrected based on the user data at that time.
689 cellular()->service()->SetAutoConnect(true);
690 }
Arman Ugurayefea6e02013-02-21 13:28:04 -0800691 cellular()->service()->SetActivationState(activation_state);
Ben Chan3d6de0e2012-12-10 12:01:34 -0800692 // TODO(benchan): For now, assume the cellular service is activated over
693 // a non-cellular network if service activation is required (i.e. a
694 // corresponding entry is found in the cellular operator info file).
695 // We will need to generalize this logic when migrating CDMA support from
696 // cromo to ModemManager.
697 cellular()->service()->SetActivateOverNonCellularNetwork(activation_required);
Arman Ugurayefea6e02013-02-21 13:28:04 -0800698}
699
700void CellularCapabilityUniversal::OnServiceCreated() {
701 UpdateStorageIdentifier();
702 UpdateServiceActivationState();
Ben Chane1e1e562013-01-26 00:39:01 -0800703 UpdateScanningProperty();
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400704 UpdateServingOperator();
Ben Chan6d0d1e72012-11-06 21:19:28 -0800705 UpdateOLP();
Thieu Le398b1da2013-03-11 17:31:10 -0700706
707 // WORKAROUND:
708 // E362 modems on Verizon network does not properly redirect when a SIM
709 // runs out of credits, we need to enforce out-of-credits detection.
710 // TODO(thieule): Remove this workaround (crosbug.com/p/18619).
711 if (model_id_ == kE362ModelId)
712 cellular()->service()->set_enforce_out_of_credits_detection(true);
Arman Uguray7af0fac2013-03-18 17:35:35 -0700713
714 // Make sure that the network technology is set when the service gets
715 // created, just in case.
716 cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400717}
718
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400719// Create the list of APNs to try, in the following order:
720// - last APN that resulted in a successful connection attempt on the
721// current network (if any)
722// - the APN, if any, that was set by the user
723// - the list of APNs found in the mobile broadband provider DB for the
724// home provider associated with the current SIM
725// - as a last resort, attempt to connect with no APN
726void CellularCapabilityUniversal::SetupApnTryList() {
727 apn_try_list_.clear();
728
729 DCHECK(cellular()->service().get());
730 const Stringmap *apn_info = cellular()->service()->GetLastGoodApn();
731 if (apn_info)
732 apn_try_list_.push_back(*apn_info);
733
734 apn_info = cellular()->service()->GetUserSpecifiedApn();
735 if (apn_info)
736 apn_try_list_.push_back(*apn_info);
737
738 apn_try_list_.insert(apn_try_list_.end(), apn_list_.begin(), apn_list_.end());
739}
740
741void CellularCapabilityUniversal::SetupConnectProperties(
742 DBusPropertiesMap *properties) {
743 SetupApnTryList();
744 FillConnectPropertyMap(properties);
745}
746
747void CellularCapabilityUniversal::FillConnectPropertyMap(
748 DBusPropertiesMap *properties) {
749
750 // TODO(jglasgow): Is this really needed anymore?
Jason Glasgow14521872012-05-07 19:12:15 -0400751 (*properties)[kConnectNumber].writer().append_string(
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400752 kPhoneNumber);
753
Jason Glasgow14521872012-05-07 19:12:15 -0400754 (*properties)[kConnectAllowRoaming].writer().append_bool(
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400755 AllowRoaming());
756
757 if (!apn_try_list_.empty()) {
758 // Leave the APN at the front of the list, so that it can be recorded
759 // if the connect attempt succeeds.
760 Stringmap apn_info = apn_try_list_.front();
Ben Chanfad4a0b2012-04-18 15:49:59 -0700761 SLOG(Cellular, 2) << __func__ << ": Using APN "
762 << apn_info[flimflam::kApnProperty];
Jason Glasgow14521872012-05-07 19:12:15 -0400763 (*properties)[kConnectApn].writer().append_string(
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400764 apn_info[flimflam::kApnProperty].c_str());
765 if (ContainsKey(apn_info, flimflam::kApnUsernameProperty))
Jason Glasgow14521872012-05-07 19:12:15 -0400766 (*properties)[kConnectUser].writer().append_string(
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400767 apn_info[flimflam::kApnUsernameProperty].c_str());
768 if (ContainsKey(apn_info, flimflam::kApnPasswordProperty))
Jason Glasgow14521872012-05-07 19:12:15 -0400769 (*properties)[kConnectPassword].writer().append_string(
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400770 apn_info[flimflam::kApnPasswordProperty].c_str());
771 }
772}
773
774void CellularCapabilityUniversal::OnConnectReply(const ResultCallback &callback,
Nathan Williamsb54974f2012-04-19 11:16:30 -0400775 const DBus::Path &path,
776 const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700777 SLOG(Cellular, 2) << __func__ << "(" << error << ")";
Nathan Williamsb54974f2012-04-19 11:16:30 -0400778
Jason Glasgow7234ec32012-05-23 16:01:21 -0400779 CellularServiceRefPtr service = cellular()->service();
780 if (!service) {
781 // The service could have been deleted before our Connect() request
782 // completes if the modem was enabled and then quickly disabled.
783 apn_try_list_.clear();
784 } else if (error.IsFailure()) {
785 service->ClearLastGoodApn();
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400786 // The APN that was just tried (and failed) is still at the
787 // front of the list, about to be removed. If the list is empty
788 // after that, try one last time without an APN. This may succeed
789 // with some modems in some cases.
Jason Glasgow14521872012-05-07 19:12:15 -0400790 if (RetriableConnectError(error) && !apn_try_list_.empty()) {
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400791 apn_try_list_.pop_front();
Ben Chanfad4a0b2012-04-18 15:49:59 -0700792 SLOG(Cellular, 2) << "Connect failed with invalid APN, "
793 << apn_try_list_.size() << " remaining APNs to try";
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400794 DBusPropertiesMap props;
795 FillConnectPropertyMap(&props);
796 Error error;
797 Connect(props, &error, callback);
798 return;
799 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400800 } else {
801 if (!apn_try_list_.empty()) {
Jason Glasgow7234ec32012-05-23 16:01:21 -0400802 service->SetLastGoodApn(apn_try_list_.front());
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400803 apn_try_list_.clear();
804 }
Nathan Williamsb54974f2012-04-19 11:16:30 -0400805 bearer_path_ = path;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400806 }
807
808 if (!callback.is_null())
809 callback.Run(error);
Arman Ugurayc7b15602013-02-16 00:56:18 -0800810
Arman Uguray0a3e2792013-01-17 16:31:50 -0800811 UpdatePendingActivationState();
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400812}
813
814bool CellularCapabilityUniversal::AllowRoaming() {
Darin Petkovf508c822012-09-21 13:43:17 +0200815 return provider_requires_roaming_ || allow_roaming_property();
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400816}
817
Arman Ugurayf84a4242013-04-09 20:01:07 -0700818bool CellularCapabilityUniversal::ShouldDetectOutOfCredit() const {
Arman Ugurayd42d8ec2013-04-08 19:28:21 -0700819 return model_id_ == kE362ModelId;
820}
821
Jason Glasgowef965562012-04-10 16:12:35 -0400822void CellularCapabilityUniversal::GetProperties() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700823 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400824
Jason Glasgowaf583282012-04-18 15:18:22 -0400825 scoped_ptr<DBusPropertiesProxyInterface> properties_proxy(
826 proxy_factory()->CreateDBusPropertiesProxy(cellular()->dbus_path(),
827 cellular()->dbus_owner()));
828 DBusPropertiesMap properties(
829 properties_proxy->GetAll(MM_DBUS_INTERFACE_MODEM));
830 OnModemPropertiesChanged(properties, vector<string>());
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400831
Jason Glasgowaf583282012-04-18 15:18:22 -0400832 properties = properties_proxy->GetAll(MM_DBUS_INTERFACE_MODEM_MODEM3GPP);
833 OnModem3GPPPropertiesChanged(properties, vector<string>());
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400834}
835
Arman Uguray2717a102013-01-29 23:36:06 -0800836// static
837string CellularCapabilityUniversal::GenerateNewGenericServiceName() {
838 return base::StringPrintf("%s %u",
839 kGenericServiceNamePrefix,
840 friendly_service_name_id_++);
841}
842
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400843string CellularCapabilityUniversal::CreateFriendlyServiceName() {
Darin Petkova4ca3c32012-08-17 16:05:24 +0200844 SLOG(Cellular, 2) << __func__ << ": " << GetRoamingStateString();
Ben Chan092b12b2012-11-07 22:04:05 -0800845
846 // If |serving_operator_| does not have an operator ID, call
847 // UpdateOperatorInfo() to use |operator_id_| as a fallback when appropriate.
848 if (serving_operator_.GetCode().empty()) {
849 UpdateOperatorInfo();
850 }
851
Darin Petkova4ca3c32012-08-17 16:05:24 +0200852 string name = serving_operator_.GetName();
853 string home_provider_name = cellular()->home_provider().GetName();
854 if (!name.empty()) {
855 // If roaming, try to show "<home-provider> | <serving-operator>", per 3GPP
856 // rules (TS 31.102 and annex A of 122.101).
857 if (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING &&
858 !home_provider_name.empty()) {
859 return home_provider_name + " | " + name;
860 }
861 return name;
862 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400863 if (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME &&
Darin Petkova4ca3c32012-08-17 16:05:24 +0200864 !home_provider_name.empty()) {
865 return home_provider_name;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400866 }
Arman Uguray2717a102013-01-29 23:36:06 -0800867 if (!serving_operator_.GetCode().empty()) {
868 return "cellular_" + serving_operator_.GetCode();
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400869 }
Arman Uguray2717a102013-01-29 23:36:06 -0800870 return GenerateNewGenericServiceName();
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400871}
872
873void CellularCapabilityUniversal::SetHomeProvider() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700874 SLOG(Cellular, 2) << __func__ << "(IMSI: " << imsi_
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400875 << " SPN: " << spn_ << ")";
Arman Ugurayd73783f2013-01-31 16:11:21 -0800876
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700877 if (!modem_info()->provider_db())
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400878 return;
Arman Ugurayd73783f2013-01-31 16:11:21 -0800879
880 // MCCMNC can be determined either from IMSI or Operator Code. Use whichever
881 // one is available. If both were reported by the SIM, use IMSI.
882 const string &network_id = imsi_.empty() ? operator_id_ : imsi_;
883 mobile_provider *provider = mobile_provider_lookup_best_match(
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700884 modem_info()->provider_db(),
Arman Ugurayd73783f2013-01-31 16:11:21 -0800885 spn_.c_str(),
886 network_id.c_str());
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400887 if (!provider) {
Arman Ugurayd73783f2013-01-31 16:11:21 -0800888 SLOG(Cellular, 2) << "3GPP provider not found.";
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400889 return;
890 }
Jason Glasgow4380f0d2012-05-03 18:05:04 -0400891
892 // Even if provider is the same as home_provider_, it is possible
893 // that the spn_ has changed. Run all the code below.
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400894 home_provider_ = provider;
Darin Petkovf508c822012-09-21 13:43:17 +0200895 provider_requires_roaming_ = home_provider_->requires_roaming;
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400896 Cellular::Operator oper;
Arman Ugurayd73783f2013-01-31 16:11:21 -0800897 // If Operator ID is available, use that as network code, otherwise
898 // use what was returned from the database.
899 if (!operator_id_.empty()) {
900 oper.SetCode(operator_id_);
901 } else if (provider->networks && provider->networks[0]) {
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400902 oper.SetCode(provider->networks[0]);
903 }
904 if (provider->country) {
905 oper.SetCountry(provider->country);
906 }
907 if (spn_.empty()) {
908 const char *name = mobile_provider_get_name(provider);
909 if (name) {
910 oper.SetName(name);
911 }
912 } else {
913 oper.SetName(spn_);
914 }
915 cellular()->set_home_provider(oper);
Darin Petkova4ca3c32012-08-17 16:05:24 +0200916 SLOG(Cellular, 2) << "Home provider: " << oper.GetCode() << ", "
Darin Petkovf508c822012-09-21 13:43:17 +0200917 << oper.GetName() << ", " << oper.GetCountry()
918 << (provider_requires_roaming_ ? ", roaming required" : "");
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400919 InitAPNList();
Arman Uguray2717a102013-01-29 23:36:06 -0800920 UpdateServiceName();
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400921}
922
Ben Chan8a2c01e2013-01-23 10:09:14 -0800923void CellularCapabilityUniversal::UpdateScanningProperty() {
924 // Set the Scanning property to true if there is a ongoing network scan
925 // (i.e. |scanning_| is true) or the modem is enabled but not yet registered
926 // to a network.
927 //
928 // TODO(benchan): As the Device DBus interface currently does not have a
929 // State property to indicate whether the device is being enabled, set the
930 // Scanning property to true when the modem is being enabled such that
931 // the network UI can start showing the initializing/scanning animation as
932 // soon as the modem is being enabled.
933 Cellular::ModemState modem_state = cellular()->modem_state();
Ben Chan40a2f862013-02-13 17:44:38 -0800934 bool is_activated_service_waiting_for_registration =
935 ((modem_state == Cellular::kModemStateEnabled ||
936 modem_state == Cellular::kModemStateSearching) &&
Ben Chane1e1e562013-01-26 00:39:01 -0800937 !IsServiceActivationRequired());
Ben Chan8a2c01e2013-01-23 10:09:14 -0800938 bool new_scanning_or_searching =
Ben Chan40a2f862013-02-13 17:44:38 -0800939 modem_state == Cellular::kModemStateEnabling ||
940 is_activated_service_waiting_for_registration ||
Ben Chan8a2c01e2013-01-23 10:09:14 -0800941 scanning_;
942 if (new_scanning_or_searching != scanning_or_searching_) {
943 scanning_or_searching_ = new_scanning_or_searching;
944 cellular()->adaptor()->EmitBoolChanged(flimflam::kScanningProperty,
945 new_scanning_or_searching);
Arman Uguray1361c032013-02-11 17:53:39 -0800946
947 if (!scanning_or_searching_) {
948 SLOG(Cellular, 2) << "Initial network scan ended. Canceling timeout.";
949 scanning_or_searching_timeout_callback_.Cancel();
950 } else if (scanning_or_searching_timeout_callback_.IsCancelled()) {
951 SLOG(Cellular, 2) << "Initial network scan started. Starting timeout.";
952 scanning_or_searching_timeout_callback_.Reset(
953 Bind(&CellularCapabilityUniversal::OnScanningOrSearchingTimeout,
954 weak_ptr_factory_.GetWeakPtr()));
955 cellular()->dispatcher()->PostDelayedTask(
956 scanning_or_searching_timeout_callback_.callback(),
957 scanning_or_searching_timeout_milliseconds_);
958 }
Ben Chan8a2c01e2013-01-23 10:09:14 -0800959 }
960}
961
Arman Uguray1361c032013-02-11 17:53:39 -0800962void CellularCapabilityUniversal::OnScanningOrSearchingTimeout() {
963 SLOG(Cellular, 2) << "Initial network scan timed out. Changing "
964 << "flimflam::kScanningProperty to |false|.";
965 scanning_or_searching_ = false;
966 cellular()->adaptor()->EmitBoolChanged(flimflam::kScanningProperty, false);
967}
968
Ben Chan6d0d1e72012-11-06 21:19:28 -0800969void CellularCapabilityUniversal::UpdateOLP() {
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700970 if (!modem_info()->cellular_operator_info())
Ben Chan6d0d1e72012-11-06 21:19:28 -0800971 return;
972
Arman Ugurayf4c61812013-01-10 18:58:39 -0800973 const CellularService::OLP *result =
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700974 modem_info()->cellular_operator_info()->GetOLPByMCCMNC(operator_id_);
Arman Ugurayf4c61812013-01-10 18:58:39 -0800975 if (!result)
Ben Chan6d0d1e72012-11-06 21:19:28 -0800976 return;
977
Arman Ugurayf4c61812013-01-10 18:58:39 -0800978 CellularService::OLP olp;
979 olp.CopyFrom(*result);
Ben Chan6d0d1e72012-11-06 21:19:28 -0800980 string post_data = olp.GetPostData();
Ben Chan6d0d1e72012-11-06 21:19:28 -0800981 ReplaceSubstringsAfterOffset(&post_data, 0, "${iccid}", sim_identifier_);
982 ReplaceSubstringsAfterOffset(&post_data, 0, "${imei}", imei_);
983 ReplaceSubstringsAfterOffset(&post_data, 0, "${imsi}", imsi_);
984 ReplaceSubstringsAfterOffset(&post_data, 0, "${mdn}", mdn_);
Ben Chan6d0d1e72012-11-06 21:19:28 -0800985 ReplaceSubstringsAfterOffset(&post_data, 0, "${min}", min_);
986 olp.SetPostData(post_data);
987 cellular()->service()->SetOLP(olp);
988}
989
Arman Uguray2717a102013-01-29 23:36:06 -0800990void CellularCapabilityUniversal::UpdateServiceName() {
991 if (cellular()->service()) {
992 cellular()->service()->SetFriendlyName(CreateFriendlyServiceName());
993 }
994}
995
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400996void CellularCapabilityUniversal::UpdateOperatorInfo() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700997 SLOG(Cellular, 2) << __func__;
Arman Uguray2717a102013-01-29 23:36:06 -0800998 // TODO(armansito): Use CellularOperatorInfo here instead of
999 // mobile_provider_db.
Ben Chan092b12b2012-11-07 22:04:05 -08001000
Arman Uguray3645c432013-01-31 15:57:26 -08001001 // Sometimes the modem fails to acquire the operator code OTA, in which case
1002 // |serving_operator_| may not have an operator ID (sometimes due to service
1003 // activation being required or broken modem firmware). Use |operator_id_| as
1004 // a fallback when available. |operator_id_| is retrieved from the SIM card.
1005 if (serving_operator_.GetCode().empty() && !operator_id_.empty()) {
Ben Chan092b12b2012-11-07 22:04:05 -08001006 SLOG(Cellular, 2) << "Assuming operator '" << operator_id_
1007 << "' as serving operator.";
1008 serving_operator_.SetCode(operator_id_);
1009 }
1010
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001011 const string &network_id = serving_operator_.GetCode();
1012 if (!network_id.empty()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001013 SLOG(Cellular, 2) << "Looking up network id: " << network_id;
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001014 mobile_provider *provider =
Prathmesh Prabhu27526f12013-03-25 19:42:18 -07001015 mobile_provider_lookup_by_network(modem_info()->provider_db(),
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001016 network_id.c_str());
1017 if (provider) {
Darin Petkova4ca3c32012-08-17 16:05:24 +02001018 if (serving_operator_.GetName().empty()) {
1019 const char *provider_name = mobile_provider_get_name(provider);
1020 if (provider_name && *provider_name) {
1021 serving_operator_.SetName(provider_name);
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001022 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001023 }
Darin Petkova4ca3c32012-08-17 16:05:24 +02001024 if (provider->country && *provider->country) {
1025 serving_operator_.SetCountry(provider->country);
1026 }
1027 SLOG(Cellular, 2) << "Operator name: " << serving_operator_.GetName()
1028 << ", country: " << serving_operator_.GetCountry();
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001029 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001030 SLOG(Cellular, 2) << "GSM provider not found.";
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001031 }
1032 }
1033 UpdateServingOperator();
1034}
1035
1036void CellularCapabilityUniversal::UpdateServingOperator() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001037 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001038 if (cellular()->service().get()) {
1039 cellular()->service()->SetServingOperator(serving_operator_);
1040 }
1041}
1042
Arman Uguray6e5639f2012-11-15 20:30:19 -08001043void CellularCapabilityUniversal::UpdateBearerPath() {
1044 SLOG(Cellular, 2) << __func__;
1045 DBusPathsCallback cb = Bind(&CellularCapabilityUniversal::OnListBearersReply,
1046 weak_ptr_factory_.GetWeakPtr());
1047 Error error;
1048 modem_proxy_->ListBearers(&error, cb, kTimeoutDefault);
1049}
1050
1051void CellularCapabilityUniversal::OnListBearersReply(
1052 const std::vector<DBus::Path> &paths,
1053 const Error &error) {
1054 SLOG(Cellular, 2) << __func__ << "(" << error << ")";
1055 if (error.IsFailure()) {
1056 SLOG(Cellular, 2) << "ListBearers failed with error: " << error;
1057 return;
1058 }
1059 // Look for the first active bearer and use its path as the connected
1060 // one. Right now, we don't allow more than one active bearer.
mukesh agrawal9da07772013-05-15 14:15:17 -07001061 DBus::Path new_bearer_path;
1062 uint32 ipconfig_method(MM_BEARER_IP_METHOD_UNKNOWN);
1063 string network_device;
Arman Uguray6e5639f2012-11-15 20:30:19 -08001064 for (size_t i = 0; i < paths.size(); ++i) {
1065 const DBus::Path &path = paths[i];
1066 scoped_ptr<mm1::BearerProxyInterface> bearer_proxy(
1067 proxy_factory()->CreateBearerProxy(path, cellular()->dbus_owner()));
mukesh agrawal9da07772013-05-15 14:15:17 -07001068 if (!bearer_proxy->Connected()) {
1069 continue;
1070 }
1071 CHECK(new_bearer_path.empty()) << "Found more than one active bearer.";
1072 network_device = bearer_proxy->Interface();
1073 SLOG(Cellular, 2) << "Found active bearer \"" << path << "\".";
1074 SLOG(Cellular, 2) << "Bearer uses interface \"" << network_device << "\".";
1075 // TODO(quiche): Add support for scenarios where the bearer is
1076 // IPv6 only, or where there are conflicting configuration methods
1077 // for IPv4 and IPv6. crbug.com/248360.
1078 DBusPropertiesMap bearer_ip4config = bearer_proxy->Ip4Config();
1079 if (ContainsKey(bearer_ip4config, kIpConfigPropertyMethod)) {
1080 ipconfig_method = bearer_ip4config[kIpConfigPropertyMethod].reader().
1081 get_uint32();
1082 SLOG(Cellular, 2) << "Bearer has IPv4 config method " << ipconfig_method;
1083 } else {
1084 SLOG(Cellular, 2) << "Bearer does not specify IPv4 config method.";
1085 for (const auto &i : bearer_ip4config) {
1086 SLOG(Cellular, 5) << "Bearer IPv4 config has key \""
1087 << i.first
1088 << "\".";
Arman Uguray6e5639f2012-11-15 20:30:19 -08001089 }
1090 }
mukesh agrawal9da07772013-05-15 14:15:17 -07001091 new_bearer_path = path;
Arman Uguray6e5639f2012-11-15 20:30:19 -08001092 }
mukesh agrawal9da07772013-05-15 14:15:17 -07001093 bearer_path_ = new_bearer_path;
1094 if (new_bearer_path.empty()) {
1095 SLOG(Cellular, 2) << "No active bearer found.";
1096 return;
1097 }
1098 if (ipconfig_method == MM_BEARER_IP_METHOD_PPP) {
1099 cellular()->StartPPP(network_device);
Arman Uguray6e5639f2012-11-15 20:30:19 -08001100 }
1101}
1102
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001103void CellularCapabilityUniversal::InitAPNList() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001104 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001105 if (!home_provider_) {
1106 return;
1107 }
1108 apn_list_.clear();
1109 for (int i = 0; i < home_provider_->num_apns; ++i) {
1110 Stringmap props;
1111 mobile_apn *apn = home_provider_->apns[i];
1112 if (apn->value) {
1113 props[flimflam::kApnProperty] = apn->value;
1114 }
1115 if (apn->username) {
1116 props[flimflam::kApnUsernameProperty] = apn->username;
1117 }
1118 if (apn->password) {
1119 props[flimflam::kApnPasswordProperty] = apn->password;
1120 }
1121 // Find the first localized and non-localized name, if any.
1122 const localized_name *lname = NULL;
1123 const localized_name *name = NULL;
1124 for (int j = 0; j < apn->num_names; ++j) {
1125 if (apn->names[j]->lang) {
1126 if (!lname) {
1127 lname = apn->names[j];
1128 }
1129 } else if (!name) {
1130 name = apn->names[j];
1131 }
1132 }
1133 if (name) {
1134 props[flimflam::kApnNameProperty] = name->name;
1135 }
1136 if (lname) {
1137 props[flimflam::kApnLocalizedNameProperty] = lname->name;
1138 props[flimflam::kApnLanguageProperty] = lname->lang;
1139 }
1140 apn_list_.push_back(props);
1141 }
Darin Petkovdb6083f2012-08-16 12:50:23 +02001142 if (cellular()->adaptor()) {
1143 cellular()->adaptor()->EmitStringmapsChanged(
1144 flimflam::kCellularApnListProperty, apn_list_);
1145 } else {
1146 LOG(ERROR) << "Null RPC service adaptor.";
1147 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001148}
1149
Ben Chan15786032012-11-04 21:28:02 -08001150bool CellularCapabilityUniversal::IsServiceActivationRequired() const {
Arman Ugurayc7b15602013-02-16 00:56:18 -08001151 if (!sim_identifier_.empty() &&
Arman Uguray41cc6342013-03-29 16:34:39 -07001152 modem_info()->pending_activation_store()->GetActivationState(
1153 PendingActivationStore::kIdentifierICCID,
1154 sim_identifier_) != PendingActivationStore::kStateUnknown)
Arman Ugurayc7b15602013-02-16 00:56:18 -08001155 return false;
1156
Ben Chan15786032012-11-04 21:28:02 -08001157 // If there is no online payment portal information, it's safer to assume
1158 // the service does not require activation.
Prathmesh Prabhu27526f12013-03-25 19:42:18 -07001159 if (!modem_info()->cellular_operator_info())
Ben Chan15786032012-11-04 21:28:02 -08001160 return false;
1161
Arman Ugurayf4c61812013-01-10 18:58:39 -08001162 const CellularService::OLP *olp =
Prathmesh Prabhu27526f12013-03-25 19:42:18 -07001163 modem_info()->cellular_operator_info()->GetOLPByMCCMNC(operator_id_);
Arman Ugurayf4c61812013-01-10 18:58:39 -08001164 if (!olp)
Ben Chan15786032012-11-04 21:28:02 -08001165 return false;
1166
1167 // To avoid false positives, it's safer to assume the service does not
1168 // require activation if MDN is not set.
1169 if (mdn_.empty())
1170 return false;
1171
Ben Chand7592522013-02-13 16:02:01 -08001172 // If MDN contains only zeros, the service requires activation.
Arman Ugurayc7b15602013-02-16 00:56:18 -08001173 return !IsMdnValid();
1174}
1175
1176bool CellularCapabilityUniversal::IsMdnValid() const {
Arman Ugurayefea6e02013-02-21 13:28:04 -08001177 // Note that |mdn_| is normalized to contain only digits in OnMdnChanged().
Ben Chan15786032012-11-04 21:28:02 -08001178 for (size_t i = 0; i < mdn_.size(); ++i) {
Ben Chand7592522013-02-13 16:02:01 -08001179 if (mdn_[i] != '0')
Arman Ugurayc7b15602013-02-16 00:56:18 -08001180 return true;
Ben Chan15786032012-11-04 21:28:02 -08001181 }
Arman Ugurayc7b15602013-02-16 00:56:18 -08001182 return false;
Ben Chan15786032012-11-04 21:28:02 -08001183}
1184
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001185// always called from an async context
1186void CellularCapabilityUniversal::Register(const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001187 SLOG(Cellular, 2) << __func__ << " \"" << selected_network_ << "\"";
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001188 CHECK(!callback.is_null());
1189 Error error;
1190 ResultCallback cb = Bind(&CellularCapabilityUniversal::OnRegisterReply,
1191 weak_ptr_factory_.GetWeakPtr(), callback);
1192 modem_3gpp_proxy_->Register(selected_network_, &error, cb, kTimeoutRegister);
1193 if (error.IsFailure())
1194 callback.Run(error);
1195}
1196
1197void CellularCapabilityUniversal::RegisterOnNetwork(
1198 const string &network_id,
1199 Error *error,
1200 const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001201 SLOG(Cellular, 2) << __func__ << "(" << network_id << ")";
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001202 CHECK(error);
1203 desired_network_ = network_id;
1204 ResultCallback cb = Bind(&CellularCapabilityUniversal::OnRegisterReply,
1205 weak_ptr_factory_.GetWeakPtr(), callback);
1206 modem_3gpp_proxy_->Register(network_id, error, cb, kTimeoutRegister);
1207}
1208
1209void CellularCapabilityUniversal::OnRegisterReply(
1210 const ResultCallback &callback,
1211 const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001212 SLOG(Cellular, 2) << __func__ << "(" << error << ")";
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001213
1214 if (error.IsSuccess()) {
1215 selected_network_ = desired_network_;
1216 desired_network_.clear();
1217 callback.Run(error);
1218 return;
1219 }
1220 // If registration on the desired network failed,
1221 // try to register on the home network.
1222 if (!desired_network_.empty()) {
1223 desired_network_.clear();
1224 selected_network_.clear();
1225 LOG(INFO) << "Couldn't register on selected network, trying home network";
1226 Register(callback);
1227 return;
1228 }
1229 callback.Run(error);
1230}
1231
1232bool CellularCapabilityUniversal::IsRegistered() {
Prathmesh Prabhu8f6479f2013-05-15 12:56:13 -07001233 return IsRegisteredState(registration_state_);
1234}
1235
1236bool CellularCapabilityUniversal::IsRegisteredState(
1237 MMModem3gppRegistrationState state) {
1238 return (state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
1239 state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING);
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001240}
1241
Eric Shienbrood7fce52c2012-04-13 19:11:02 -04001242void CellularCapabilityUniversal::SetUnregistered(bool searching) {
1243 // If we're already in some non-registered state, don't override that
1244 if (registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
1245 registration_state_ == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) {
1246 registration_state_ =
1247 (searching ? MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING :
1248 MM_MODEM_3GPP_REGISTRATION_STATE_IDLE);
1249 }
1250}
1251
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001252void CellularCapabilityUniversal::RequirePIN(
Jason Glasgow4c0724a2012-04-17 15:47:40 -04001253 const string &pin, bool require,
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001254 Error *error, const ResultCallback &callback) {
1255 CHECK(error);
1256 sim_proxy_->EnablePin(pin, require, error, callback, kTimeoutDefault);
1257}
1258
1259void CellularCapabilityUniversal::EnterPIN(const string &pin,
1260 Error *error,
1261 const ResultCallback &callback) {
1262 CHECK(error);
Arman Ugurayf6366ac2013-06-12 16:02:28 -07001263 SLOG(Cellular, 2) << __func__;
1264 sim_proxy_->SendPin(pin, error, callback, kEnterPinTimeoutMilliseconds);
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001265}
1266
1267void CellularCapabilityUniversal::UnblockPIN(const string &unblock_code,
1268 const string &pin,
1269 Error *error,
1270 const ResultCallback &callback) {
1271 CHECK(error);
1272 sim_proxy_->SendPuk(unblock_code, pin, error, callback, kTimeoutDefault);
1273}
1274
1275void CellularCapabilityUniversal::ChangePIN(
1276 const string &old_pin, const string &new_pin,
1277 Error *error, const ResultCallback &callback) {
1278 CHECK(error);
1279 sim_proxy_->ChangePin(old_pin, new_pin, error, callback, kTimeoutDefault);
1280}
1281
Ben Chan5d0d32c2013-01-08 02:05:29 -08001282void CellularCapabilityUniversal::Reset(Error *error,
1283 const ResultCallback &callback) {
1284 SLOG(Cellular, 2) << __func__;
1285 CHECK(error);
1286 if (resetting_) {
1287 Error::PopulateAndLog(error, Error::kInProgress, "Already resetting");
1288 return;
1289 }
1290 ResultCallback cb = Bind(&CellularCapabilityUniversal::OnResetReply,
1291 weak_ptr_factory_.GetWeakPtr(), callback);
1292 modem_proxy_->Reset(error, cb, kTimeoutReset);
1293 if (!error->IsFailure()) {
1294 resetting_ = true;
1295 }
1296}
1297
1298void CellularCapabilityUniversal::OnResetReply(const ResultCallback &callback,
1299 const Error &error) {
1300 SLOG(Cellular, 2) << __func__;
1301 resetting_ = false;
1302 if (!callback.is_null())
1303 callback.Run(error);
1304}
1305
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001306void CellularCapabilityUniversal::Scan(Error *error,
1307 const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001308 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001309 CHECK(error);
Jason Glasgowcd0349c2012-05-03 23:32:15 -04001310 if (scanning_) {
1311 Error::PopulateAndLog(error, Error::kInProgress, "Already scanning");
1312 return;
1313 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001314 DBusPropertyMapsCallback cb = Bind(&CellularCapabilityUniversal::OnScanReply,
1315 weak_ptr_factory_.GetWeakPtr(), callback);
1316 modem_3gpp_proxy_->Scan(error, cb, kTimeoutScan);
Jason Glasgowcd0349c2012-05-03 23:32:15 -04001317 if (!error->IsFailure()) {
1318 scanning_ = true;
Ben Chan8a2c01e2013-01-23 10:09:14 -08001319 UpdateScanningProperty();
Jason Glasgowcd0349c2012-05-03 23:32:15 -04001320 }
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001321}
1322
1323void CellularCapabilityUniversal::OnScanReply(const ResultCallback &callback,
1324 const ScanResults &results,
1325 const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001326 SLOG(Cellular, 2) << __func__;
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001327
1328 // Error handling is weak. The current expectation is that on any
1329 // error, found_networks_ should be cleared and a property change
1330 // notification sent out.
1331 //
1332 // TODO(jglasgow): fix error handling
Jason Glasgowcd0349c2012-05-03 23:32:15 -04001333 scanning_ = false;
Ben Chan8a2c01e2013-01-23 10:09:14 -08001334 UpdateScanningProperty();
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001335 found_networks_.clear();
1336 if (!error.IsFailure()) {
1337 for (ScanResults::const_iterator it = results.begin();
1338 it != results.end(); ++it) {
1339 found_networks_.push_back(ParseScanResult(*it));
1340 }
1341 }
1342 cellular()->adaptor()->EmitStringmapsChanged(flimflam::kFoundNetworksProperty,
1343 found_networks_);
Gary Morainceba6aa2012-05-03 10:28:26 -07001344
1345 // TODO(gmorain): This check for is_null() is a work-around because
1346 // Cellular::Scan() passes a null callback. Instead: 1. Have Cellular::Scan()
1347 // pass in a callback. 2. Have Cellular "own" the found_networks_ property
1348 // 3. Have Cellular EmitStingMapsChanged() 4. Share the code between GSM and
1349 // Universal.
1350 if (!callback.is_null())
1351 callback.Run(error);
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001352}
1353
1354Stringmap CellularCapabilityUniversal::ParseScanResult(
1355 const ScanResult &result) {
1356
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001357 /* ScanResults contain the following keys:
1358
1359 "status"
1360 A MMModem3gppNetworkAvailability value representing network
1361 availability status, given as an unsigned integer (signature "u").
1362 This key will always be present.
1363
1364 "operator-long"
1365 Long-format name of operator, given as a string value (signature
1366 "s"). If the name is unknown, this field should not be present.
1367
1368 "operator-short"
1369 Short-format name of operator, given as a string value
1370 (signature "s"). If the name is unknown, this field should not
1371 be present.
1372
1373 "operator-code"
1374 Mobile code of the operator, given as a string value (signature
1375 "s"). Returned in the format "MCCMNC", where MCC is the
1376 three-digit ITU E.212 Mobile Country Code and MNC is the two- or
1377 three-digit GSM Mobile Network Code. e.g. "31026" or "310260".
1378
1379 "access-technology"
1380 A MMModemAccessTechnology value representing the generic access
1381 technology used by this mobile network, given as an unsigned
1382 integer (signature "u").
1383 */
1384 Stringmap parsed;
1385
1386 uint32 status;
1387 if (DBusProperties::GetUint32(result, kStatusProperty, &status)) {
1388 // numerical values are taken from 3GPP TS 27.007 Section 7.3.
1389 static const char * const kStatusString[] = {
1390 "unknown", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN
1391 "available", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE
1392 "current", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT
1393 "forbidden", // MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN
1394 };
1395 parsed[flimflam::kStatusProperty] = kStatusString[status];
1396 }
1397
1398 uint32 tech; // MMModemAccessTechnology
1399 if (DBusProperties::GetUint32(result, kOperatorAccessTechnologyProperty,
1400 &tech)) {
1401 parsed[flimflam::kTechnologyProperty] = AccessTechnologyToString(tech);
1402 }
1403
1404 string operator_long, operator_short, operator_code;
1405 if (DBusProperties::GetString(result, kOperatorLongProperty, &operator_long))
1406 parsed[flimflam::kLongNameProperty] = operator_long;
1407 if (DBusProperties::GetString(result, kOperatorShortProperty,
1408 &operator_short))
1409 parsed[flimflam::kShortNameProperty] = operator_short;
1410 if (DBusProperties::GetString(result, kOperatorCodeProperty, &operator_code))
1411 parsed[flimflam::kNetworkIdProperty] = operator_code;
1412
1413 // If the long name is not available but the network ID is, look up the long
1414 // name in the mobile provider database.
1415 if ((!ContainsKey(parsed, flimflam::kLongNameProperty) ||
1416 parsed[flimflam::kLongNameProperty].empty()) &&
1417 ContainsKey(parsed, flimflam::kNetworkIdProperty)) {
1418 mobile_provider *provider =
1419 mobile_provider_lookup_by_network(
Prathmesh Prabhu27526f12013-03-25 19:42:18 -07001420 modem_info()->provider_db(),
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001421 parsed[flimflam::kNetworkIdProperty].c_str());
1422 if (provider) {
1423 const char *long_name = mobile_provider_get_name(provider);
1424 if (long_name && *long_name) {
1425 parsed[flimflam::kLongNameProperty] = long_name;
1426 }
1427 }
1428 }
1429 return parsed;
1430}
1431
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001432string CellularCapabilityUniversal::GetNetworkTechnologyString() const {
Arman Uguray7af0fac2013-03-18 17:35:35 -07001433 // If we know that the modem is an E362, return LTE here to make sure that
1434 // Chrome sees LTE as the network technology even if the actual technology is
1435 // unknown.
1436 // TODO(armansito): This hack will cause the UI to display LTE even if the
1437 // modem doesn't support it at a given time. This might be problematic if we
1438 // ever want to support switching between access technologies (e.g. falling
1439 // back to 3G when LTE is not available).
1440 if (model_id_ == kE362ModelId)
1441 return flimflam::kNetworkTechnologyLte;
1442
Jason Glasgow9f09aef2012-05-08 16:26:55 -04001443 // Order is important. Return the highest speed technology
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001444 // TODO(jglasgow): change shill interfaces to a capability model
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001445 return AccessTechnologyToString(access_technologies_);
1446}
1447
1448string CellularCapabilityUniversal::GetRoamingStateString() const {
1449 switch (registration_state_) {
1450 case MM_MODEM_3GPP_REGISTRATION_STATE_HOME:
1451 return flimflam::kRoamingStateHome;
1452 case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING:
1453 return flimflam::kRoamingStateRoaming;
1454 default:
1455 break;
1456 }
1457 return flimflam::kRoamingStateUnknown;
1458}
1459
1460void CellularCapabilityUniversal::GetSignalQuality() {
Nathan Williams218cbcd2012-04-17 16:48:44 -04001461 // TODO(njw): Switch to asynchronous calls (crosbug.com/17583).
1462 const DBus::Struct<unsigned int, bool> quality =
1463 modem_proxy_->SignalQuality();
1464 OnSignalQualityChanged(quality._1);
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001465}
1466
Jason Glasgow9f09aef2012-05-08 16:26:55 -04001467string CellularCapabilityUniversal::GetTypeString() const {
1468 return AccessTechnologyToTechnologyFamily(access_technologies_);
1469}
1470
Jason Glasgow4c0724a2012-04-17 15:47:40 -04001471void CellularCapabilityUniversal::OnModemPropertiesChanged(
1472 const DBusPropertiesMap &properties,
1473 const vector<string> &/* invalidated_properties */) {
Eric Shienbrood7fce52c2012-04-13 19:11:02 -04001474 // This solves a bootstrapping problem: If the modem is not yet
1475 // enabled, there are no proxy objects associated with the capability
1476 // object, so modem signals like StateChanged aren't seen. By monitoring
1477 // changes to the State property via the ModemManager, we're able to
1478 // get the initialization process started, which will result in the
1479 // creation of the proxy objects.
1480 //
1481 // The first time we see the change to State (when the modem state
1482 // is Unknown), we simply update the state, and rely on the Manager to
1483 // enable the device when it is registered with the Manager. On subsequent
1484 // changes to State, we need to explicitly enable the device ourselves.
1485 int32 istate;
Jason Glasgowaf583282012-04-18 15:18:22 -04001486 if (DBusProperties::GetInt32(properties, MM_MODEM_PROPERTY_STATE, &istate)) {
Eric Shienbrood7fce52c2012-04-13 19:11:02 -04001487 Cellular::ModemState state = static_cast<Cellular::ModemState>(istate);
Jason Glasgowaf583282012-04-18 15:18:22 -04001488 OnModemStateChanged(state);
Eric Shienbrood7fce52c2012-04-13 19:11:02 -04001489 }
Jason Glasgowaf583282012-04-18 15:18:22 -04001490 string string_value;
Nathan Williamse9840802012-04-18 18:47:40 -04001491 if (DBusProperties::GetObjectPath(properties,
Jason Glasgowaf583282012-04-18 15:18:22 -04001492 MM_MODEM_PROPERTY_SIM, &string_value))
1493 OnSimPathChanged(string_value);
1494 uint32 uint_value;
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001495 if (DBusProperties::GetUint32(properties,
Jason Glasgowaf583282012-04-18 15:18:22 -04001496 MM_MODEM_PROPERTY_CURRENTCAPABILITIES,
1497 &uint_value))
1498 OnModemCurrentCapabilitiesChanged(uint_value);
1499 // not needed: MM_MODEM_PROPERTY_MAXBEARERS
1500 // not needed: MM_MODEM_PROPERTY_MAXACTIVEBEARERS
1501 if (DBusProperties::GetString(properties,
1502 MM_MODEM_PROPERTY_MANUFACTURER,
1503 &string_value))
1504 OnModemManufacturerChanged(string_value);
1505 if (DBusProperties::GetString(properties,
1506 MM_MODEM_PROPERTY_MODEL,
1507 &string_value))
1508 OnModemModelChanged(string_value);
1509 if (DBusProperties::GetString(properties,
1510 MM_MODEM_PROPERTY_REVISION,
1511 &string_value))
1512 OnModemRevisionChanged(string_value);
1513 // not needed: MM_MODEM_PROPERTY_DEVICEIDENTIFIER
1514 // not needed: MM_MODEM_PROPERTY_DEVICE
1515 // not needed: MM_MODEM_PROPERTY_DRIVER
1516 // not needed: MM_MODEM_PROPERTY_PLUGIN
1517 // not needed: MM_MODEM_PROPERTY_EQUIPMENTIDENTIFIER
Nathan Williams218cbcd2012-04-17 16:48:44 -04001518
Jason Glasgowaf583282012-04-18 15:18:22 -04001519 // Unlock required and SimLock
Jason Glasgowaf583282012-04-18 15:18:22 -04001520 uint32_t unlock_required; // This is really of type MMModemLock
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001521 if (DBusProperties::GetUint32(properties,
1522 MM_MODEM_PROPERTY_UNLOCKREQUIRED,
Arman Ugurayc7e63af2013-06-13 17:07:32 -07001523 &unlock_required))
1524 OnLockTypeChanged(static_cast<MMModemLock>(unlock_required));
1525
1526 // Unlock retries
Jason Glasgowaf583282012-04-18 15:18:22 -04001527 DBusPropertiesMap::const_iterator it =
1528 properties.find(MM_MODEM_PROPERTY_UNLOCKRETRIES);
1529 if (it != properties.end()) {
Arman Ugurayc7e63af2013-06-13 17:07:32 -07001530 LockRetryData lock_retries = it->second.operator LockRetryData();
1531 OnLockRetriesChanged(lock_retries);
Jason Glasgowaf583282012-04-18 15:18:22 -04001532 }
Arman Ugurayc7e63af2013-06-13 17:07:32 -07001533
Jason Glasgowaf583282012-04-18 15:18:22 -04001534 if (DBusProperties::GetUint32(properties,
1535 MM_MODEM_PROPERTY_ACCESSTECHNOLOGIES,
1536 &uint_value))
1537 OnAccessTechnologiesChanged(uint_value);
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001538
Jason Glasgowaf583282012-04-18 15:18:22 -04001539 it = properties.find(MM_MODEM_PROPERTY_SIGNALQUALITY);
1540 if (it != properties.end()) {
1541 DBus::Struct<unsigned int, bool> quality = it->second;
1542 OnSignalQualityChanged(quality._1);
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001543 }
Jason Glasgowaf583282012-04-18 15:18:22 -04001544 vector<string> numbers;
1545 if (DBusProperties::GetStrings(properties, MM_MODEM_PROPERTY_OWNNUMBERS,
1546 &numbers)) {
1547 string mdn;
1548 if (numbers.size() > 0)
1549 mdn = numbers[0];
1550 OnMdnChanged(mdn);
1551 }
Jason Glasgowaf583282012-04-18 15:18:22 -04001552 // au: MM_MODEM_PROPERTY_SUPPORTEDBANDS,
1553 // au: MM_MODEM_PROPERTY_BANDS
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001554}
1555
1556void CellularCapabilityUniversal::OnDBusPropertiesChanged(
1557 const string &interface,
1558 const DBusPropertiesMap &changed_properties,
Jason Glasgow4c0724a2012-04-17 15:47:40 -04001559 const vector<string> &invalidated_properties) {
Jason Glasgow7234ec32012-05-23 16:01:21 -04001560 SLOG(Cellular, 2) << __func__ << "(" << interface << ")";
Jason Glasgowef965562012-04-10 16:12:35 -04001561 if (interface == MM_DBUS_INTERFACE_MODEM) {
Jason Glasgow4c0724a2012-04-17 15:47:40 -04001562 OnModemPropertiesChanged(changed_properties, invalidated_properties);
Jason Glasgowef965562012-04-10 16:12:35 -04001563 }
Jason Glasgowaf583282012-04-18 15:18:22 -04001564 if (interface == MM_DBUS_INTERFACE_MODEM_MODEM3GPP) {
1565 OnModem3GPPPropertiesChanged(changed_properties, invalidated_properties);
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001566 }
Jason Glasgowaf583282012-04-18 15:18:22 -04001567 if (interface == MM_DBUS_INTERFACE_SIM) {
1568 OnSimPropertiesChanged(changed_properties, invalidated_properties);
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001569 }
1570}
1571
Jason Glasgow14521872012-05-07 19:12:15 -04001572bool CellularCapabilityUniversal::RetriableConnectError(
1573 const Error &error) const {
1574 if (error.type() == Error::kInvalidApn)
1575 return true;
1576
1577 // modemmanager does not ever return kInvalidApn for E362 modems
1578 // with 1.41 firmware. It remains to be seem if this will change
1579 // with 3.x firmware.
1580 if ((model_id_ == kE362ModelId) && (error.type() == Error::kOperationFailed))
1581 return true;
1582
1583 return false;
1584}
1585
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001586void CellularCapabilityUniversal::OnNetworkModeSignal(uint32 /*mode*/) {
1587 // TODO(petkov): Implement this.
1588 NOTIMPLEMENTED();
1589}
1590
Arman Uguray6552f8c2013-02-12 15:33:18 -08001591bool CellularCapabilityUniversal::IsValidSimPath(const string &sim_path) const {
1592 return !sim_path.empty() && sim_path != kRootPath;
1593}
1594
Ben Chand7592522013-02-13 16:02:01 -08001595string CellularCapabilityUniversal::NormalizeMdn(const string &mdn) const {
1596 string normalized_mdn;
1597 for (size_t i = 0; i < mdn.size(); ++i) {
1598 if (IsAsciiDigit(mdn[i]))
1599 normalized_mdn += mdn[i];
1600 }
1601 return normalized_mdn;
1602}
1603
Jason Glasgowaf583282012-04-18 15:18:22 -04001604void CellularCapabilityUniversal::OnSimPathChanged(
1605 const string &sim_path) {
1606 if (sim_path == sim_path_)
1607 return;
1608
1609 mm1::SimProxyInterface *proxy = NULL;
Arman Uguray6552f8c2013-02-12 15:33:18 -08001610 if (IsValidSimPath(sim_path))
Jason Glasgowaf583282012-04-18 15:18:22 -04001611 proxy = proxy_factory()->CreateSimProxy(sim_path,
1612 cellular()->dbus_owner());
1613 sim_path_ = sim_path;
1614 sim_proxy_.reset(proxy);
1615
Arman Uguray6552f8c2013-02-12 15:33:18 -08001616 if (!IsValidSimPath(sim_path)) {
Jason Glasgowaf583282012-04-18 15:18:22 -04001617 // Clear all data about the sim
Jason Glasgow4380f0d2012-05-03 18:05:04 -04001618 imsi_ = "";
1619 spn_ = "";
Ben Chanbd3aee82012-10-16 23:52:04 -07001620 sim_present_ = false;
Jason Glasgowaf583282012-04-18 15:18:22 -04001621 OnSimIdentifierChanged("");
1622 OnOperatorIdChanged("");
Jason Glasgowaf583282012-04-18 15:18:22 -04001623 } else {
Ben Chanbd3aee82012-10-16 23:52:04 -07001624 sim_present_ = true;
Jason Glasgowaf583282012-04-18 15:18:22 -04001625 scoped_ptr<DBusPropertiesProxyInterface> properties_proxy(
1626 proxy_factory()->CreateDBusPropertiesProxy(sim_path,
1627 cellular()->dbus_owner()));
1628 // TODO(jglasgow): convert to async interface
1629 DBusPropertiesMap properties(
1630 properties_proxy->GetAll(MM_DBUS_INTERFACE_SIM));
1631 OnSimPropertiesChanged(properties, vector<string>());
1632 }
1633}
1634
Jason Glasgowaf583282012-04-18 15:18:22 -04001635void CellularCapabilityUniversal::OnModemCurrentCapabilitiesChanged(
1636 uint32 current_capabilities) {
1637 current_capabilities_ = current_capabilities;
Ben Chanfcca27b2013-01-22 15:03:44 -08001638
1639 // Only allow network scan when the modem's current capabilities support
1640 // GSM/UMTS.
1641 //
1642 // TODO(benchan): We should consider having the modem plugins in ModemManager
1643 // reporting whether network scan is supported.
1644 scanning_supported_ =
1645 (current_capabilities & MM_MODEM_CAPABILITY_GSM_UMTS) != 0;
1646 if (cellular()->adaptor()) {
1647 cellular()->adaptor()->EmitBoolChanged(
1648 flimflam::kSupportNetworkScanProperty, scanning_supported_);
1649 }
Jason Glasgowaf583282012-04-18 15:18:22 -04001650}
1651
1652void CellularCapabilityUniversal::OnMdnChanged(
1653 const string &mdn) {
Ben Chand7592522013-02-13 16:02:01 -08001654 mdn_ = NormalizeMdn(mdn);
Arman Uguray0a3e2792013-01-17 16:31:50 -08001655 UpdatePendingActivationState();
Jason Glasgowaf583282012-04-18 15:18:22 -04001656}
1657
1658void CellularCapabilityUniversal::OnModemManufacturerChanged(
1659 const string &manufacturer) {
1660 manufacturer_ = manufacturer;
1661}
1662
1663void CellularCapabilityUniversal::OnModemModelChanged(
1664 const string &model) {
1665 model_id_ = model;
1666}
1667
1668void CellularCapabilityUniversal::OnModemRevisionChanged(
1669 const string &revision) {
1670 firmware_revision_ = revision;
1671}
1672
1673void CellularCapabilityUniversal::OnModemStateChanged(
1674 Cellular::ModemState state) {
1675 Cellular::ModemState prev_modem_state = cellular()->modem_state();
1676 bool was_enabled = cellular()->IsUnderlyingDeviceEnabled();
1677 if (Cellular::IsEnabledModemState(state))
1678 cellular()->set_modem_state(state);
Gary Moraine285a842012-08-15 08:23:57 -07001679 SLOG(Cellular, 2) << __func__ << ": prev_modem_state: " << prev_modem_state
1680 << " was_enabled: " << was_enabled
1681 << " cellular state: "
1682 << cellular()->GetStateString(cellular()->state());
Jason Glasgowaf583282012-04-18 15:18:22 -04001683 if (prev_modem_state != Cellular::kModemStateUnknown &&
1684 prev_modem_state != Cellular::kModemStateEnabling &&
1685 !was_enabled &&
1686 cellular()->state() == Cellular::kStateDisabled &&
1687 cellular()->IsUnderlyingDeviceEnabled()) {
1688 cellular()->SetEnabled(true);
1689 }
Ben Chan8a2c01e2013-01-23 10:09:14 -08001690 UpdateScanningProperty();
Jason Glasgowaf583282012-04-18 15:18:22 -04001691}
1692
1693void CellularCapabilityUniversal::OnAccessTechnologiesChanged(
1694 uint32 access_technologies) {
Jason Glasgow9f09aef2012-05-08 16:26:55 -04001695 if (access_technologies_ != access_technologies) {
Jason Glasgowbad114b2012-05-21 15:24:16 -04001696 const string old_type_string(GetTypeString());
Jason Glasgow9f09aef2012-05-08 16:26:55 -04001697 access_technologies_ = access_technologies;
Jason Glasgowbad114b2012-05-21 15:24:16 -04001698 const string new_type_string(GetTypeString());
1699 if (new_type_string != old_type_string) {
1700 // TODO(jglasgow): address layering violation of emitting change
1701 // signal here for a property owned by Cellular.
1702 cellular()->adaptor()->EmitStringChanged(
1703 flimflam::kTechnologyFamilyProperty, new_type_string);
1704 }
Jason Glasgow9f09aef2012-05-08 16:26:55 -04001705 if (cellular()->service().get()) {
1706 cellular()->service()->SetNetworkTechnology(GetNetworkTechnologyString());
1707 }
Jason Glasgowaf583282012-04-18 15:18:22 -04001708 }
1709}
1710
Jason Glasgowaf583282012-04-18 15:18:22 -04001711void CellularCapabilityUniversal::OnLockRetriesChanged(
Jason Glasgowaf583282012-04-18 15:18:22 -04001712 const LockRetryData &lock_retries) {
Arman Ugurayc7e63af2013-06-13 17:07:32 -07001713 SLOG(Cellular, 2) << __func__;
1714 LockRetryData::const_iterator it =
1715 lock_retries.find(sim_lock_status_.lock_type);
Jason Glasgowaf583282012-04-18 15:18:22 -04001716 if (it != lock_retries.end()) {
1717 sim_lock_status_.retries_left = it->second;
1718 } else {
1719 // Unknown, use 999
1720 sim_lock_status_.retries_left = 999;
1721 }
1722 OnSimLockStatusChanged();
1723}
1724
Arman Ugurayc7e63af2013-06-13 17:07:32 -07001725void CellularCapabilityUniversal::OnLockTypeChanged(
1726 MMModemLock lock_type) {
1727 SLOG(Cellular, 2) << __func__ << ": " << lock_type;
1728 sim_lock_status_.lock_type = lock_type;
1729 OnSimLockStatusChanged();
1730}
1731
Jason Glasgowaf583282012-04-18 15:18:22 -04001732void CellularCapabilityUniversal::OnSimLockStatusChanged() {
Arman Ugurayf6366ac2013-06-12 16:02:28 -07001733 SLOG(Cellular, 2) << __func__;
Jason Glasgowaf583282012-04-18 15:18:22 -04001734 cellular()->adaptor()->EmitKeyValueStoreChanged(
1735 flimflam::kSIMLockStatusProperty, SimLockStatusToProperty(NULL));
Arman Ugurayab9364e2012-12-19 20:45:25 -08001736
1737 // If the SIM is currently unlocked, assume that we need to refresh
1738 // carrier information, since a locked SIM prevents shill from obtaining
1739 // the necessary data to establish a connection later (e.g. IMSI).
Arman Ugurayc7e63af2013-06-13 17:07:32 -07001740 if (!sim_path_.empty() &&
1741 sim_lock_status_.lock_type == MM_MODEM_LOCK_NONE) {
Arman Ugurayab9364e2012-12-19 20:45:25 -08001742 scoped_ptr<DBusPropertiesProxyInterface> properties_proxy(
1743 proxy_factory()->CreateDBusPropertiesProxy(sim_path_,
1744 cellular()->dbus_owner()));
1745 DBusPropertiesMap properties(
1746 properties_proxy->GetAll(MM_DBUS_INTERFACE_SIM));
1747 OnSimPropertiesChanged(properties, vector<string>());
1748 }
Jason Glasgowaf583282012-04-18 15:18:22 -04001749}
1750
1751void CellularCapabilityUniversal::OnModem3GPPPropertiesChanged(
1752 const DBusPropertiesMap &properties,
1753 const vector<string> &/* invalidated_properties */) {
Ben Chan6d0d1e72012-11-06 21:19:28 -08001754 SLOG(Cellular, 2) << __func__;
Jason Glasgowaf583282012-04-18 15:18:22 -04001755 string imei;
1756 if (DBusProperties::GetString(properties,
1757 MM_MODEM_MODEM3GPP_PROPERTY_IMEI,
1758 &imei))
1759 OnImeiChanged(imei);
1760
1761 // Handle registration state changes as a single change
1762 string operator_code = serving_operator_.GetCode();
1763 string operator_name = serving_operator_.GetName();
1764 MMModem3gppRegistrationState state = registration_state_;
1765 bool registration_changed = false;
1766 uint32 uint_value;
1767 if (DBusProperties::GetUint32(properties,
1768 MM_MODEM_MODEM3GPP_PROPERTY_REGISTRATIONSTATE,
1769 &uint_value)) {
1770 state = static_cast<MMModem3gppRegistrationState>(uint_value);
1771 registration_changed = true;
1772 }
1773 if (DBusProperties::GetString(properties,
1774 MM_MODEM_MODEM3GPP_PROPERTY_OPERATORCODE,
1775 &operator_code))
1776 registration_changed = true;
1777 if (DBusProperties::GetString(properties,
1778 MM_MODEM_MODEM3GPP_PROPERTY_OPERATORNAME,
1779 &operator_name))
1780 registration_changed = true;
1781 if (registration_changed)
1782 On3GPPRegistrationChanged(state, operator_code, operator_name);
1783
1784 uint32 locks = 0;
1785 if (DBusProperties::GetUint32(
1786 properties, MM_MODEM_MODEM3GPP_PROPERTY_ENABLEDFACILITYLOCKS,
1787 &locks))
1788 OnFacilityLocksChanged(locks);
1789}
1790
1791void CellularCapabilityUniversal::OnImeiChanged(const string &imei) {
1792 imei_ = imei;
1793}
1794
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001795void CellularCapabilityUniversal::On3GPPRegistrationChanged(
1796 MMModem3gppRegistrationState state,
1797 const string &operator_code,
1798 const string &operator_name) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001799 SLOG(Cellular, 2) << __func__ << ": regstate=" << state
1800 << ", opercode=" << operator_code
1801 << ", opername=" << operator_name;
Prathmesh Prabhu8f6479f2013-05-15 12:56:13 -07001802
1803 // While the modem is connected, if the state changed from a registered state
1804 // to a non registered state, defer the state change by 15 seconds.
1805 // TODO(pprabhu) [crbug.com/241231] Add UMA stat to determine whether this
1806 // condition is specific to E362. If it is, guard the delayed update by E362
1807 // specific flag.
1808 if (cellular()->modem_state() == Cellular::kModemStateConnected &&
1809 IsRegistered() && !IsRegisteredState(state)) {
1810 if (!registration_dropped_update_callback_.IsCancelled()) {
1811 LOG(WARNING) << "Modem reported consecutive 3GPP registration drops. "
1812 << "Ignoring earlier notifications.";
1813 registration_dropped_update_callback_.Cancel();
1814 }
1815 SLOG(Cellular, 2) << "Posted deferred registration state update";
1816 registration_dropped_update_callback_.Reset(
1817 Bind(&CellularCapabilityUniversal::Handle3GPPRegistrationChange,
1818 weak_ptr_factory_.GetWeakPtr(),
1819 state,
1820 operator_code,
1821 operator_name));
1822 cellular()->dispatcher()->PostDelayedTask(
1823 registration_dropped_update_callback_.callback(),
1824 registration_dropped_update_timeout_milliseconds_);
1825 } else {
1826 if (!registration_dropped_update_callback_.IsCancelled()) {
1827 SLOG(Cellular, 2) << "Cancelled a deferred registration state update";
1828 registration_dropped_update_callback_.Cancel();
1829 }
1830 Handle3GPPRegistrationChange(state, operator_code, operator_name);
1831 }
1832}
1833
1834void CellularCapabilityUniversal::Handle3GPPRegistrationChange(
1835 MMModem3gppRegistrationState updated_state,
1836 string updated_operator_code,
1837 string updated_operator_name) {
1838 // A finished callback does not qualify as a canceled callback.
1839 // We test for a canceled callback to check for outstanding callbacks.
1840 // So, explicitly cancel the callback here.
1841 registration_dropped_update_callback_.Cancel();
1842
1843 SLOG(Cellular, 2) << __func__ << ": regstate=" << updated_state
1844 << ", opercode=" << updated_operator_code
1845 << ", opername=" << updated_operator_name;
1846
1847 registration_state_ = updated_state;
1848 serving_operator_.SetCode(updated_operator_code);
1849 serving_operator_.SetName(updated_operator_name);
Arman Uguray2717a102013-01-29 23:36:06 -08001850
1851 // Update the carrier name for |serving_operator_|.
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001852 UpdateOperatorInfo();
Arman Uguray2717a102013-01-29 23:36:06 -08001853
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001854 cellular()->HandleNewRegistrationState();
Arman Uguray2717a102013-01-29 23:36:06 -08001855
1856 // Update the user facing name of the cellular service.
1857 UpdateServiceName();
Arman Ugurayc7b15602013-02-16 00:56:18 -08001858
1859 // If the modem registered with the network and the current ICCID is pending
1860 // activation, then reset the modem.
Arman Uguray0a3e2792013-01-17 16:31:50 -08001861 UpdatePendingActivationState();
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001862}
1863
Eric Shienbrood7fce52c2012-04-13 19:11:02 -04001864void CellularCapabilityUniversal::OnModemStateChangedSignal(
1865 int32 old_state, int32 new_state, uint32 reason) {
Eric Shienbrood9b1bd7b2012-04-20 16:42:26 -04001866 SLOG(Cellular, 2) << __func__ << "(" << old_state << ", " << new_state << ", "
1867 << reason << ")";
Eric Shienbrood7fce52c2012-04-13 19:11:02 -04001868 cellular()->OnModemStateChanged(static_cast<Cellular::ModemState>(old_state),
1869 static_cast<Cellular::ModemState>(new_state),
1870 reason);
Ben Chan2e12ccd2013-01-24 15:24:27 -08001871 UpdateScanningProperty();
Gary Moraine285a842012-08-15 08:23:57 -07001872 if (!deferred_enable_modem_callback_.is_null() &&
1873 (new_state == Cellular::kModemStateDisabled)) {
1874 SLOG(Cellular, 2) << "Enabling modem after deferring";
1875 deferred_enable_modem_callback_.Run();
1876 deferred_enable_modem_callback_.Reset();
Arman Uguray6e5639f2012-11-15 20:30:19 -08001877 } else if (new_state == Cellular::kModemStateConnected) {
1878 SLOG(Cellular, 2) << "Updating bearer path to reflect the active bearer.";
1879 UpdateBearerPath();
Gary Moraine285a842012-08-15 08:23:57 -07001880 }
Eric Shienbrood7fce52c2012-04-13 19:11:02 -04001881}
1882
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001883void CellularCapabilityUniversal::OnSignalQualityChanged(uint32 quality) {
1884 cellular()->HandleNewSignalQuality(quality);
1885}
1886
Jason Glasgowaf583282012-04-18 15:18:22 -04001887void CellularCapabilityUniversal::OnFacilityLocksChanged(uint32 locks) {
1888 if (sim_lock_status_.enabled != (locks & MM_MODEM_3GPP_FACILITY_SIM)) {
1889 sim_lock_status_.enabled = locks & MM_MODEM_3GPP_FACILITY_SIM;
1890 OnSimLockStatusChanged();
1891 }
1892}
Jason Glasgowef965562012-04-10 16:12:35 -04001893
Jason Glasgowaf583282012-04-18 15:18:22 -04001894void CellularCapabilityUniversal::OnSimPropertiesChanged(
1895 const DBusPropertiesMap &props,
1896 const vector<string> &/* invalidated_properties */) {
Ben Chan6d0d1e72012-11-06 21:19:28 -08001897 SLOG(Cellular, 2) << __func__;
Jason Glasgowaf583282012-04-18 15:18:22 -04001898 string value;
Jason Glasgowaf583282012-04-18 15:18:22 -04001899 if (DBusProperties::GetString(props, MM_SIM_PROPERTY_SIMIDENTIFIER, &value))
1900 OnSimIdentifierChanged(value);
1901 if (DBusProperties::GetString(props, MM_SIM_PROPERTY_OPERATORIDENTIFIER,
1902 &value))
1903 OnOperatorIdChanged(value);
Arman Ugurayd73783f2013-01-31 16:11:21 -08001904 if (DBusProperties::GetString(props, MM_SIM_PROPERTY_OPERATORNAME, &value))
1905 OnSpnChanged(value);
1906 if (DBusProperties::GetString(props, MM_SIM_PROPERTY_IMSI, &value))
1907 OnImsiChanged(value);
1908 SetHomeProvider();
1909}
Jason Glasgowaf583282012-04-18 15:18:22 -04001910
Arman Ugurayd73783f2013-01-31 16:11:21 -08001911// TODO(armansito): The following methods should probably log their argument
1912// values. Need to learn if any of them need to be scrubbed.
1913void CellularCapabilityUniversal::OnImsiChanged(const std::string &imsi) {
1914 imsi_ = imsi;
1915}
1916
1917void CellularCapabilityUniversal::OnSpnChanged(const std::string &spn) {
1918 spn_ = spn;
Jason Glasgowaf583282012-04-18 15:18:22 -04001919}
1920
1921void CellularCapabilityUniversal::OnSimIdentifierChanged(const string &id) {
1922 sim_identifier_ = id;
Arman Uguray0a3e2792013-01-17 16:31:50 -08001923 UpdatePendingActivationState();
Jason Glasgowaf583282012-04-18 15:18:22 -04001924}
1925
1926void CellularCapabilityUniversal::OnOperatorIdChanged(
1927 const string &operator_id) {
Ben Chan6d0d1e72012-11-06 21:19:28 -08001928 SLOG(Cellular, 2) << "Operator ID = '" << operator_id << "'";
Jason Glasgowaf583282012-04-18 15:18:22 -04001929 operator_id_ = operator_id;
1930}
1931
Jason Glasgow82f9ab32012-04-04 14:27:19 -04001932} // namespace shill