blob: 89d3f7b901a1f35d2408865a87f0534354f266c8 [file] [log] [blame]
Darin Petkovc64fe5e2012-01-11 12:46:13 +01001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Chris Masone3bd3c8c2011-06-13 08:20:26 -07002// 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.h"
6
Darin Petkov0828f5f2011-08-11 10:18:52 -07007#include <netinet/in.h>
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -07008#include <linux/if.h> // Needs definitions from netinet/in.h
Darin Petkov0828f5f2011-08-11 10:18:52 -07009
Chris Masone3bd3c8c2011-06-13 08:20:26 -070010#include <string>
Chris Masone889666b2011-07-03 12:58:50 -070011#include <utility>
12#include <vector>
Chris Masone3bd3c8c2011-06-13 08:20:26 -070013
Eric Shienbrood3e20a232012-02-16 11:35:56 -050014#include <base/bind.h>
Gary Moraina9fb3252012-05-31 12:05:31 -070015#include <base/callback.h>
Ben Chana0ddf462014-02-06 11:32:42 -080016#include <base/files/file_path.h>
Prathmesh Prabhu95afcbb2014-04-08 16:05:42 -070017#include <base/strings/string_util.h>
Ben Chana0ddf462014-02-06 11:32:42 -080018#include <base/strings/stringprintf.h>
Chris Masoneb925cc82011-06-22 15:39:57 -070019#include <chromeos/dbus/service_constants.h>
Chris Masone3bd3c8c2011-06-13 08:20:26 -070020
Eric Shienbrood5de44ab2011-12-05 10:46:27 -050021#include "shill/adaptor_interfaces.h"
Ben Chan539ab022014-02-03 16:34:57 -080022#include "shill/cellular_bearer.h"
Darin Petkovdaf43862011-10-27 11:37:28 +020023#include "shill/cellular_capability_cdma.h"
24#include "shill/cellular_capability_gsm.h"
Jason Glasgow82f9ab32012-04-04 14:27:19 -040025#include "shill/cellular_capability_universal.h"
Arman Uguray72fab6a2013-01-10 19:32:42 -080026#include "shill/cellular_capability_universal_cdma.h"
Chris Masone3bd3c8c2011-06-13 08:20:26 -070027#include "shill/cellular_service.h"
28#include "shill/control_interface.h"
29#include "shill/device.h"
30#include "shill/device_info.h"
Darin Petkov4d6d9412011-08-24 13:19:54 -070031#include "shill/error.h"
Paul Stewart26b327e2011-10-19 11:38:09 -070032#include "shill/event_dispatcher.h"
mukesh agrawal9da07772013-05-15 14:15:17 -070033#include "shill/external_task.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070034#include "shill/logging.h"
Chris Masone3bd3c8c2011-06-13 08:20:26 -070035#include "shill/manager.h"
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -070036#include "shill/mobile_operator_info.h"
mukesh agrawal9da07772013-05-15 14:15:17 -070037#include "shill/ppp_device.h"
mukesh agrawalf407d592013-07-31 11:37:57 -070038#include "shill/ppp_device_factory.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070039#include "shill/profile.h"
Chris Masone889666b2011-07-03 12:58:50 -070040#include "shill/property_accessor.h"
Darin Petkove9d12e02011-07-27 15:09:37 -070041#include "shill/proxy_factory.h"
Darin Petkov0828f5f2011-08-11 10:18:52 -070042#include "shill/rtnl_handler.h"
Jason Glasgow7b461df2012-05-01 16:38:45 -040043#include "shill/store_interface.h"
Gaurav Shah435de2c2011-11-17 19:01:07 -080044#include "shill/technology.h"
Chris Masone3bd3c8c2011-06-13 08:20:26 -070045
Eric Shienbrood3e20a232012-02-16 11:35:56 -050046using base::Bind;
Gary Moraina9fb3252012-05-31 12:05:31 -070047using base::Closure;
mukesh agrawal9da07772013-05-15 14:15:17 -070048using base::FilePath;
Ben Chana0ddf462014-02-06 11:32:42 -080049using base::StringPrintf;
mukesh agrawal9da07772013-05-15 14:15:17 -070050using std::map;
Chris Masone3bd3c8c2011-06-13 08:20:26 -070051using std::string;
Chris Masone889666b2011-07-03 12:58:50 -070052using std::vector;
Chris Masone3bd3c8c2011-06-13 08:20:26 -070053
54namespace shill {
55
Jason Glasgow7b461df2012-05-01 16:38:45 -040056// static
57const char Cellular::kAllowRoaming[] = "AllowRoaming";
Prathmesh Prabhu49ffffd2014-01-09 18:28:55 -080058const int64 Cellular::kDefaultScanningTimeoutMilliseconds = 60000;
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -070059const char Cellular::kGenericServiceNamePrefix[] = "MobileNetwork";
Prathmesh Prabhu95afcbb2014-04-08 16:05:42 -070060unsigned int Cellular::friendly_service_name_id_ = 1;
Jason Glasgow7b461df2012-05-01 16:38:45 -040061
Darin Petkov3335b372011-08-22 11:05:32 -070062Cellular::Operator::Operator() {
63 SetName("");
64 SetCode("");
65 SetCountry("");
66}
67
68Cellular::Operator::~Operator() {}
69
70void Cellular::Operator::CopyFrom(const Operator &oper) {
71 dict_ = oper.dict_;
72}
73
74const string &Cellular::Operator::GetName() const {
Ben Chan7ea768e2013-09-20 15:08:40 -070075 return dict_.find(kOperatorNameKey)->second;
Darin Petkov3335b372011-08-22 11:05:32 -070076}
77
78void Cellular::Operator::SetName(const string &name) {
Ben Chan7ea768e2013-09-20 15:08:40 -070079 dict_[kOperatorNameKey] = name;
Darin Petkov3335b372011-08-22 11:05:32 -070080}
81
82const string &Cellular::Operator::GetCode() const {
Ben Chan7ea768e2013-09-20 15:08:40 -070083 return dict_.find(kOperatorCodeKey)->second;
Darin Petkov3335b372011-08-22 11:05:32 -070084}
85
86void Cellular::Operator::SetCode(const string &code) {
Ben Chan7ea768e2013-09-20 15:08:40 -070087 dict_[kOperatorCodeKey] = code;
Darin Petkov3335b372011-08-22 11:05:32 -070088}
89
90const string &Cellular::Operator::GetCountry() const {
Ben Chan7ea768e2013-09-20 15:08:40 -070091 return dict_.find(kOperatorCountryKey)->second;
Darin Petkov3335b372011-08-22 11:05:32 -070092}
93
94void Cellular::Operator::SetCountry(const string &country) {
Ben Chan7ea768e2013-09-20 15:08:40 -070095 dict_[kOperatorCountryKey] = country;
Darin Petkov3335b372011-08-22 11:05:32 -070096}
97
98const Stringmap &Cellular::Operator::ToDict() const {
99 return dict_;
100}
101
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700102Cellular::Cellular(ModemInfo *modem_info,
Darin Petkove9d12e02011-07-27 15:09:37 -0700103 const string &link_name,
Darin Petkov3335b372011-08-22 11:05:32 -0700104 const string &address,
Darin Petkove9d12e02011-07-27 15:09:37 -0700105 int interface_index,
106 Type type,
107 const string &owner,
Jason Glasgowa585fc32012-06-06 11:04:09 -0400108 const string &service,
Darin Petkov137884a2011-10-26 18:52:47 +0200109 const string &path,
Ben Chan3ecdf822012-08-06 12:29:23 -0700110 ProxyFactory *proxy_factory)
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700111 : Device(modem_info->control_interface(),
112 modem_info->dispatcher(),
113 modem_info->metrics(),
114 modem_info->manager(),
Darin Petkove9d12e02011-07-27 15:09:37 -0700115 link_name,
Chris Masone626719f2011-08-18 16:58:48 -0700116 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -0800117 interface_index,
118 Technology::kCellular),
Thieu Le64b0fe52012-08-08 14:57:36 -0700119 weak_ptr_factory_(this),
Darin Petkove9d12e02011-07-27 15:09:37 -0700120 state_(kStateDisabled),
Darin Petkovbac96002011-08-09 13:22:00 -0700121 modem_state_(kModemStateUnknown),
Miao-chen Chou70190b32014-05-14 18:24:30 -0700122 home_provider_info_(
123 new MobileOperatorInfo(modem_info->dispatcher(), "HomeProvider")),
124 serving_operator_info_(
125 new MobileOperatorInfo(modem_info->dispatcher(), "ServingOperator")),
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -0700126 mobile_operator_info_observer_(
Prathmesh Prabhu8599e052014-04-25 14:20:22 -0700127 new Cellular::MobileOperatorInfoObserver(this)),
Darin Petkove9d12e02011-07-27 15:09:37 -0700128 dbus_owner_(owner),
Jason Glasgowa585fc32012-06-06 11:04:09 -0400129 dbus_service_(service),
Darin Petkove9d12e02011-07-27 15:09:37 -0700130 dbus_path_(path),
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -0800131 scanning_supported_(false),
Prathmesh Prabhu49ffffd2014-01-09 18:28:55 -0800132 scanning_(false),
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -0800133 provider_requires_roaming_(false),
134 scan_interval_(0),
135 sim_present_(false),
Prathmesh Prabhu700ff4d2014-01-16 15:59:33 -0800136 prl_version_(0),
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700137 modem_info_(modem_info),
Prathmesh Prabhu401a5b92014-04-29 17:37:55 -0700138 type_(type),
Ben Chan3ecdf822012-08-06 12:29:23 -0700139 proxy_factory_(proxy_factory),
mukesh agrawalf407d592013-07-31 11:37:57 -0700140 ppp_device_factory_(PPPDeviceFactory::GetInstance()),
Thieu Le26fc01b2013-01-28 12:08:48 -0800141 allow_roaming_(false),
Prathmesh Prabhu49ffffd2014-01-09 18:28:55 -0800142 proposed_scan_in_progress_(false),
mukesh agrawalfc362912013-08-06 18:10:07 -0700143 explicit_disconnect_(false),
Prathmesh Prabhu49ffffd2014-01-09 18:28:55 -0800144 is_ppp_authenticating_(false),
145 scanning_timeout_milliseconds_(kDefaultScanningTimeoutMilliseconds) {
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -0800146 RegisterProperties();
Prathmesh Prabhu92df6192014-04-29 18:08:08 -0700147 InitCapability(type);
148
Prathmesh Prabhu8599e052014-04-25 14:20:22 -0700149 // TODO(pprabhu) Split MobileOperatorInfo into a context that stores the
150 // costly database, and lighter objects that |Cellular| can own.
Prathmesh Prabhu92df6192014-04-29 18:08:08 -0700151 // crbug.com/363874
Prathmesh Prabhu8599e052014-04-25 14:20:22 -0700152 home_provider_info_->Init();
153 serving_operator_info_->Init();
154 home_provider_info()->AddObserver(mobile_operator_info_observer_.get());
155 serving_operator_info()->AddObserver(mobile_operator_info_observer_.get());
Prathmesh Prabhu92df6192014-04-29 18:08:08 -0700156
Ben Chanfad4a0b2012-04-18 15:49:59 -0700157 SLOG(Cellular, 2) << "Cellular device " << this->link_name()
158 << " initialized.";
Chris Masone3bd3c8c2011-06-13 08:20:26 -0700159}
160
Prathmesh Prabhub6ffd182014-01-28 16:56:31 -0800161Cellular::~Cellular() {
162 // Under certain conditions, Cellular::StopModem may not be
163 // called before the Cellular device is destroyed. This happens if the dbus
164 // modem exported by the modem-manager daemon disappears soon after the modem
165 // is disabled, not giving shill enough time to complete the disable
166 // operation.
167 // In that case, the termination action associated with this cellular object
Prathmesh Prabhu527b04b2014-04-03 17:39:08 -0700168 // may not have been removed.
Prathmesh Prabhub6ffd182014-01-28 16:56:31 -0800169 manager()->RemoveTerminationAction(FriendlyName());
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -0700170
Prathmesh Prabhu8599e052014-04-25 14:20:22 -0700171 home_provider_info()->RemoveObserver(mobile_operator_info_observer_.get());
172 serving_operator_info()->RemoveObserver(
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -0700173 mobile_operator_info_observer_.get());
Prathmesh Prabhu92df6192014-04-29 18:08:08 -0700174 // Explicitly delete the observer to ensure that it is destroyed before the
175 // handle to |capability_| that it holds.
176 mobile_operator_info_observer_.reset();
Prathmesh Prabhub6ffd182014-01-28 16:56:31 -0800177}
Darin Petkove9d12e02011-07-27 15:09:37 -0700178
Jason Glasgow7b461df2012-05-01 16:38:45 -0400179bool Cellular::Load(StoreInterface *storage) {
180 const string id = GetStorageIdentifier();
181 if (!storage->ContainsGroup(id)) {
182 LOG(WARNING) << "Device is not available in the persistent store: " << id;
183 return false;
184 }
185 storage->GetBool(id, kAllowRoaming, &allow_roaming_);
186 return Device::Load(storage);
187}
188
189bool Cellular::Save(StoreInterface *storage) {
190 const string id = GetStorageIdentifier();
191 storage->SetBool(id, kAllowRoaming, allow_roaming_);
192 return Device::Save(storage);
193}
194
Darin Petkovcc044422011-08-17 13:30:06 -0700195// static
196string Cellular::GetStateString(State state) {
197 switch (state) {
Ben Chanf45b3232013-10-08 20:54:01 -0700198 case kStateDisabled:
199 return "CellularStateDisabled";
200 case kStateEnabled:
201 return "CellularStateEnabled";
202 case kStateRegistered:
203 return "CellularStateRegistered";
204 case kStateConnected:
205 return "CellularStateConnected";
206 case kStateLinked:
207 return "CellularStateLinked";
208 default:
209 NOTREACHED();
Darin Petkove9d12e02011-07-27 15:09:37 -0700210 }
Darin Petkovcc044422011-08-17 13:30:06 -0700211 return StringPrintf("CellularStateUnknown-%d", state);
Darin Petkov0828f5f2011-08-11 10:18:52 -0700212}
213
Ben Chanf45b3232013-10-08 20:54:01 -0700214// static
215string Cellular::GetModemStateString(ModemState modem_state) {
216 switch (modem_state) {
Ben Chan7b7d63d2013-10-18 14:53:05 -0700217 case kModemStateFailed:
218 return "CellularModemStateFailed";
Ben Chanf45b3232013-10-08 20:54:01 -0700219 case kModemStateUnknown:
220 return "CellularModemStateUnknown";
221 case kModemStateInitializing:
222 return "CellularModemStateInitializing";
223 case kModemStateLocked:
224 return "CellularModemStateLocked";
225 case kModemStateDisabled:
226 return "CellularModemStateDisabled";
227 case kModemStateDisabling:
228 return "CellularModemStateDisabling";
229 case kModemStateEnabling:
230 return "CellularModemStateEnabling";
231 case kModemStateEnabled:
232 return "CellularModemStateEnabled";
233 case kModemStateSearching:
234 return "CellularModemStateSearching";
235 case kModemStateRegistered:
236 return "CellularModemStateRegistered";
237 case kModemStateDisconnecting:
238 return "CellularModemStateDisconnecting";
239 case kModemStateConnecting:
240 return "CellularModemStateConnecting";
241 case kModemStateConnected:
242 return "CellularModemStateConnected";
243 default:
244 NOTREACHED();
245 }
246 return StringPrintf("CellularModemStateUnknown-%d", modem_state);
247}
248
Eric Shienbrood0db6a9b2012-03-30 16:11:39 -0400249string Cellular::GetTechnologyFamily(Error *error) {
250 return capability_->GetTypeString();
251}
252
Darin Petkov0828f5f2011-08-11 10:18:52 -0700253void Cellular::SetState(State state) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700254 SLOG(Cellular, 2) << GetStateString(state_) << " -> "
255 << GetStateString(state);
Darin Petkov0828f5f2011-08-11 10:18:52 -0700256 state_ = state;
Chris Masone3bd3c8c2011-06-13 08:20:26 -0700257}
258
Jason Glasgow7b461df2012-05-01 16:38:45 -0400259void Cellular::HelpRegisterDerivedBool(
260 const string &name,
261 bool(Cellular::*get)(Error *error),
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700262 bool(Cellular::*set)(const bool &value, Error *error)) {
Jason Glasgow7b461df2012-05-01 16:38:45 -0400263 mutable_store()->RegisterDerivedBool(
264 name,
265 BoolAccessor(
266 new CustomAccessor<Cellular, bool>(this, get, set)));
267}
268
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700269void Cellular::HelpRegisterConstDerivedString(
Eric Shienbrood0db6a9b2012-03-30 16:11:39 -0400270 const string &name,
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700271 string(Cellular::*get)(Error *)) {
Eric Shienbrood0db6a9b2012-03-30 16:11:39 -0400272 mutable_store()->RegisterDerivedString(
273 name,
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700274 StringAccessor(new CustomAccessor<Cellular, string>(this, get, NULL)));
Eric Shienbrood0db6a9b2012-03-30 16:11:39 -0400275}
276
Eric Shienbrood9a245532012-03-07 14:20:39 -0500277void Cellular::Start(Error *error,
278 const EnabledStateChangedCallback &callback) {
Jason Glasgow4a490792012-04-10 15:02:05 -0400279 DCHECK(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700280 SLOG(Cellular, 2) << __func__ << ": " << GetStateString(state_);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500281 if (state_ != kStateDisabled) {
282 return;
283 }
Thieu Le64b0fe52012-08-08 14:57:36 -0700284 ResultCallback cb = Bind(&Cellular::StartModemCallback,
285 weak_ptr_factory_.GetWeakPtr(),
286 callback);
287 capability_->StartModem(error, cb);
Chris Masone3bd3c8c2011-06-13 08:20:26 -0700288}
289
Eric Shienbrood9a245532012-03-07 14:20:39 -0500290void Cellular::Stop(Error *error,
291 const EnabledStateChangedCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700292 SLOG(Cellular, 2) << __func__ << ": " << GetStateString(state_);
Thieu Le26fc01b2013-01-28 12:08:48 -0800293 explicit_disconnect_ = true;
Thieu Le64b0fe52012-08-08 14:57:36 -0700294 ResultCallback cb = Bind(&Cellular::StopModemCallback,
295 weak_ptr_factory_.GetWeakPtr(),
296 callback);
297 capability_->StopModem(error, cb);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500298}
299
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400300bool Cellular::IsUnderlyingDeviceEnabled() const {
301 return IsEnabledModemState(modem_state_);
302}
303
Thieu Led0012052012-07-25 16:09:09 -0700304bool Cellular::IsModemRegistered() const {
305 return (modem_state_ == Cellular::kModemStateRegistered ||
306 modem_state_ == Cellular::kModemStateConnecting ||
307 modem_state_ == Cellular::kModemStateConnected);
308}
309
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400310// static
311bool Cellular::IsEnabledModemState(ModemState state) {
312 switch (state) {
Ben Chan7b7d63d2013-10-18 14:53:05 -0700313 case kModemStateFailed:
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400314 case kModemStateUnknown:
315 case kModemStateDisabled:
316 case kModemStateInitializing:
317 case kModemStateLocked:
318 case kModemStateDisabling:
319 case kModemStateEnabling:
320 return false;
321 case kModemStateEnabled:
322 case kModemStateSearching:
323 case kModemStateRegistered:
324 case kModemStateDisconnecting:
325 case kModemStateConnecting:
326 case kModemStateConnected:
327 return true;
328 }
329 return false;
330}
331
Thieu Le37b90032012-05-15 15:18:41 -0700332void Cellular::StartModemCallback(const EnabledStateChangedCallback &callback,
333 const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700334 SLOG(Cellular, 2) << __func__ << ": " << GetStateString(state_);
Gary Morainbaeefdf2012-04-30 14:53:35 -0700335 if (error.IsSuccess() && (state_ == kStateDisabled)) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500336 SetState(kStateEnabled);
Jason Glasgow4380f0d2012-05-03 18:05:04 -0400337 // Registration state updates may have been ignored while the
338 // modem was not yet marked enabled.
339 HandleNewRegistrationState();
340 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500341 callback.Run(error);
342}
343
Thieu Le37b90032012-05-15 15:18:41 -0700344void Cellular::StopModemCallback(const EnabledStateChangedCallback &callback,
345 const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700346 SLOG(Cellular, 2) << __func__ << ": " << GetStateString(state_);
Thieu Le26fc01b2013-01-28 12:08:48 -0800347 explicit_disconnect_ = false;
Thieu Le37b90032012-05-15 15:18:41 -0700348 // Destroy the cellular service regardless of any errors that occur during
349 // the stop process since we do not know the state of the modem at this
350 // point.
351 DestroyService();
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500352 if (state_ != kStateDisabled)
Eric Shienbrood9a245532012-03-07 14:20:39 -0500353 SetState(kStateDisabled);
354 callback.Run(error);
Ben Chan9f3dcf82013-09-25 18:04:58 -0700355 // In case no termination action was executed (and TerminationActionComplete
356 // was not invoked) in response to a suspend request, any registered
357 // termination action needs to be removed explicitly.
358 manager()->RemoveTerminationAction(FriendlyName());
Chris Masone3bd3c8c2011-06-13 08:20:26 -0700359}
360
Ben Chan3ecdf822012-08-06 12:29:23 -0700361void Cellular::InitCapability(Type type) {
Darin Petkov5f316f62011-11-18 12:10:26 +0100362 // TODO(petkov): Consider moving capability construction into a factory that's
363 // external to the Cellular class.
Ben Chanfad4a0b2012-04-18 15:49:59 -0700364 SLOG(Cellular, 2) << __func__ << "(" << type << ")";
Darin Petkov5f316f62011-11-18 12:10:26 +0100365 switch (type) {
Darin Petkovdaf43862011-10-27 11:37:28 +0200366 case kTypeGSM:
Thieu Lece4483e2013-01-23 15:12:03 -0800367 capability_.reset(new CellularCapabilityGSM(this,
368 proxy_factory_,
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700369 modem_info_));
Darin Petkovdaf43862011-10-27 11:37:28 +0200370 break;
371 case kTypeCDMA:
Thieu Lece4483e2013-01-23 15:12:03 -0800372 capability_.reset(new CellularCapabilityCDMA(this,
373 proxy_factory_,
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700374 modem_info_));
Darin Petkovdaf43862011-10-27 11:37:28 +0200375 break;
David Rochbergfa1d31d2012-03-20 10:38:07 -0400376 case kTypeUniversal:
Arman Ugurayc7b15602013-02-16 00:56:18 -0800377 capability_.reset(new CellularCapabilityUniversal(
378 this,
379 proxy_factory_,
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700380 modem_info_));
David Rochbergfa1d31d2012-03-20 10:38:07 -0400381 break;
Arman Uguray72fab6a2013-01-10 19:32:42 -0800382 case kTypeUniversalCDMA:
383 capability_.reset(new CellularCapabilityUniversalCDMA(
384 this,
385 proxy_factory_,
386 modem_info_));
387 break;
Darin Petkovdaf43862011-10-27 11:37:28 +0200388 default: NOTREACHED();
389 }
Prathmesh Prabhu92df6192014-04-29 18:08:08 -0700390 mobile_operator_info_observer_->set_capability(capability_.get());
Darin Petkovdaf43862011-10-27 11:37:28 +0200391}
392
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400393void Cellular::Activate(const string &carrier,
Eric Shienbrood9a245532012-03-07 14:20:39 -0500394 Error *error, const ResultCallback &callback) {
395 capability_->Activate(carrier, error, callback);
Darin Petkov9ae310f2011-08-30 15:41:13 -0700396}
397
Arman Ugurayc7b15602013-02-16 00:56:18 -0800398void Cellular::CompleteActivation(Error *error) {
399 capability_->CompleteActivation(error);
400}
401
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500402void Cellular::RegisterOnNetwork(const string &network_id,
Eric Shienbrood9a245532012-03-07 14:20:39 -0500403 Error *error,
404 const ResultCallback &callback) {
405 capability_->RegisterOnNetwork(network_id, error, callback);
Darin Petkova3d3be52011-11-14 21:34:16 +0100406}
407
Eric Shienbrood9a245532012-03-07 14:20:39 -0500408void Cellular::RequirePIN(const string &pin, bool require,
409 Error *error, const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700410 SLOG(Cellular, 2) << __func__ << "(" << require << ")";
Eric Shienbrood9a245532012-03-07 14:20:39 -0500411 capability_->RequirePIN(pin, require, error, callback);
Darin Petkove42e1012011-08-31 12:35:04 -0700412}
413
Eric Shienbrood9a245532012-03-07 14:20:39 -0500414void Cellular::EnterPIN(const string &pin,
415 Error *error, const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700416 SLOG(Cellular, 2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500417 capability_->EnterPIN(pin, error, callback);
Darin Petkove42e1012011-08-31 12:35:04 -0700418}
419
Darin Petkovc64fe5e2012-01-11 12:46:13 +0100420void Cellular::UnblockPIN(const string &unblock_code,
421 const string &pin,
Eric Shienbrood9a245532012-03-07 14:20:39 -0500422 Error *error, const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700423 SLOG(Cellular, 2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500424 capability_->UnblockPIN(unblock_code, pin, error, callback);
Darin Petkove42e1012011-08-31 12:35:04 -0700425}
426
Eric Shienbrood9a245532012-03-07 14:20:39 -0500427void Cellular::ChangePIN(const string &old_pin, const string &new_pin,
428 Error *error, const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700429 SLOG(Cellular, 2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500430 capability_->ChangePIN(old_pin, new_pin, error, callback);
Darin Petkove42e1012011-08-31 12:35:04 -0700431}
432
Ben Chan5d0d32c2013-01-08 02:05:29 -0800433void Cellular::Reset(Error *error, const ResultCallback &callback) {
434 SLOG(Cellular, 2) << __func__;
435 capability_->Reset(error, callback);
436}
437
Darin Petkovc37a9c42012-09-06 15:28:22 +0200438void Cellular::SetCarrier(const string &carrier,
439 Error *error, const ResultCallback &callback) {
440 SLOG(Cellular, 2) << __func__ << "(" << carrier << ")";
441 capability_->SetCarrier(carrier, error, callback);
442}
443
Ben Chanbcc6e012013-11-04 14:28:37 -0800444bool Cellular::IsIPv6Allowed() const {
445 // A cellular device is disabled before the system goes into suspend mode.
446 // However, outstanding TCP sockets may not be nuked when the associated
447 // network interface goes down. When the system resumes from suspend, the
448 // cellular device is re-enabled and may reconnect to the network, which
449 // acquire a new IPv6 address on the network interface. However, those
450 // outstanding TCP sockets may initiate traffic with the old IPv6 address.
451 // Some network may not like the fact that two IPv6 addresses originated from
452 // the same modem within a connection session and may drop the connection.
453 // Here we disable IPv6 support on cellular devices to work around the issue.
454 //
455 // TODO(benchan): Resolve the IPv6 issue in a different way and then
456 // re-enable IPv6 support on cellular devices.
457 return false;
458}
459
mukesh agrawal5d851b12013-07-11 14:09:41 -0700460void Cellular::DropConnection() {
461 if (ppp_device_) {
462 // For PPP dongles, IP configuration is handled on the |ppp_device_|,
463 // rather than the netdev plumbed into |this|.
464 ppp_device_->DropConnection();
465 } else {
466 Device::DropConnection();
467 }
468}
469
mukesh agrawal0381f9a2013-07-11 16:41:52 -0700470void Cellular::SetServiceState(Service::ConnectState state) {
471 if (ppp_device_) {
472 ppp_device_->SetServiceState(state);
mukesh agrawalfc362912013-08-06 18:10:07 -0700473 } else if (selected_service()) {
mukesh agrawal0381f9a2013-07-11 16:41:52 -0700474 Device::SetServiceState(state);
mukesh agrawalfc362912013-08-06 18:10:07 -0700475 } else if (service_) {
476 service_->SetState(state);
477 } else {
478 LOG(WARNING) << "State change with no Service.";
mukesh agrawal0381f9a2013-07-11 16:41:52 -0700479 }
480}
481
482void Cellular::SetServiceFailure(Service::ConnectFailure failure_state) {
483 if (ppp_device_) {
484 ppp_device_->SetServiceFailure(failure_state);
mukesh agrawalfc362912013-08-06 18:10:07 -0700485 } else if (selected_service()) {
mukesh agrawal0381f9a2013-07-11 16:41:52 -0700486 Device::SetServiceFailure(failure_state);
mukesh agrawalfc362912013-08-06 18:10:07 -0700487 } else if (service_) {
488 service_->SetFailure(failure_state);
489 } else {
490 LOG(WARNING) << "State change with no Service.";
mukesh agrawal0381f9a2013-07-11 16:41:52 -0700491 }
492}
493
494void Cellular::SetServiceFailureSilent(Service::ConnectFailure failure_state) {
495 if (ppp_device_) {
496 ppp_device_->SetServiceFailureSilent(failure_state);
mukesh agrawalfc362912013-08-06 18:10:07 -0700497 } else if (selected_service()) {
mukesh agrawal0381f9a2013-07-11 16:41:52 -0700498 Device::SetServiceFailureSilent(failure_state);
mukesh agrawalfc362912013-08-06 18:10:07 -0700499 } else if (service_) {
500 service_->SetFailureSilent(failure_state);
501 } else {
502 LOG(WARNING) << "State change with no Service.";
mukesh agrawal0381f9a2013-07-11 16:41:52 -0700503 }
504}
505
mukesh agrawal28185512013-10-18 16:57:09 -0700506void Cellular::OnAfterResume() {
507 SLOG(Cellular, 2) << __func__;
508 if (enabled_persistent()) {
509 LOG(INFO) << "Restarting modem after resume.";
510
511 // If we started disabling the modem before suspend, but that
512 // suspend is still in progress, then we are not yet in
513 // kStateDisabled. That's a problem, because Cellular::Start
514 // returns immediately in that case. Hack around that by forcing
515 // |state_| here.
516 //
517 // TODO(quiche): Remove this hack. Maybe
518 // CellularCapabilityUniversal should generate separate
519 // notifications for Stop_Disable, and Stop_PowerDown. Then we'd
520 // update our state to kStateDisabled when Stop_Disable completes.
521 state_ = kStateDisabled;
522
523 Error error;
524 SetEnabledUnchecked(true, &error, Bind(LogRestartModemResult));
525 if (error.IsSuccess()) {
526 LOG(INFO) << "Modem restart completed immediately.";
527 } else if (error.IsOngoing()) {
528 LOG(INFO) << "Modem restart in progress.";
529 } else {
530 LOG(WARNING) << "Modem restart failed: " << error;
531 }
532 }
533 // TODO(quiche): Consider if this should be conditional. If, e.g.,
534 // the device was still disabling when we suspended, will trying to
535 // renew DHCP here cause problems?
536 Device::OnAfterResume();
537}
538
Wade Guthrie4823f4f2013-07-25 10:03:03 -0700539void Cellular::Scan(ScanType /*scan_type*/, Error *error,
540 const string &/*reason*/) {
Prathmesh Prabhu49ffffd2014-01-09 18:28:55 -0800541 SLOG(Cellular, 2) << __func__;
542 CHECK(error);
543 if (proposed_scan_in_progress_) {
544 Error::PopulateAndLog(error, Error::kInProgress, "Already scanning");
545 return;
546 }
547
Wade Guthrie68d41092013-04-02 12:56:02 -0700548 // |scan_type| is ignored because Cellular only does a full scan.
Prathmesh Prabhu49ffffd2014-01-09 18:28:55 -0800549 ResultStringmapsCallback cb = Bind(&Cellular::OnScanReply,
550 weak_ptr_factory_.GetWeakPtr());
551 capability_->Scan(error, cb);
552 // An immediate failure in |cabapility_->Scan(...)| is indicated through the
553 // |error| argument.
554 if (error->IsFailure())
555 return;
556
557 proposed_scan_in_progress_ = true;
558 UpdateScanning();
559}
560
561void Cellular::OnScanReply(const Stringmaps &found_networks,
562 const Error &error) {
563 proposed_scan_in_progress_ = false;
564 UpdateScanning();
565
566 // TODO(jglasgow): fix error handling.
567 // At present, there is no way of notifying user of this asynchronous error.
568 if (error.IsFailure()) {
569 clear_found_networks();
570 return;
571 }
572
573 set_found_networks(found_networks);
Darin Petkovceb68172011-07-29 14:47:48 -0700574}
575
Darin Petkovd9661952011-08-03 16:25:42 -0700576void Cellular::HandleNewRegistrationState() {
mukesh agrawal9da07772013-05-15 14:15:17 -0700577 SLOG(Cellular, 2) << __func__
578 << ": (new state " << GetStateString(state_) << ")";
Ben Chan09fa2a02012-11-07 22:09:09 -0800579 if (capability_->IsServiceActivationRequired()) {
580 if (state_ == kStateEnabled && !service_.get()) {
Arman Uguray6bb252d2013-05-15 14:29:53 -0700581 SLOG(Cellular, 2) << "Service activation required. Creating dummy "
582 << "service.";
Ben Chan09fa2a02012-11-07 22:09:09 -0800583 CreateService();
584 }
585 return;
586 }
Darin Petkovb72cf402011-11-22 14:51:39 +0100587 if (!capability_->IsRegistered()) {
Thieu Le26fc01b2013-01-28 12:08:48 -0800588 if (!explicit_disconnect_ &&
589 (state_ == kStateLinked || state_ == kStateConnected) &&
590 service_.get())
591 metrics()->NotifyCellularDeviceDrop(
mukesh agrawal09e08112013-08-16 13:26:44 -0700592 capability_->GetNetworkTechnologyString(), service_->strength());
Darin Petkov2c377382012-01-11 11:40:43 +0100593 DestroyService();
Darin Petkov0828f5f2011-08-11 10:18:52 -0700594 if (state_ == kStateLinked ||
595 state_ == kStateConnected ||
596 state_ == kStateRegistered) {
597 SetState(kStateEnabled);
Darin Petkovd9661952011-08-03 16:25:42 -0700598 }
599 return;
600 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500601 // In Disabled state, defer creating a service until fully
602 // enabled. UI will ignore the appearance of a new service
603 // on a disabled device.
604 if (state_ == kStateDisabled) {
605 return;
606 }
Darin Petkovd9661952011-08-03 16:25:42 -0700607 if (state_ == kStateEnabled) {
Darin Petkov0828f5f2011-08-11 10:18:52 -0700608 SetState(kStateRegistered);
Darin Petkovd9661952011-08-03 16:25:42 -0700609 }
610 if (!service_.get()) {
Thieu Le18c11072013-01-28 17:21:37 -0800611 metrics()->NotifyDeviceScanFinished(interface_index());
Darin Petkovd9661952011-08-03 16:25:42 -0700612 CreateService();
613 }
Darin Petkov3e509242011-11-10 14:46:44 +0100614 capability_->GetSignalQuality();
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500615 if (state_ == kStateRegistered && modem_state_ == kModemStateConnected)
616 OnConnected();
Darin Petkovb72cf402011-11-22 14:51:39 +0100617 service_->SetNetworkTechnology(capability_->GetNetworkTechnologyString());
618 service_->SetRoamingState(capability_->GetRoamingStateString());
Christopher Wiley9169d252012-11-30 15:13:39 -0800619 manager()->UpdateService(service_);
Darin Petkovd9661952011-08-03 16:25:42 -0700620}
621
Darin Petkovd9661952011-08-03 16:25:42 -0700622void Cellular::HandleNewSignalQuality(uint32 strength) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700623 SLOG(Cellular, 2) << "Signal strength: " << strength;
Darin Petkovd78ee7e2012-01-12 11:21:10 +0100624 if (service_) {
625 service_->SetStrength(strength);
Darin Petkovd9661952011-08-03 16:25:42 -0700626 }
627}
628
629void Cellular::CreateService() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700630 SLOG(Cellular, 2) << __func__;
Darin Petkovd9661952011-08-03 16:25:42 -0700631 CHECK(!service_.get());
Prathmesh Prabhu0d36b4f2013-04-01 11:45:54 -0700632 service_ = new CellularService(modem_info_, this);
Darin Petkovae0c64e2011-11-15 15:50:27 +0100633 capability_->OnServiceCreated();
Prathmesh Prabhu401a5b92014-04-29 17:37:55 -0700634
635 // Storage identifier must be set only once, and before registering the
636 // service with the manager, since we key off of this identifier to
637 // determine the profile to load.
638 // TODO(pprabhu) Make profile matching more robust (crbug.com/369755)
639 string service_id;
640 if (home_provider_info_->IsMobileNetworkOperatorKnown() &&
Alex Vakulenko8a532292014-06-16 17:18:44 -0700641 !home_provider_info_->uuid().empty()) {
Prathmesh Prabhu401a5b92014-04-29 17:37:55 -0700642 service_id = home_provider_info_->uuid();
643 } else if (serving_operator_info_->IsMobileNetworkOperatorKnown() &&
644 !serving_operator_info_->uuid().empty()) {
645 service_id = serving_operator_info_->uuid();
646 } else {
647 switch (type_) {
648 case kTypeGSM:
649 case kTypeUniversal:
650 if (!sim_identifier().empty()) {
651 service_id = sim_identifier();
652 }
653 break;
654
655 case kTypeCDMA:
656 case kTypeUniversalCDMA:
657 if (!meid().empty()) {
658 service_id = meid();
659 }
660 break;
661
662 default:
663 NOTREACHED();
664 }
665 }
666
667 if (!service_id.empty()) {
668 string storage_id = base::StringPrintf(
669 "%s_%s_%s",
670 kTypeCellular, address().c_str(), service_id.c_str());
671 service()->SetStorageIdentifier(storage_id);
672 }
673
Darin Petkov31332412012-01-28 01:50:02 +0100674 manager()->RegisterService(service_);
Prathmesh Prabhu401a5b92014-04-29 17:37:55 -0700675
Prathmesh Prabhu49ffffd2014-01-09 18:28:55 -0800676 // We might have missed a property update because the service wasn't created
677 // ealier.
678 UpdateScanning();
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -0700679 mobile_operator_info_observer_->OnOperatorChanged();
Darin Petkovf5f61e02011-07-29 11:35:40 -0700680}
681
Darin Petkov2c377382012-01-11 11:40:43 +0100682void Cellular::DestroyService() {
mukesh agrawal6813e762013-07-10 19:05:08 -0700683 SLOG(Cellular, 2) << __func__;
Darin Petkov0f5dfa02012-11-14 16:35:32 +0100684 DropConnection();
Darin Petkov2c377382012-01-11 11:40:43 +0100685 if (service_) {
Darin Petkov457728b2013-01-09 09:49:08 +0100686 LOG(INFO) << "Deregistering cellular service " << service_->unique_name()
Darin Petkov0f5dfa02012-11-14 16:35:32 +0100687 << " for device " << link_name();
Darin Petkov2c377382012-01-11 11:40:43 +0100688 manager()->DeregisterService(service_);
689 service_ = NULL;
690 }
Darin Petkov2c377382012-01-11 11:40:43 +0100691}
692
Darin Petkov4d6d9412011-08-24 13:19:54 -0700693void Cellular::Connect(Error *error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700694 SLOG(Cellular, 2) << __func__;
Eric Shienbrood30bc0ec2012-03-21 18:19:46 -0400695 if (state_ == kStateConnected || state_ == kStateLinked) {
Paul Stewartbe005172011-11-02 18:10:29 -0700696 Error::PopulateAndLog(error, Error::kAlreadyConnected,
697 "Already connected; connection request ignored.");
Darin Petkovc5f56562011-08-06 16:40:05 -0700698 return;
Thieu Lec7d8cd12013-02-13 11:38:14 -0800699 } else if (state_ != kStateRegistered) {
700 Error::PopulateAndLog(error, Error::kNotRegistered,
701 "Modem not registered; connection request ignored.");
702 return;
Darin Petkovc5f56562011-08-06 16:40:05 -0700703 }
Darin Petkovd2045802011-08-23 11:09:25 -0700704
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400705 if (!capability_->AllowRoaming() &&
Ben Chan7ea768e2013-09-20 15:08:40 -0700706 service_->roaming_state() == kRoamingStateRoaming) {
Paul Stewartbe005172011-11-02 18:10:29 -0700707 Error::PopulateAndLog(error, Error::kNotOnHomeNetwork,
708 "Roaming disallowed; connection request ignored.");
Darin Petkovd2045802011-08-23 11:09:25 -0700709 return;
710 }
711
Darin Petkovc5f56562011-08-06 16:40:05 -0700712 DBusPropertiesMap properties;
Darin Petkovae0c64e2011-11-15 15:50:27 +0100713 capability_->SetupConnectProperties(&properties);
Thieu Le64b0fe52012-08-08 14:57:36 -0700714 ResultCallback cb = Bind(&Cellular::OnConnectReply,
715 weak_ptr_factory_.GetWeakPtr());
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400716 OnConnecting();
Nathan Williamsb54974f2012-04-19 11:16:30 -0400717 capability_->Connect(properties, error, cb);
Thieu Le7cf36b02013-01-30 17:15:56 -0800718 if (!error->IsSuccess())
719 return;
720
721 bool is_auto_connecting = service_.get() && service_->is_auto_connecting();
722 metrics()->NotifyDeviceConnectStarted(interface_index(), is_auto_connecting);
Nathan Williamsb54974f2012-04-19 11:16:30 -0400723}
724
725// Note that there's no ResultCallback argument to this,
726// since Connect() isn't yet passed one.
727void Cellular::OnConnectReply(const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700728 SLOG(Cellular, 2) << __func__ << "(" << error << ")";
Thieu Lecdb5a212013-01-25 11:17:18 -0800729 if (error.IsSuccess()) {
730 metrics()->NotifyDeviceConnectFinished(interface_index());
Nathan Williamsb54974f2012-04-19 11:16:30 -0400731 OnConnected();
Thieu Lecdb5a212013-01-25 11:17:18 -0800732 } else {
Thieu Leb7aa5f72013-01-31 15:57:48 -0800733 metrics()->NotifyCellularDeviceFailure(error);
Nathan Williamsb54974f2012-04-19 11:16:30 -0400734 OnConnectFailed(error);
Thieu Lecdb5a212013-01-25 11:17:18 -0800735 }
Darin Petkovc5f56562011-08-06 16:40:05 -0700736}
737
Ben Chan9f3dcf82013-09-25 18:04:58 -0700738void Cellular::OnDisabled() {
739 SetEnabled(false);
740}
741
742void Cellular::OnEnabled() {
743 manager()->AddTerminationAction(FriendlyName(),
744 Bind(&Cellular::StartTermination,
745 weak_ptr_factory_.GetWeakPtr()));
746 SetEnabled(true);
747}
748
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400749void Cellular::OnConnecting() {
750 if (service_)
751 service_->SetState(Service::kStateAssociating);
752}
753
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500754void Cellular::OnConnected() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700755 SLOG(Cellular, 2) << __func__;
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400756 if (state_ == kStateConnected || state_ == kStateLinked) {
Ben Chan09fa2a02012-11-07 22:09:09 -0800757 SLOG(Cellular, 2) << "Already connected";
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400758 return;
759 }
Darin Petkov0828f5f2011-08-11 10:18:52 -0700760 SetState(kStateConnected);
Thieu Led4974cd2013-05-23 10:39:28 -0700761 if (!service_) {
762 LOG(INFO) << "Disconnecting due to no cellular service.";
Samuel Tan0d061192014-07-07 15:45:15 -0700763 Disconnect(NULL, "no celluar service");
Thieu Led4974cd2013-05-23 10:39:28 -0700764 } else if (!capability_->AllowRoaming() &&
Ben Chan7ea768e2013-09-20 15:08:40 -0700765 service_->roaming_state() == kRoamingStateRoaming) {
Arman Uguray539c4232012-09-11 10:00:22 -0700766 LOG(INFO) << "Disconnecting due to roaming.";
Samuel Tan0d061192014-07-07 15:45:15 -0700767 Disconnect(NULL, "roaming");
Darin Petkov9c1dcef2012-02-07 15:58:26 +0100768 } else {
769 EstablishLink();
770 }
Darin Petkovbac96002011-08-09 13:22:00 -0700771}
772
Eric Shienbrood30bc0ec2012-03-21 18:19:46 -0400773void Cellular::OnConnectFailed(const Error &error) {
Thieu Leb5954a22012-05-18 10:37:34 -0700774 if (service_)
775 service_->SetFailure(Service::kFailureUnknown);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500776}
777
Samuel Tan0d061192014-07-07 15:45:15 -0700778void Cellular::Disconnect(Error *error, const char *reason) {
779 SLOG(Cellular, 2) << __func__ << ": " << reason;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500780 if (state_ != kStateConnected && state_ != kStateLinked) {
Darin Petkovfb0625e2012-01-16 13:05:56 +0100781 Error::PopulateAndLog(
Eric Shienbrood9a245532012-03-07 14:20:39 -0500782 error, Error::kNotConnected, "Not connected; request ignored.");
Darin Petkovfb0625e2012-01-16 13:05:56 +0100783 return;
784 }
mukesh agrawal5c8ed242013-10-04 11:59:58 -0700785 StopPPP();
Thieu Le26fc01b2013-01-28 12:08:48 -0800786 explicit_disconnect_ = true;
Thieu Le64b0fe52012-08-08 14:57:36 -0700787 ResultCallback cb = Bind(&Cellular::OnDisconnectReply,
Christopher Wiley8a468902012-11-30 11:52:38 -0800788 weak_ptr_factory_.GetWeakPtr());
Nathan Williamsb54974f2012-04-19 11:16:30 -0400789 capability_->Disconnect(error, cb);
790}
791
Christopher Wiley8a468902012-11-30 11:52:38 -0800792void Cellular::OnDisconnectReply(const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700793 SLOG(Cellular, 2) << __func__ << "(" << error << ")";
Thieu Le26fc01b2013-01-28 12:08:48 -0800794 explicit_disconnect_ = false;
Thieu Leb7aa5f72013-01-31 15:57:48 -0800795 if (error.IsSuccess()) {
Nathan Williamsb54974f2012-04-19 11:16:30 -0400796 OnDisconnected();
Thieu Leb7aa5f72013-01-31 15:57:48 -0800797 } else {
798 metrics()->NotifyCellularDeviceFailure(error);
Nathan Williamsb54974f2012-04-19 11:16:30 -0400799 OnDisconnectFailed();
Thieu Leb7aa5f72013-01-31 15:57:48 -0800800 }
Darin Petkovfb0625e2012-01-16 13:05:56 +0100801}
802
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500803void Cellular::OnDisconnected() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700804 SLOG(Cellular, 2) << __func__;
Arman Uguray539c4232012-09-11 10:00:22 -0700805 if (!DisconnectCleanup()) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500806 LOG(WARNING) << "Disconnect occurred while in state "
807 << GetStateString(state_);
Eric Shienbroodcc95c5d2012-03-30 15:25:49 -0400808 }
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500809}
810
811void Cellular::OnDisconnectFailed() {
Arman Uguray539c4232012-09-11 10:00:22 -0700812 SLOG(Cellular, 2) << __func__;
813 // If the modem is in the disconnecting state, then
814 // the disconnect should eventually succeed, so do
815 // nothing.
816 if (modem_state_ == kModemStateDisconnecting) {
817 LOG(WARNING) << "Ignoring failed disconnect while modem is disconnecting.";
818 return;
819 }
820
821 // OnDisconnectFailed got called because no bearers
822 // to disconnect were found. Which means that we shouldn't
823 // really remain in the connected/linked state if we
824 // are in one of those.
825 if (!DisconnectCleanup()) {
826 // otherwise, no-op
827 LOG(WARNING) << "Ignoring failed disconnect while in state "
828 << GetStateString(state_);
829 }
830
831 // TODO(armansito): In either case, shill ends up thinking
832 // that it's disconnected, while for some reason the underlying
833 // modem might still actually be connected. In that case the UI
834 // would be reflecting an incorrect state and a further connection
835 // request would fail. We should perhaps tear down the modem and
836 // restart it here.
Darin Petkovfb0625e2012-01-16 13:05:56 +0100837}
838
Darin Petkovbac96002011-08-09 13:22:00 -0700839void Cellular::EstablishLink() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700840 SLOG(Cellular, 2) << __func__;
Darin Petkov0828f5f2011-08-11 10:18:52 -0700841 CHECK_EQ(kStateConnected, state_);
Ben Chan539ab022014-02-03 16:34:57 -0800842
843 CellularBearer *bearer = capability_->GetActiveBearer();
844 if (bearer && bearer->ipv4_config_method() == IPConfig::kMethodPPP) {
845 LOG(INFO) << "Start PPP connection on " << bearer->data_interface();
846 StartPPP(bearer->data_interface());
847 return;
848 }
849
Darin Petkov0828f5f2011-08-11 10:18:52 -0700850 unsigned int flags = 0;
Paul Stewartac4ac002011-08-26 12:04:26 -0700851 if (manager()->device_info()->GetFlags(interface_index(), &flags) &&
Darin Petkov0828f5f2011-08-11 10:18:52 -0700852 (flags & IFF_UP) != 0) {
853 LinkEvent(flags, IFF_UP);
854 return;
855 }
856 // TODO(petkov): Provide a timeout for a failed link-up request.
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700857 rtnl_handler()->SetInterfaceFlags(interface_index(), IFF_UP, IFF_UP);
Arman Uguray32c76402012-11-27 14:01:13 -0800858
859 // Set state to associating.
860 OnConnecting();
Darin Petkov0828f5f2011-08-11 10:18:52 -0700861}
862
863void Cellular::LinkEvent(unsigned int flags, unsigned int change) {
864 Device::LinkEvent(flags, change);
mukesh agrawalfbc40d22013-06-28 00:25:13 -0700865 if (ppp_task_) {
866 LOG(INFO) << "Ignoring LinkEvent on device with PPP interface.";
867 return;
868 }
869
Darin Petkov0828f5f2011-08-11 10:18:52 -0700870 if ((flags & IFF_UP) != 0 && state_ == kStateConnected) {
Paul Stewartac4ac002011-08-26 12:04:26 -0700871 LOG(INFO) << link_name() << " is up.";
Darin Petkov0828f5f2011-08-11 10:18:52 -0700872 SetState(kStateLinked);
Ben Chan539ab022014-02-03 16:34:57 -0800873
874 // TODO(benchan): IPv6 support is currently disabled for cellular devices.
875 // Check and obtain IPv6 configuration from the bearer when we later enable
876 // IPv6 support on cellular devices.
877 CellularBearer *bearer = capability_->GetActiveBearer();
878 if (bearer && bearer->ipv4_config_method() == IPConfig::kMethodStatic) {
879 SLOG(Cellular, 2) << "Assign static IP configuration from bearer.";
Darin Petkov60b8c3b2011-08-25 11:03:20 -0700880 SelectService(service_);
881 SetServiceState(Service::kStateConfiguring);
Ben Chan539ab022014-02-03 16:34:57 -0800882 AssignIPConfig(*bearer->ipv4_config_properties());
883 return;
Darin Petkov60b8c3b2011-08-25 11:03:20 -0700884 }
Ben Chan539ab022014-02-03 16:34:57 -0800885
886 if (AcquireIPConfig()) {
887 SLOG(Cellular, 2) << "Start DHCP to acquire IP configuration.";
888 SelectService(service_);
889 SetServiceState(Service::kStateConfiguring);
890 return;
891 }
892
893 LOG(ERROR) << "Unable to acquire IP configuration over DHCP.";
894 return;
895 }
896
897 if ((flags & IFF_UP) == 0 && state_ == kStateLinked) {
Arman Uguray32c76402012-11-27 14:01:13 -0800898 LOG(INFO) << link_name() << " is down.";
Darin Petkov0828f5f2011-08-11 10:18:52 -0700899 SetState(kStateConnected);
Arman Uguray32c76402012-11-27 14:01:13 -0800900 DropConnection();
Darin Petkov0828f5f2011-08-11 10:18:52 -0700901 }
Darin Petkovc5f56562011-08-06 16:40:05 -0700902}
903
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400904void Cellular::OnDBusPropertiesChanged(
905 const string &interface,
906 const DBusPropertiesMap &changed_properties,
907 const vector<string> &invalidated_properties) {
908 capability_->OnDBusPropertiesChanged(interface,
909 changed_properties,
910 invalidated_properties);
911}
912
Prathmesh Prabhu95afcbb2014-04-08 16:05:42 -0700913string Cellular::CreateDefaultFriendlyServiceName() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700914 SLOG(Cellular, 2) << __func__;
Prathmesh Prabhu95afcbb2014-04-08 16:05:42 -0700915 return base::StringPrintf("%s_%u",
916 kGenericServiceNamePrefix,
917 friendly_service_name_id_++);
918}
919
920bool Cellular::IsDefaultFriendlyServiceName(const string &service_name) const {
921 return StartsWithASCII(service_name, kGenericServiceNamePrefix, true);
Darin Petkovac635a82012-01-10 16:51:58 +0100922}
923
Arman Uguray1ee93912013-09-24 21:24:10 -0700924void Cellular::OnModemStateChanged(ModemState new_state) {
925 ModemState old_state = modem_state_;
Ben Chanf45b3232013-10-08 20:54:01 -0700926 SLOG(Cellular, 2) << __func__ << ": " << GetModemStateString(old_state)
927 << " -> " << GetModemStateString(new_state);
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400928 if (old_state == new_state) {
Arman Uguray1ee93912013-09-24 21:24:10 -0700929 SLOG(Cellular, 2) << "The new state matches the old state. Nothing to do.";
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400930 return;
931 }
932 set_modem_state(new_state);
Thieu Le5218cf22012-11-26 11:52:57 -0800933 if (old_state >= kModemStateRegistered &&
934 new_state < kModemStateRegistered) {
935 capability_->SetUnregistered(new_state == kModemStateSearching);
936 HandleNewRegistrationState();
937 }
Arman Uguray1ee93912013-09-24 21:24:10 -0700938 if (new_state == kModemStateDisabled) {
Ben Chan9f3dcf82013-09-25 18:04:58 -0700939 OnDisabled();
Arman Uguray1ee93912013-09-24 21:24:10 -0700940 } else if (new_state >= kModemStateEnabled) {
941 if (old_state < kModemStateEnabled) {
942 // Just became enabled, update enabled state.
Ben Chan9f3dcf82013-09-25 18:04:58 -0700943 OnEnabled();
Arman Uguray1ee93912013-09-24 21:24:10 -0700944 }
945 if ((new_state == kModemStateEnabled ||
946 new_state == kModemStateSearching ||
947 new_state == kModemStateRegistered) &&
948 (old_state == kModemStateConnected ||
949 old_state == kModemStateConnecting ||
950 old_state == kModemStateDisconnecting))
951 OnDisconnected();
952 else if (new_state == kModemStateConnecting)
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400953 OnConnecting();
Arman Uguray1ee93912013-09-24 21:24:10 -0700954 else if (new_state == kModemStateConnected &&
955 old_state == kModemStateConnecting)
956 OnConnected();
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400957 }
Prathmesh Prabhu49ffffd2014-01-09 18:28:55 -0800958
959 // Update the kScanningProperty property after we've handled the current state
960 // update completely.
961 UpdateScanning();
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400962}
963
Christopher Wiley1582bdd2012-11-15 11:31:14 -0800964bool Cellular::IsActivating() const {
965 return capability_->IsActivating();
966}
967
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700968bool Cellular::SetAllowRoaming(const bool &value, Error */*error*/) {
Jason Glasgow7b461df2012-05-01 16:38:45 -0400969 SLOG(Cellular, 2) << __func__
970 << "(" << allow_roaming_ << "->" << value << ")";
971 if (allow_roaming_ == value) {
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700972 return false;
Jason Glasgow7b461df2012-05-01 16:38:45 -0400973 }
974 allow_roaming_ = value;
Darin Petkove7c6ad32012-06-29 10:22:09 +0200975 manager()->UpdateDevice(this);
Jason Glasgow7b461df2012-05-01 16:38:45 -0400976
977 // Use AllowRoaming() instead of allow_roaming_ in order to
978 // incorporate provider preferences when evaluating if a disconnect
979 // is required.
980 if (!capability_->AllowRoaming() &&
Ben Chan7ea768e2013-09-20 15:08:40 -0700981 capability_->GetRoamingStateString() == kRoamingStateRoaming) {
Jason Glasgow7b461df2012-05-01 16:38:45 -0400982 Error error;
Samuel Tan0d061192014-07-07 15:45:15 -0700983 Disconnect(&error, __func__);
Jason Glasgow7b461df2012-05-01 16:38:45 -0400984 }
Ben Chan7ea768e2013-09-20 15:08:40 -0700985 adaptor()->EmitBoolChanged(kCellularAllowRoamingProperty, value);
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700986 return true;
Jason Glasgow7b461df2012-05-01 16:38:45 -0400987}
988
Gary Moraina9fb3252012-05-31 12:05:31 -0700989void Cellular::StartTermination() {
Darin Petkov3ec55342012-09-28 14:04:44 +0200990 LOG(INFO) << __func__;
Gary Moraina9fb3252012-05-31 12:05:31 -0700991 Error error;
mukesh agrawal28185512013-10-18 16:57:09 -0700992 StopPPP();
Ben Chan9f3dcf82013-09-25 18:04:58 -0700993 SetEnabledNonPersistent(
994 false,
995 &error,
996 Bind(&Cellular::OnTerminationCompleted, weak_ptr_factory_.GetWeakPtr()));
997 if (error.IsFailure() && error.type() != Error::kInProgress) {
998 // If we fail to disable the modem right away, proceed to suspend instead of
999 // wasting the time to wait for the suspend delay to expire.
1000 LOG(WARNING)
1001 << "Proceed to suspend even through the modem is not yet disabled: "
1002 << error;
1003 OnTerminationCompleted(error);
1004 }
1005}
1006
1007void Cellular::OnTerminationCompleted(const Error &error) {
1008 LOG(INFO) << __func__ << ": " << error;
1009 manager()->TerminationActionComplete(FriendlyName());
1010 manager()->RemoveTerminationAction(FriendlyName());
Gary Moraina9fb3252012-05-31 12:05:31 -07001011}
1012
Arman Uguray539c4232012-09-11 10:00:22 -07001013bool Cellular::DisconnectCleanup() {
Christopher Wiley8a468902012-11-30 11:52:38 -08001014 bool succeeded = false;
Arman Uguray539c4232012-09-11 10:00:22 -07001015 if (state_ == kStateConnected || state_ == kStateLinked) {
1016 SetState(kStateRegistered);
1017 SetServiceFailureSilent(Service::kFailureUnknown);
1018 DestroyIPConfig();
Christopher Wiley8a468902012-11-30 11:52:38 -08001019 succeeded = true;
Arman Uguray539c4232012-09-11 10:00:22 -07001020 }
Christopher Wiley8a468902012-11-30 11:52:38 -08001021 capability_->DisconnectCleanup();
1022 return succeeded;
Arman Uguray539c4232012-09-11 10:00:22 -07001023}
Gary Moraina9fb3252012-05-31 12:05:31 -07001024
mukesh agrawal28185512013-10-18 16:57:09 -07001025// static
1026void Cellular::LogRestartModemResult(const Error &error) {
1027 if (error.IsSuccess()) {
1028 LOG(INFO) << "Modem restart completed.";
1029 } else {
1030 LOG(WARNING) << "Attempt to restart modem failed: " << error;
1031 }
1032}
1033
mukesh agrawal9da07772013-05-15 14:15:17 -07001034void Cellular::StartPPP(const string &serial_device) {
mukesh agrawalfc362912013-08-06 18:10:07 -07001035 SLOG(PPP, 2) << __func__ << " on " << serial_device;
mukesh agrawal35ec8402013-07-19 14:49:08 -07001036 // Detach any SelectedService from this device. It will be grafted onto
1037 // the PPPDevice after PPP is up (in Cellular::Notify).
1038 //
1039 // This has two important effects: 1) kills dhcpcd if it is running.
1040 // 2) stops Cellular::LinkEvent from driving changes to the
1041 // SelectedService.
1042 if (selected_service()) {
1043 CHECK_EQ(service_.get(), selected_service().get());
1044 // Save and restore |service_| state, as DropConnection calls
1045 // SelectService, and SelectService will move selected_service()
1046 // to kStateIdle.
1047 Service::ConnectState original_state(service_->state());
1048 Device::DropConnection(); // Don't redirect to PPPDevice.
1049 service_->SetState(original_state);
1050 } else {
1051 CHECK(!ipconfig()); // Shouldn't have ipconfig without selected_service().
1052 }
mukesh agrawalfbc40d22013-06-28 00:25:13 -07001053
mukesh agrawal9da07772013-05-15 14:15:17 -07001054 base::Callback<void(pid_t, int)> death_callback(
1055 Bind(&Cellular::OnPPPDied, weak_ptr_factory_.GetWeakPtr()));
1056 vector<string> args;
1057 map<string, string> environment;
1058 Error error;
1059 if (SLOG_IS_ON(PPP, 5)) {
1060 args.push_back("debug");
1061 }
1062 args.push_back("nodetach");
1063 args.push_back("nodefaultroute"); // Don't let pppd muck with routing table.
1064 args.push_back("usepeerdns"); // Request DNS servers.
1065 args.push_back("plugin"); // Goes with next arg.
1066 args.push_back(PPPDevice::kPluginPath);
1067 args.push_back(serial_device);
mukesh agrawalfc362912013-08-06 18:10:07 -07001068 is_ppp_authenticating_ = false;
mukesh agrawal9da07772013-05-15 14:15:17 -07001069 scoped_ptr<ExternalTask> new_ppp_task(
1070 new ExternalTask(modem_info_->control_interface(),
1071 modem_info_->glib(),
1072 weak_ptr_factory_.GetWeakPtr(),
1073 death_callback));
1074 if (new_ppp_task->Start(
mukesh agrawalc4f9aa02013-08-15 19:23:13 -07001075 FilePath(PPPDevice::kDaemonPath), args, environment, true, &error)) {
mukesh agrawal9da07772013-05-15 14:15:17 -07001076 LOG(INFO) << "Forked pppd process.";
1077 ppp_task_ = new_ppp_task.Pass();
1078 }
1079}
1080
mukesh agrawal5c8ed242013-10-04 11:59:58 -07001081void Cellular::StopPPP() {
1082 SLOG(PPP, 2) << __func__;
1083 if (!ppp_task_) {
1084 return;
1085 }
1086 DropConnection();
1087 ppp_task_.reset();
1088 ppp_device_ = NULL;
1089}
1090
mukesh agrawal9da07772013-05-15 14:15:17 -07001091// called by |ppp_task_|
1092void Cellular::GetLogin(string *user, string *password) {
mukesh agrawalfc362912013-08-06 18:10:07 -07001093 SLOG(PPP, 2) << __func__;
mukesh agrawal3ffe52c2013-06-20 15:21:29 -07001094 if (!service()) {
1095 LOG(ERROR) << __func__ << " with no service ";
1096 return;
1097 }
1098 CHECK(user);
1099 CHECK(password);
1100 *user = service()->ppp_username();
1101 *password = service()->ppp_password();
mukesh agrawal9da07772013-05-15 14:15:17 -07001102}
1103
1104// Called by |ppp_task_|.
1105void Cellular::Notify(const string &reason,
1106 const map<string, string> &dict) {
mukesh agrawalfc362912013-08-06 18:10:07 -07001107 SLOG(PPP, 2) << __func__ << " " << reason << " on " << link_name();
mukesh agrawal9da07772013-05-15 14:15:17 -07001108
mukesh agrawalfc362912013-08-06 18:10:07 -07001109 if (reason == kPPPReasonAuthenticating) {
1110 OnPPPAuthenticating();
1111 } else if (reason == kPPPReasonAuthenticated) {
1112 OnPPPAuthenticated();
1113 } else if (reason == kPPPReasonConnect) {
1114 OnPPPConnected(dict);
1115 } else if (reason == kPPPReasonDisconnect) {
1116 OnPPPDisconnected();
1117 } else {
1118 NOTREACHED();
mukesh agrawal9da07772013-05-15 14:15:17 -07001119 }
mukesh agrawalfc362912013-08-06 18:10:07 -07001120}
mukesh agrawal9da07772013-05-15 14:15:17 -07001121
mukesh agrawalfc362912013-08-06 18:10:07 -07001122void Cellular::OnPPPAuthenticated() {
1123 SLOG(PPP, 2) << __func__;
1124 is_ppp_authenticating_ = false;
1125}
1126
1127void Cellular::OnPPPAuthenticating() {
1128 SLOG(PPP, 2) << __func__;
1129 is_ppp_authenticating_ = true;
1130}
1131
1132void Cellular::OnPPPConnected(const map<string, string> &params) {
1133 SLOG(PPP, 2) << __func__;
1134 string interface_name = PPPDevice::GetInterfaceName(params);
mukesh agrawal9da07772013-05-15 14:15:17 -07001135 DeviceInfo *device_info = modem_info_->manager()->device_info();
1136 int interface_index = device_info->GetIndex(interface_name);
1137 if (interface_index < 0) {
1138 // TODO(quiche): Consider handling the race when the RTNL notification about
1139 // the new PPP device has not been received yet. crbug.com/246832.
1140 NOTIMPLEMENTED() << ": No device info for " << interface_name << ".";
1141 return;
1142 }
1143
1144 if (!ppp_device_ || ppp_device_->interface_index() != interface_index) {
mukesh agrawalf407d592013-07-31 11:37:57 -07001145 if (ppp_device_) {
1146 ppp_device_->SelectService(NULL); // No longer drives |service_|.
1147 }
1148 ppp_device_ = ppp_device_factory_->CreatePPPDevice(
1149 modem_info_->control_interface(),
1150 modem_info_->dispatcher(),
1151 modem_info_->metrics(),
1152 modem_info_->manager(),
1153 interface_name,
1154 interface_index);
mukesh agrawal9da07772013-05-15 14:15:17 -07001155 device_info->RegisterDevice(ppp_device_);
1156 }
1157
mukesh agrawal9da07772013-05-15 14:15:17 -07001158 CHECK(service_);
mukesh agrawal5d851b12013-07-11 14:09:41 -07001159 // For PPP, we only SelectService on the |ppp_device_|.
1160 CHECK(!selected_service());
mukesh agrawal9da07772013-05-15 14:15:17 -07001161 const bool kBlackholeIPv6 = false;
1162 ppp_device_->SetEnabled(true);
1163 ppp_device_->SelectService(service_);
mukesh agrawalfc362912013-08-06 18:10:07 -07001164 ppp_device_->UpdateIPConfigFromPPP(params, kBlackholeIPv6);
1165}
1166
1167void Cellular::OnPPPDisconnected() {
1168 SLOG(PPP, 2) << __func__;
1169 // DestroyLater, rather than while on stack.
1170 ppp_task_.release()->DestroyLater(modem_info_->dispatcher());
1171 if (is_ppp_authenticating_) {
1172 SetServiceFailure(Service::kFailurePPPAuth);
1173 } else {
1174 // TODO(quiche): Don't set failure if we disconnected intentionally.
1175 SetServiceFailure(Service::kFailureUnknown);
1176 }
mukesh agrawalee10f372013-08-14 14:07:11 -07001177 Error error;
Samuel Tan0d061192014-07-07 15:45:15 -07001178 Disconnect(&error, __func__);
mukesh agrawal9da07772013-05-15 14:15:17 -07001179}
1180
1181void Cellular::OnPPPDied(pid_t pid, int exit) {
1182 LOG(INFO) << __func__ << " on " << link_name();
mukesh agrawalee10f372013-08-14 14:07:11 -07001183 OnPPPDisconnected();
mukesh agrawal9da07772013-05-15 14:15:17 -07001184}
1185
Prathmesh Prabhu49ffffd2014-01-09 18:28:55 -08001186void Cellular::UpdateScanning() {
1187 if (proposed_scan_in_progress_) {
1188 set_scanning(true);
1189 return;
1190 }
1191
1192 if (modem_state_ == kModemStateEnabling) {
1193 set_scanning(true);
1194 return;
1195 }
1196
1197 if (service_ && service_->activation_state() != kActivationStateActivated) {
1198 set_scanning(false);
1199 return;
1200 }
1201
1202 if (modem_state_ == kModemStateEnabled ||
1203 modem_state_ == kModemStateSearching) {
1204 set_scanning(true);
1205 return;
1206 }
1207
1208 set_scanning(false);
1209}
1210
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001211void Cellular::RegisterProperties() {
1212 PropertyStore *store = this->mutable_store();
1213
1214 // These properties do not have setters, and events are not generated when
1215 // they are changed.
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001216 store->RegisterConstString(kDBusServiceProperty, &dbus_service_);
1217 store->RegisterConstString(kDBusObjectProperty, &dbus_path_);
1218
1219 store->RegisterUint16(kScanIntervalProperty, &scan_interval_);
1220
1221 // These properties have setters that should be used to change their values.
1222 // Events are generated whenever the values change.
1223 store->RegisterConstStringmap(kHomeProviderProperty,
1224 &home_provider_.ToDict());
1225 store->RegisterConstString(kCarrierProperty, &carrier_);
1226 store->RegisterConstBool(kSupportNetworkScanProperty, &scanning_supported_);
1227 store->RegisterConstString(kEsnProperty, &esn_);
1228 store->RegisterConstString(kFirmwareRevisionProperty, &firmware_revision_);
1229 store->RegisterConstString(kHardwareRevisionProperty, &hardware_revision_);
1230 store->RegisterConstString(kImeiProperty, &imei_);
1231 store->RegisterConstString(kImsiProperty, &imsi_);
1232 store->RegisterConstString(kMdnProperty, &mdn_);
1233 store->RegisterConstString(kMeidProperty, &meid_);
1234 store->RegisterConstString(kMinProperty, &min_);
1235 store->RegisterConstString(kManufacturerProperty, &manufacturer_);
1236 store->RegisterConstString(kModelIDProperty, &model_id_);
Prathmesh Prabhu49ffffd2014-01-09 18:28:55 -08001237 store->RegisterConstBool(kScanningProperty, &scanning_);
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001238
1239 store->RegisterConstString(kSelectedNetworkProperty, &selected_network_);
1240 store->RegisterConstStringmaps(kFoundNetworksProperty, &found_networks_);
1241 store->RegisterConstBool(kProviderRequiresRoamingProperty,
1242 &provider_requires_roaming_);
1243 store->RegisterConstBool(kSIMPresentProperty, &sim_present_);
1244 store->RegisterConstStringmaps(kCellularApnListProperty, &apn_list_);
Prathmesh Prabhu700ff4d2014-01-16 15:59:33 -08001245 store->RegisterConstString(kIccidProperty, &sim_identifier_);
1246
1247 store->RegisterConstStrings(kSupportedCarriersProperty, &supported_carriers_);
1248 store->RegisterConstUint16(kPRLVersionProperty, &prl_version_);
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001249
1250 // TODO(pprabhu): Decide whether these need their own custom setters.
1251 HelpRegisterConstDerivedString(kTechnologyFamilyProperty,
1252 &Cellular::GetTechnologyFamily);
1253 HelpRegisterDerivedBool(kCellularAllowRoamingProperty,
1254 &Cellular::GetAllowRoaming,
1255 &Cellular::SetAllowRoaming);
1256}
1257
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -07001258void Cellular::set_home_provider(const Cellular::Operator &home_provider) {
1259 if (home_provider_.Equals(home_provider)) {
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001260 return;
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -07001261 }
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001262
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -07001263 home_provider_.CopyFrom(home_provider);
1264
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001265 adaptor()->EmitStringmapChanged(kHomeProviderProperty,
1266 home_provider_.ToDict());
1267}
1268
1269void Cellular::set_carrier(const string &carrier) {
1270 if (carrier_ == carrier)
1271 return;
1272
1273 carrier_ = carrier;
1274 adaptor()->EmitStringChanged(kCarrierProperty, carrier_);
1275}
1276
1277void Cellular::set_scanning_supported(bool scanning_supported) {
1278 if (scanning_supported_ == scanning_supported)
1279 return;
1280
1281 scanning_supported_ = scanning_supported;
1282 if (adaptor())
1283 adaptor()->EmitBoolChanged(kSupportNetworkScanProperty,
1284 scanning_supported_);
1285 else
1286 SLOG(Cellular, 2) << "Could not emit signal for property |"
1287 << kSupportNetworkScanProperty
1288 << "| change. DBus adaptor is NULL!";
1289}
1290
1291void Cellular::set_esn(const string &esn) {
1292 if (esn_ == esn)
1293 return;
1294
1295 esn_ = esn;
1296 adaptor()->EmitStringChanged(kEsnProperty, esn_);
1297}
1298
Thieu Lec466ccb2014-06-23 15:24:56 -07001299void Cellular::set_firmware_revision(const string &firmware_revision) {
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001300 if (firmware_revision_ == firmware_revision)
1301 return;
1302
1303 firmware_revision_ = firmware_revision;
1304 adaptor()->EmitStringChanged(kFirmwareRevisionProperty, firmware_revision_);
1305}
1306
Thieu Lec466ccb2014-06-23 15:24:56 -07001307void Cellular::set_hardware_revision(const string &hardware_revision) {
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001308 if (hardware_revision_ == hardware_revision)
1309 return;
1310
1311 hardware_revision_ = hardware_revision;
1312 adaptor()->EmitStringChanged(kHardwareRevisionProperty, hardware_revision_);
1313}
1314
1315// TODO(armansito): The following methods should probably log their argument
1316// values. Need to learn if any of them need to be scrubbed.
Thieu Lec466ccb2014-06-23 15:24:56 -07001317void Cellular::set_imei(const string &imei) {
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001318 if (imei_ == imei)
1319 return;
1320
1321 imei_ = imei;
1322 adaptor()->EmitStringChanged(kImeiProperty, imei_);
1323}
1324
Thieu Lec466ccb2014-06-23 15:24:56 -07001325void Cellular::set_imsi(const string &imsi) {
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001326 if (imsi_ == imsi)
1327 return;
1328
1329 imsi_ = imsi;
1330 adaptor()->EmitStringChanged(kImsiProperty, imsi_);
1331}
1332
Thieu Lec466ccb2014-06-23 15:24:56 -07001333void Cellular::set_mdn(const string &mdn) {
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001334 if (mdn_ == mdn)
1335 return;
1336
1337 mdn_ = mdn;
1338 adaptor()->EmitStringChanged(kMdnProperty, mdn_);
1339}
1340
Thieu Lec466ccb2014-06-23 15:24:56 -07001341void Cellular::set_meid(const string &meid) {
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001342 if (meid_ == meid)
1343 return;
1344
1345 meid_ = meid;
1346 adaptor()->EmitStringChanged(kMeidProperty, meid_);
1347}
1348
Thieu Lec466ccb2014-06-23 15:24:56 -07001349void Cellular::set_min(const string &min) {
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001350 if (min_ == min)
1351 return;
1352
1353 min_ = min;
1354 adaptor()->EmitStringChanged(kMinProperty, min_);
1355}
1356
Thieu Lec466ccb2014-06-23 15:24:56 -07001357void Cellular::set_manufacturer(const string &manufacturer) {
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001358 if (manufacturer_ == manufacturer)
1359 return;
1360
1361 manufacturer_ = manufacturer;
1362 adaptor()->EmitStringChanged(kManufacturerProperty, manufacturer_);
1363}
1364
Thieu Lec466ccb2014-06-23 15:24:56 -07001365void Cellular::set_model_id(const string &model_id) {
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001366 if (model_id_ == model_id)
1367 return;
1368
1369 model_id_ = model_id;
1370 adaptor()->EmitStringChanged(kModelIDProperty, model_id_);
1371}
1372
Thieu Lec466ccb2014-06-23 15:24:56 -07001373void Cellular::set_mm_plugin(const string &mm_plugin) {
1374 mm_plugin_ = mm_plugin;
1375}
1376
Prathmesh Prabhu49ffffd2014-01-09 18:28:55 -08001377void Cellular::set_scanning(bool scanning) {
1378 if (scanning_ == scanning)
1379 return;
1380
1381 scanning_ = scanning;
1382 adaptor()->EmitBoolChanged(kScanningProperty, scanning_);
1383
1384 // kScanningProperty is a sticky-false property.
1385 // Every time it is set to |true|, it will remain |true| up to a maximum of
1386 // |kScanningTimeout| time, after which it will be reset to |false|.
1387 if (!scanning_ && !scanning_timeout_callback_.IsCancelled()) {
1388 SLOG(Cellular, 2) << "Scanning set to false. "
1389 << "Cancelling outstanding timeout.";
1390 scanning_timeout_callback_.Cancel();
1391 } else {
1392 CHECK(scanning_timeout_callback_.IsCancelled());
1393 SLOG(Cellular, 2) << "Scanning set to true. "
1394 << "Starting timeout to reset to false.";
1395 scanning_timeout_callback_.Reset(Bind(&Cellular::set_scanning,
1396 weak_ptr_factory_.GetWeakPtr(),
1397 false));
1398 dispatcher()->PostDelayedTask(
1399 scanning_timeout_callback_.callback(),
1400 scanning_timeout_milliseconds_);
1401 }
1402}
1403
Thieu Lec466ccb2014-06-23 15:24:56 -07001404void Cellular::set_selected_network(const string &selected_network) {
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001405 if (selected_network_ == selected_network)
1406 return;
1407
1408 selected_network_ = selected_network;
1409 adaptor()->EmitStringChanged(kSelectedNetworkProperty, selected_network_);
1410}
1411
1412void Cellular::set_found_networks(const Stringmaps &found_networks) {
1413 // There is no canonical form of a Stringmaps value.
1414 // So don't check for redundant updates.
1415 found_networks_ = found_networks;
1416 adaptor()->EmitStringmapsChanged(kFoundNetworksProperty, found_networks_);
1417}
1418
Prathmesh Prabhu49ffffd2014-01-09 18:28:55 -08001419void Cellular::clear_found_networks() {
1420 if (found_networks_.empty())
1421 return;
1422
1423 found_networks_.clear();
1424 adaptor()->EmitStringmapsChanged(kFoundNetworksProperty, found_networks_);
1425}
1426
Prathmesh Prabhu9f06c872013-11-21 14:08:23 -08001427void Cellular::set_provider_requires_roaming(bool provider_requires_roaming) {
1428 if (provider_requires_roaming_ == provider_requires_roaming)
1429 return;
1430
1431 provider_requires_roaming_ = provider_requires_roaming;
1432 adaptor()->EmitBoolChanged(kProviderRequiresRoamingProperty,
1433 provider_requires_roaming_);
1434}
1435
1436void Cellular::set_sim_present(bool sim_present) {
1437 if (sim_present_ == sim_present)
1438 return;
1439
1440 sim_present_ = sim_present;
1441 adaptor()->EmitBoolChanged(kSIMPresentProperty, sim_present_);
1442}
1443
1444void Cellular::set_apn_list(const Stringmaps &apn_list) {
1445 // There is no canonical form of a Stringmaps value.
1446 // So don't check for redundant updates.
1447 apn_list_ = apn_list;
1448 // See crbug.com/215581: Sometimes adaptor may be NULL when |set_apn_list| is
1449 // called.
1450 if (adaptor())
1451 adaptor()->EmitStringmapsChanged(kCellularApnListProperty, apn_list_);
1452 else
1453 SLOG(Cellular, 2) << "Could not emit signal for property |"
1454 << kCellularApnListProperty
1455 << "| change. DBus adaptor is NULL!";
1456}
1457
Prathmesh Prabhu700ff4d2014-01-16 15:59:33 -08001458void Cellular::set_sim_identifier(const string &sim_identifier) {
1459 if (sim_identifier_ == sim_identifier)
1460 return;
1461
1462 sim_identifier_ = sim_identifier;
1463 adaptor()->EmitStringChanged(kIccidProperty, sim_identifier_);
1464}
1465
1466void Cellular::set_supported_carriers(const Strings &supported_carriers) {
1467 // There is no canonical form of a Strings value.
1468 // So don't check for redundant updates.
1469 supported_carriers_ = supported_carriers;
1470 adaptor()->EmitStringsChanged(kSupportedCarriersProperty,
1471 supported_carriers_);
1472}
1473
1474void Cellular::set_prl_version(uint16 prl_version) {
1475 if (prl_version_ == prl_version)
1476 return;
1477
1478 prl_version_ = prl_version;
1479 adaptor()->EmitUint16Changed(kPRLVersionProperty, prl_version_);
1480}
1481
Prathmesh Prabhu8599e052014-04-25 14:20:22 -07001482void Cellular::set_home_provider_info(MobileOperatorInfo *home_provider_info) {
1483 home_provider_info_.reset(home_provider_info);
1484}
1485
1486void Cellular::set_serving_operator_info(
1487 MobileOperatorInfo *serving_operator_info) {
1488 serving_operator_info_.reset(serving_operator_info);
1489}
1490
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -07001491void Cellular::UpdateHomeProvider(const MobileOperatorInfo *operator_info) {
Prathmesh Prabhubfd6e5b2014-04-15 16:47:50 -07001492 SLOG(Cellular, 3) << __func__;
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -07001493 // TODO(pprabhu) Change |set_home_provider| to take Stringmap argument and
1494 // update this.
1495 Operator oper;
1496 if (!operator_info->mccmnc().empty()) {
1497 oper.SetCode(operator_info->mccmnc());
1498 }
1499 if (!operator_info->operator_name().empty()) {
1500 oper.SetName(operator_info->operator_name());
1501 }
1502 if (!operator_info->country().empty()) {
1503 oper.SetCountry(operator_info->country());
1504 }
1505 set_home_provider(oper);
1506
1507 // Update the APN list.
1508 const ScopedVector<MobileOperatorInfo::MobileAPN> &apn_list =
1509 operator_info->apn_list();
1510 Stringmaps apn_list_dict;
1511
1512 for (const auto &mobile_apn : apn_list) {
1513 Stringmap props;
1514 if (!mobile_apn->apn.empty()) {
1515 props[kApnProperty] = mobile_apn->apn;
1516 }
1517 if (!mobile_apn->username.empty()) {
1518 props[kApnUsernameProperty] = mobile_apn->username;
1519 }
1520 if (!mobile_apn->password.empty()) {
1521 props[kApnPasswordProperty] = mobile_apn->password;
1522 }
1523
1524 // Find the first localized and non-localized name, if any.
1525 if (!mobile_apn->operator_name_list.empty()) {
1526 props[kApnNameProperty] = mobile_apn->operator_name_list[0].name;
1527 }
1528 for (const auto &lname : mobile_apn->operator_name_list) {
1529 if (!lname.language.empty()) {
1530 props[kApnLocalizedNameProperty] = lname.name;
1531 }
1532 }
1533
1534 apn_list_dict.push_back(props);
1535 }
1536 set_apn_list(apn_list_dict);
1537
1538 set_provider_requires_roaming(operator_info->requires_roaming());
1539}
1540
1541void Cellular::UpdateServingOperator(
1542 const MobileOperatorInfo *operator_info,
1543 const MobileOperatorInfo *home_provider_info) {
Prathmesh Prabhubfd6e5b2014-04-15 16:47:50 -07001544 SLOG(Cellular, 3) << __func__;
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -07001545 if (!service()) {
1546 return;
1547 }
1548
1549 // TODO(pprabhu) Update |CellularService::SetServingOperator| to take
1550 // Stringmap argument and update this.
1551 Operator oper;
1552 if (!operator_info->mccmnc().empty()) {
1553 oper.SetCode(operator_info->mccmnc());
1554 }
1555 if (!operator_info->operator_name().empty()) {
1556 oper.SetName(operator_info->operator_name());
1557 }
1558 if (!operator_info->country().empty()) {
1559 oper.SetCountry(operator_info->country());
1560 }
1561 service()->SetServingOperator(oper);
1562
1563 // Set friendly name of service.
1564 string service_name;
1565 if (!operator_info->operator_name().empty()) {
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -07001566 // If roaming, try to show "<home-provider> | <serving-operator>", per 3GPP
1567 // rules (TS 31.102 and annex A of 122.101).
1568 if (service()->roaming_state() == kRoamingStateRoaming &&
1569 home_provider_info &&
1570 !home_provider_info->operator_name().empty()) {
Prathmesh Prabhubfd6e5b2014-04-15 16:47:50 -07001571 service_name += home_provider_info->operator_name() + " | ";
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -07001572 }
Prathmesh Prabhubfd6e5b2014-04-15 16:47:50 -07001573 service_name += operator_info->operator_name();
Alex Vakulenko8a532292014-06-16 17:18:44 -07001574 } else if (!operator_info->mccmnc().empty()) {
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -07001575 // We could not get a name for the operator, just use the code.
1576 service_name = "cellular_" + operator_info->mccmnc();
1577 } else {
Prathmesh Prabhu95afcbb2014-04-08 16:05:42 -07001578 // We do not have any information, so must fallback to default service name.
1579 // Only assign a new default name if the service doesn't already have one,
1580 // because we we generate a new name each time.
1581 service_name = service()->friendly_name();
1582 if (!IsDefaultFriendlyServiceName(service_name)) {
1583 service_name = CreateDefaultFriendlyServiceName();
1584 }
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -07001585 }
1586 service()->SetFriendlyName(service_name);
1587}
1588
1589// /////////////////////////////////////////////////////////////////////////////
1590// MobileOperatorInfoObserver implementation.
1591Cellular::MobileOperatorInfoObserver::MobileOperatorInfoObserver(
Prathmesh Prabhu8599e052014-04-25 14:20:22 -07001592 Cellular *cellular)
Prathmesh Prabhu92df6192014-04-29 18:08:08 -07001593 : cellular_(cellular),
1594 capability_(NULL) {}
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -07001595
1596Cellular::MobileOperatorInfoObserver::~MobileOperatorInfoObserver() {}
1597
1598void Cellular::MobileOperatorInfoObserver::OnOperatorChanged() {
1599 SLOG(Cellular, 3) << __func__;
1600
Prathmesh Prabhu92df6192014-04-29 18:08:08 -07001601 // Give the capabilities a chance to hook in and update their state.
1602 // Some tests set |capability_| to NULL avoid having to expect the full
1603 // behaviour caused by this call.
1604 if (capability_) {
1605 capability_->OnOperatorChanged();
1606 }
1607
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -07001608 const MobileOperatorInfo *home_provider_info =
Prathmesh Prabhu8599e052014-04-25 14:20:22 -07001609 cellular_->home_provider_info();
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -07001610 const MobileOperatorInfo *serving_operator_info =
Prathmesh Prabhu8599e052014-04-25 14:20:22 -07001611 cellular_->serving_operator_info();
Prathmesh Prabhu28b4a3b2014-03-28 11:52:09 -07001612
1613 const bool home_provider_known =
1614 home_provider_info->IsMobileNetworkOperatorKnown();
1615 const bool serving_operator_known =
1616 serving_operator_info->IsMobileNetworkOperatorKnown();
1617
1618 if (home_provider_known) {
1619 cellular_->UpdateHomeProvider(home_provider_info);
1620 } else if (serving_operator_known) {
1621 SLOG(Cellular, 2) << "Serving provider proxying in for home provider.";
1622 cellular_->UpdateHomeProvider(serving_operator_info);
1623 }
1624
1625 if (serving_operator_known) {
1626 if (home_provider_known) {
1627 cellular_->UpdateServingOperator(serving_operator_info,
1628 home_provider_info);
1629 } else {
1630 cellular_->UpdateServingOperator(serving_operator_info, NULL);
1631 }
1632 } else if (home_provider_known) {
1633 cellular_->UpdateServingOperator(home_provider_info, home_provider_info);
1634 }
1635}
1636
Chris Masone3bd3c8c2011-06-13 08:20:26 -07001637} // namespace shill