blob: 83c6564aada4936bee59ece7c5b38db89ab8cea3 [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>
Darin Petkove9d12e02011-07-27 15:09:37 -070016#include <base/stringprintf.h>
Chris Masoneb925cc82011-06-22 15:39:57 -070017#include <chromeos/dbus/service_constants.h>
Darin Petkov137884a2011-10-26 18:52:47 +020018#include <mobile_provider.h>
Chris Masone3bd3c8c2011-06-13 08:20:26 -070019
Eric Shienbrood5de44ab2011-12-05 10:46:27 -050020#include "shill/adaptor_interfaces.h"
Darin Petkovdaf43862011-10-27 11:37:28 +020021#include "shill/cellular_capability_cdma.h"
22#include "shill/cellular_capability_gsm.h"
Jason Glasgow82f9ab32012-04-04 14:27:19 -040023#include "shill/cellular_capability_universal.h"
Arman Uguray72fab6a2013-01-10 19:32:42 -080024#include "shill/cellular_capability_universal_cdma.h"
Chris Masone3bd3c8c2011-06-13 08:20:26 -070025#include "shill/cellular_service.h"
26#include "shill/control_interface.h"
27#include "shill/device.h"
28#include "shill/device_info.h"
Darin Petkov4d6d9412011-08-24 13:19:54 -070029#include "shill/error.h"
Paul Stewart26b327e2011-10-19 11:38:09 -070030#include "shill/event_dispatcher.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070031#include "shill/logging.h"
Chris Masone3bd3c8c2011-06-13 08:20:26 -070032#include "shill/manager.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070033#include "shill/profile.h"
Chris Masone889666b2011-07-03 12:58:50 -070034#include "shill/property_accessor.h"
Darin Petkove9d12e02011-07-27 15:09:37 -070035#include "shill/proxy_factory.h"
Darin Petkov0828f5f2011-08-11 10:18:52 -070036#include "shill/rtnl_handler.h"
Jason Glasgow7b461df2012-05-01 16:38:45 -040037#include "shill/store_interface.h"
Gaurav Shah435de2c2011-11-17 19:01:07 -080038#include "shill/technology.h"
Chris Masone3bd3c8c2011-06-13 08:20:26 -070039
Eric Shienbrood3e20a232012-02-16 11:35:56 -050040using base::Bind;
Gary Moraina9fb3252012-05-31 12:05:31 -070041using base::Closure;
Chris Masone3bd3c8c2011-06-13 08:20:26 -070042using std::string;
Chris Masone889666b2011-07-03 12:58:50 -070043using std::vector;
Chris Masone3bd3c8c2011-06-13 08:20:26 -070044
45namespace shill {
46
Jason Glasgow7b461df2012-05-01 16:38:45 -040047// static
48const char Cellular::kAllowRoaming[] = "AllowRoaming";
49
Darin Petkov3335b372011-08-22 11:05:32 -070050Cellular::Operator::Operator() {
51 SetName("");
52 SetCode("");
53 SetCountry("");
54}
55
56Cellular::Operator::~Operator() {}
57
58void Cellular::Operator::CopyFrom(const Operator &oper) {
59 dict_ = oper.dict_;
60}
61
62const string &Cellular::Operator::GetName() const {
63 return dict_.find(flimflam::kOperatorNameKey)->second;
64}
65
66void Cellular::Operator::SetName(const string &name) {
67 dict_[flimflam::kOperatorNameKey] = name;
68}
69
70const string &Cellular::Operator::GetCode() const {
71 return dict_.find(flimflam::kOperatorCodeKey)->second;
72}
73
74void Cellular::Operator::SetCode(const string &code) {
75 dict_[flimflam::kOperatorCodeKey] = code;
76}
77
78const string &Cellular::Operator::GetCountry() const {
79 return dict_.find(flimflam::kOperatorCountryKey)->second;
80}
81
82void Cellular::Operator::SetCountry(const string &country) {
83 dict_[flimflam::kOperatorCountryKey] = country;
84}
85
86const Stringmap &Cellular::Operator::ToDict() const {
87 return dict_;
88}
89
Prathmesh Prabhu27526f12013-03-25 19:42:18 -070090Cellular::Cellular(ModemInfo *modem_info,
Darin Petkove9d12e02011-07-27 15:09:37 -070091 const string &link_name,
Darin Petkov3335b372011-08-22 11:05:32 -070092 const string &address,
Darin Petkove9d12e02011-07-27 15:09:37 -070093 int interface_index,
94 Type type,
95 const string &owner,
Jason Glasgowa585fc32012-06-06 11:04:09 -040096 const string &service,
Darin Petkov137884a2011-10-26 18:52:47 +020097 const string &path,
Ben Chan3ecdf822012-08-06 12:29:23 -070098 ProxyFactory *proxy_factory)
Prathmesh Prabhu27526f12013-03-25 19:42:18 -070099 : Device(modem_info->control_interface(),
100 modem_info->dispatcher(),
101 modem_info->metrics(),
102 modem_info->manager(),
Darin Petkove9d12e02011-07-27 15:09:37 -0700103 link_name,
Chris Masone626719f2011-08-18 16:58:48 -0700104 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -0800105 interface_index,
106 Technology::kCellular),
Thieu Le64b0fe52012-08-08 14:57:36 -0700107 weak_ptr_factory_(this),
Darin Petkove9d12e02011-07-27 15:09:37 -0700108 state_(kStateDisabled),
Darin Petkovbac96002011-08-09 13:22:00 -0700109 modem_state_(kModemStateUnknown),
Darin Petkove9d12e02011-07-27 15:09:37 -0700110 dbus_owner_(owner),
Jason Glasgowa585fc32012-06-06 11:04:09 -0400111 dbus_service_(service),
Darin Petkove9d12e02011-07-27 15:09:37 -0700112 dbus_path_(path),
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700113 modem_info_(modem_info),
Ben Chan3ecdf822012-08-06 12:29:23 -0700114 proxy_factory_(proxy_factory),
Thieu Le26fc01b2013-01-28 12:08:48 -0800115 allow_roaming_(false),
116 explicit_disconnect_(false) {
mukesh agrawalde29fa82011-09-16 16:16:36 -0700117 PropertyStore *store = this->mutable_store();
Jason Glasgowa585fc32012-06-06 11:04:09 -0400118 // TODO(jglasgow): kDBusConnectionProperty is deprecated.
Paul Stewartac4ac002011-08-26 12:04:26 -0700119 store->RegisterConstString(flimflam::kDBusConnectionProperty, &dbus_owner_);
Jason Glasgowa585fc32012-06-06 11:04:09 -0400120 store->RegisterConstString(flimflam::kDBusServiceProperty, &dbus_service_);
Paul Stewartac4ac002011-08-26 12:04:26 -0700121 store->RegisterConstString(flimflam::kDBusObjectProperty, &dbus_path_);
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700122 HelpRegisterConstDerivedString(flimflam::kTechnologyFamilyProperty,
123 &Cellular::GetTechnologyFamily);
Jason Glasgow7b461df2012-05-01 16:38:45 -0400124 HelpRegisterDerivedBool(flimflam::kCellularAllowRoamingProperty,
125 &Cellular::GetAllowRoaming,
126 &Cellular::SetAllowRoaming);
Paul Stewartac4ac002011-08-26 12:04:26 -0700127 store->RegisterConstStringmap(flimflam::kHomeProviderProperty,
Darin Petkov3335b372011-08-22 11:05:32 -0700128 &home_provider_.ToDict());
Arman Ugurayc7b15602013-02-16 00:56:18 -0800129
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500130 // For now, only a single capability is supported.
Ben Chan3ecdf822012-08-06 12:29:23 -0700131 InitCapability(type);
Chris Masoneb925cc82011-06-22 15:39:57 -0700132
Ben Chanfad4a0b2012-04-18 15:49:59 -0700133 SLOG(Cellular, 2) << "Cellular device " << this->link_name()
134 << " initialized.";
Chris Masone3bd3c8c2011-06-13 08:20:26 -0700135}
136
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500137Cellular::~Cellular() {
138}
Darin Petkove9d12e02011-07-27 15:09:37 -0700139
Jason Glasgow7b461df2012-05-01 16:38:45 -0400140bool Cellular::Load(StoreInterface *storage) {
141 const string id = GetStorageIdentifier();
142 if (!storage->ContainsGroup(id)) {
143 LOG(WARNING) << "Device is not available in the persistent store: " << id;
144 return false;
145 }
146 storage->GetBool(id, kAllowRoaming, &allow_roaming_);
147 return Device::Load(storage);
148}
149
150bool Cellular::Save(StoreInterface *storage) {
151 const string id = GetStorageIdentifier();
152 storage->SetBool(id, kAllowRoaming, allow_roaming_);
153 return Device::Save(storage);
154}
155
Darin Petkovcc044422011-08-17 13:30:06 -0700156// static
157string Cellular::GetStateString(State state) {
158 switch (state) {
Darin Petkove9d12e02011-07-27 15:09:37 -0700159 case kStateDisabled: return "CellularStateDisabled";
160 case kStateEnabled: return "CellularStateEnabled";
161 case kStateRegistered: return "CellularStateRegistered";
162 case kStateConnected: return "CellularStateConnected";
Darin Petkov0828f5f2011-08-11 10:18:52 -0700163 case kStateLinked: return "CellularStateLinked";
164 default: NOTREACHED();
Darin Petkove9d12e02011-07-27 15:09:37 -0700165 }
Darin Petkovcc044422011-08-17 13:30:06 -0700166 return StringPrintf("CellularStateUnknown-%d", state);
Darin Petkov0828f5f2011-08-11 10:18:52 -0700167}
168
Eric Shienbrood0db6a9b2012-03-30 16:11:39 -0400169string Cellular::GetTechnologyFamily(Error *error) {
170 return capability_->GetTypeString();
171}
172
Darin Petkov0828f5f2011-08-11 10:18:52 -0700173void Cellular::SetState(State state) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700174 SLOG(Cellular, 2) << GetStateString(state_) << " -> "
175 << GetStateString(state);
Darin Petkov0828f5f2011-08-11 10:18:52 -0700176 state_ = state;
Chris Masone3bd3c8c2011-06-13 08:20:26 -0700177}
178
Jason Glasgow7b461df2012-05-01 16:38:45 -0400179void Cellular::HelpRegisterDerivedBool(
180 const string &name,
181 bool(Cellular::*get)(Error *error),
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700182 bool(Cellular::*set)(const bool &value, Error *error)) {
Jason Glasgow7b461df2012-05-01 16:38:45 -0400183 mutable_store()->RegisterDerivedBool(
184 name,
185 BoolAccessor(
186 new CustomAccessor<Cellular, bool>(this, get, set)));
187}
188
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700189void Cellular::HelpRegisterConstDerivedString(
Eric Shienbrood0db6a9b2012-03-30 16:11:39 -0400190 const string &name,
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700191 string(Cellular::*get)(Error *)) {
Eric Shienbrood0db6a9b2012-03-30 16:11:39 -0400192 mutable_store()->RegisterDerivedString(
193 name,
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700194 StringAccessor(new CustomAccessor<Cellular, string>(this, get, NULL)));
Eric Shienbrood0db6a9b2012-03-30 16:11:39 -0400195}
196
Eric Shienbrood9a245532012-03-07 14:20:39 -0500197void Cellular::Start(Error *error,
198 const EnabledStateChangedCallback &callback) {
Jason Glasgow4a490792012-04-10 15:02:05 -0400199 DCHECK(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700200 SLOG(Cellular, 2) << __func__ << ": " << GetStateString(state_);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500201 if (state_ != kStateDisabled) {
202 return;
203 }
Thieu Le64b0fe52012-08-08 14:57:36 -0700204 ResultCallback cb = Bind(&Cellular::StartModemCallback,
205 weak_ptr_factory_.GetWeakPtr(),
206 callback);
207 capability_->StartModem(error, cb);
Chris Masone3bd3c8c2011-06-13 08:20:26 -0700208}
209
Eric Shienbrood9a245532012-03-07 14:20:39 -0500210void Cellular::Stop(Error *error,
211 const EnabledStateChangedCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700212 SLOG(Cellular, 2) << __func__ << ": " << GetStateString(state_);
Thieu Le26fc01b2013-01-28 12:08:48 -0800213 explicit_disconnect_ = true;
Thieu Le64b0fe52012-08-08 14:57:36 -0700214 ResultCallback cb = Bind(&Cellular::StopModemCallback,
215 weak_ptr_factory_.GetWeakPtr(),
216 callback);
217 capability_->StopModem(error, cb);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500218}
219
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400220bool Cellular::IsUnderlyingDeviceEnabled() const {
221 return IsEnabledModemState(modem_state_);
222}
223
Thieu Led0012052012-07-25 16:09:09 -0700224bool Cellular::IsModemRegistered() const {
225 return (modem_state_ == Cellular::kModemStateRegistered ||
226 modem_state_ == Cellular::kModemStateConnecting ||
227 modem_state_ == Cellular::kModemStateConnected);
228}
229
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400230// static
231bool Cellular::IsEnabledModemState(ModemState state) {
232 switch (state) {
233 case kModemStateUnknown:
234 case kModemStateDisabled:
235 case kModemStateInitializing:
236 case kModemStateLocked:
237 case kModemStateDisabling:
238 case kModemStateEnabling:
239 return false;
240 case kModemStateEnabled:
241 case kModemStateSearching:
242 case kModemStateRegistered:
243 case kModemStateDisconnecting:
244 case kModemStateConnecting:
245 case kModemStateConnected:
246 return true;
247 }
248 return false;
249}
250
Thieu Le37b90032012-05-15 15:18:41 -0700251void Cellular::StartModemCallback(const EnabledStateChangedCallback &callback,
252 const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700253 SLOG(Cellular, 2) << __func__ << ": " << GetStateString(state_);
Gary Morainbaeefdf2012-04-30 14:53:35 -0700254 if (error.IsSuccess() && (state_ == kStateDisabled)) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500255 SetState(kStateEnabled);
Jason Glasgow4380f0d2012-05-03 18:05:04 -0400256 // Registration state updates may have been ignored while the
257 // modem was not yet marked enabled.
258 HandleNewRegistrationState();
Arman Ugurayf84a4242013-04-09 20:01:07 -0700259 if (capability_->ShouldDetectOutOfCredit())
Arman Ugurayd42d8ec2013-04-08 19:28:21 -0700260 set_traffic_monitor_enabled(true);
Jason Glasgow4380f0d2012-05-03 18:05:04 -0400261 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500262 callback.Run(error);
263}
264
Thieu Le37b90032012-05-15 15:18:41 -0700265void Cellular::StopModemCallback(const EnabledStateChangedCallback &callback,
266 const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700267 SLOG(Cellular, 2) << __func__ << ": " << GetStateString(state_);
Thieu Le26fc01b2013-01-28 12:08:48 -0800268 explicit_disconnect_ = false;
Thieu Le37b90032012-05-15 15:18:41 -0700269 // Destroy the cellular service regardless of any errors that occur during
270 // the stop process since we do not know the state of the modem at this
271 // point.
272 DestroyService();
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500273 if (state_ != kStateDisabled)
Eric Shienbrood9a245532012-03-07 14:20:39 -0500274 SetState(kStateDisabled);
Arman Ugurayd42d8ec2013-04-08 19:28:21 -0700275 set_traffic_monitor_enabled(false);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500276 callback.Run(error);
Chris Masone3bd3c8c2011-06-13 08:20:26 -0700277}
278
Ben Chan3ecdf822012-08-06 12:29:23 -0700279void Cellular::InitCapability(Type type) {
Darin Petkov5f316f62011-11-18 12:10:26 +0100280 // TODO(petkov): Consider moving capability construction into a factory that's
281 // external to the Cellular class.
Ben Chanfad4a0b2012-04-18 15:49:59 -0700282 SLOG(Cellular, 2) << __func__ << "(" << type << ")";
Darin Petkov5f316f62011-11-18 12:10:26 +0100283 switch (type) {
Darin Petkovdaf43862011-10-27 11:37:28 +0200284 case kTypeGSM:
Thieu Lece4483e2013-01-23 15:12:03 -0800285 capability_.reset(new CellularCapabilityGSM(this,
286 proxy_factory_,
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700287 modem_info_));
Darin Petkovdaf43862011-10-27 11:37:28 +0200288 break;
289 case kTypeCDMA:
Thieu Lece4483e2013-01-23 15:12:03 -0800290 capability_.reset(new CellularCapabilityCDMA(this,
291 proxy_factory_,
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700292 modem_info_));
Darin Petkovdaf43862011-10-27 11:37:28 +0200293 break;
David Rochbergfa1d31d2012-03-20 10:38:07 -0400294 case kTypeUniversal:
Arman Ugurayc7b15602013-02-16 00:56:18 -0800295 capability_.reset(new CellularCapabilityUniversal(
296 this,
297 proxy_factory_,
Prathmesh Prabhu27526f12013-03-25 19:42:18 -0700298 modem_info_));
David Rochbergfa1d31d2012-03-20 10:38:07 -0400299 break;
Arman Uguray72fab6a2013-01-10 19:32:42 -0800300 case kTypeUniversalCDMA:
301 capability_.reset(new CellularCapabilityUniversalCDMA(
302 this,
303 proxy_factory_,
304 modem_info_));
305 break;
Darin Petkovdaf43862011-10-27 11:37:28 +0200306 default: NOTREACHED();
307 }
308}
309
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400310void Cellular::Activate(const string &carrier,
Eric Shienbrood9a245532012-03-07 14:20:39 -0500311 Error *error, const ResultCallback &callback) {
312 capability_->Activate(carrier, error, callback);
Darin Petkov9ae310f2011-08-30 15:41:13 -0700313}
314
Arman Ugurayc7b15602013-02-16 00:56:18 -0800315void Cellular::CompleteActivation(Error *error) {
316 capability_->CompleteActivation(error);
317}
318
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500319void Cellular::RegisterOnNetwork(const string &network_id,
Eric Shienbrood9a245532012-03-07 14:20:39 -0500320 Error *error,
321 const ResultCallback &callback) {
322 capability_->RegisterOnNetwork(network_id, error, callback);
Darin Petkova3d3be52011-11-14 21:34:16 +0100323}
324
Eric Shienbrood9a245532012-03-07 14:20:39 -0500325void Cellular::RequirePIN(const string &pin, bool require,
326 Error *error, const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700327 SLOG(Cellular, 2) << __func__ << "(" << require << ")";
Eric Shienbrood9a245532012-03-07 14:20:39 -0500328 capability_->RequirePIN(pin, require, error, callback);
Darin Petkove42e1012011-08-31 12:35:04 -0700329}
330
Eric Shienbrood9a245532012-03-07 14:20:39 -0500331void Cellular::EnterPIN(const string &pin,
332 Error *error, const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700333 SLOG(Cellular, 2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500334 capability_->EnterPIN(pin, error, callback);
Darin Petkove42e1012011-08-31 12:35:04 -0700335}
336
Darin Petkovc64fe5e2012-01-11 12:46:13 +0100337void Cellular::UnblockPIN(const string &unblock_code,
338 const string &pin,
Eric Shienbrood9a245532012-03-07 14:20:39 -0500339 Error *error, const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700340 SLOG(Cellular, 2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500341 capability_->UnblockPIN(unblock_code, pin, error, callback);
Darin Petkove42e1012011-08-31 12:35:04 -0700342}
343
Eric Shienbrood9a245532012-03-07 14:20:39 -0500344void Cellular::ChangePIN(const string &old_pin, const string &new_pin,
345 Error *error, const ResultCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700346 SLOG(Cellular, 2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500347 capability_->ChangePIN(old_pin, new_pin, error, callback);
Darin Petkove42e1012011-08-31 12:35:04 -0700348}
349
Ben Chan5d0d32c2013-01-08 02:05:29 -0800350void Cellular::Reset(Error *error, const ResultCallback &callback) {
351 SLOG(Cellular, 2) << __func__;
352 capability_->Reset(error, callback);
353}
354
Darin Petkovc37a9c42012-09-06 15:28:22 +0200355void Cellular::SetCarrier(const string &carrier,
356 Error *error, const ResultCallback &callback) {
357 SLOG(Cellular, 2) << __func__ << "(" << carrier << ")";
358 capability_->SetCarrier(carrier, error, callback);
359}
360
Arman Ugurayd42d8ec2013-04-08 19:28:21 -0700361void Cellular::OnNoNetworkRouting() {
362 SLOG(Cellular, 2) << __func__;
363 Device::OnNoNetworkRouting();
Arman Ugurayf84a4242013-04-09 20:01:07 -0700364
365 SLOG(Cellular, 2) << "Requesting active probe for out-of-credit detection.";
366 RequestConnectionHealthCheck();
367}
368
369void Cellular::OnConnectionHealthCheckerResult(
370 ConnectionHealthChecker::Result result) {
Prathmesh Prabhu5489b7a2013-04-10 13:33:59 -0700371 SLOG(Cellular, 2) << __func__ << "(Result = "
372 << ConnectionHealthChecker::ResultToString(result) << ")";
Arman Ugurayf84a4242013-04-09 20:01:07 -0700373
Prathmesh Prabhu3fd54bd2013-05-08 16:36:57 -0700374 if (result == ConnectionHealthChecker::kResultCongestedTxQueue) {
Arman Ugurayf84a4242013-04-09 20:01:07 -0700375 SLOG(Cellular, 2) << "Active probe determined possible out-of-credits "
376 << "scenario.";
377 if (service().get()) {
Thieu Le91fccf62013-04-22 15:23:16 -0700378 Metrics::CellularOutOfCreditsReason reason =
379 (result == ConnectionHealthChecker::kResultCongestedTxQueue) ?
380 Metrics::kCellularOutOfCreditsReasonTxCongested :
381 Metrics::kCellularOutOfCreditsReasonElongatedTimeWait;
382 metrics()->NotifyCellularOutOfCredits(reason);
383
Arman Ugurayf84a4242013-04-09 20:01:07 -0700384 service()->SetOutOfCredits(true);
385 SLOG(Cellular, 2) << "Disconnecting due to out-of-credit scenario.";
386 Error error;
387 service()->Disconnect(&error);
388 }
389 }
390}
391
392void Cellular::PortalDetectorCallback(const PortalDetector::Result &result) {
393 Device::PortalDetectorCallback(result);
394 if (result.status != PortalDetector::kStatusSuccess &&
395 capability_->ShouldDetectOutOfCredit()) {
396 SLOG(Cellular, 2) << "Portal detection failed. Launching active probe for "
397 << "out-of-credit detection.";
398 RequestConnectionHealthCheck();
399 }
Arman Ugurayd42d8ec2013-04-08 19:28:21 -0700400}
401
Wade Guthrie68d41092013-04-02 12:56:02 -0700402void Cellular::Scan(ScanType scan_type, Error *error) {
403 // |scan_type| is ignored because Cellular only does a full scan.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500404 // TODO(ers): for now report immediate success or failure.
405 capability_->Scan(error, ResultCallback());
Darin Petkovceb68172011-07-29 14:47:48 -0700406}
407
Darin Petkovd9661952011-08-03 16:25:42 -0700408void Cellular::HandleNewRegistrationState() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700409 SLOG(Cellular, 2) << __func__ << ": " << GetStateString(state_);
Ben Chan09fa2a02012-11-07 22:09:09 -0800410 if (capability_->IsServiceActivationRequired()) {
411 if (state_ == kStateEnabled && !service_.get()) {
412 CreateService();
413 }
414 return;
415 }
Darin Petkovb72cf402011-11-22 14:51:39 +0100416 if (!capability_->IsRegistered()) {
Thieu Le26fc01b2013-01-28 12:08:48 -0800417 if (!explicit_disconnect_ &&
418 (state_ == kStateLinked || state_ == kStateConnected) &&
419 service_.get())
420 metrics()->NotifyCellularDeviceDrop(
Thieu Le6c1e3bb2013-02-06 15:20:35 -0800421 interface_index(),
422 capability_->GetNetworkTechnologyString(),
423 service_->strength());
Darin Petkov2c377382012-01-11 11:40:43 +0100424 DestroyService();
Darin Petkov0828f5f2011-08-11 10:18:52 -0700425 if (state_ == kStateLinked ||
426 state_ == kStateConnected ||
427 state_ == kStateRegistered) {
428 SetState(kStateEnabled);
Darin Petkovd9661952011-08-03 16:25:42 -0700429 }
430 return;
431 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500432 // In Disabled state, defer creating a service until fully
433 // enabled. UI will ignore the appearance of a new service
434 // on a disabled device.
435 if (state_ == kStateDisabled) {
436 return;
437 }
Darin Petkovd9661952011-08-03 16:25:42 -0700438 if (state_ == kStateEnabled) {
Darin Petkov0828f5f2011-08-11 10:18:52 -0700439 SetState(kStateRegistered);
Darin Petkovd9661952011-08-03 16:25:42 -0700440 }
441 if (!service_.get()) {
Thieu Le18c11072013-01-28 17:21:37 -0800442 metrics()->NotifyDeviceScanFinished(interface_index());
Darin Petkovd9661952011-08-03 16:25:42 -0700443 CreateService();
444 }
Darin Petkov3e509242011-11-10 14:46:44 +0100445 capability_->GetSignalQuality();
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500446 if (state_ == kStateRegistered && modem_state_ == kModemStateConnected)
447 OnConnected();
Darin Petkovb72cf402011-11-22 14:51:39 +0100448 service_->SetNetworkTechnology(capability_->GetNetworkTechnologyString());
449 service_->SetRoamingState(capability_->GetRoamingStateString());
Christopher Wiley9169d252012-11-30 15:13:39 -0800450 manager()->UpdateService(service_);
Darin Petkovd9661952011-08-03 16:25:42 -0700451}
452
Darin Petkovd9661952011-08-03 16:25:42 -0700453void Cellular::HandleNewSignalQuality(uint32 strength) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700454 SLOG(Cellular, 2) << "Signal strength: " << strength;
Darin Petkovd78ee7e2012-01-12 11:21:10 +0100455 if (service_) {
456 service_->SetStrength(strength);
Darin Petkovd9661952011-08-03 16:25:42 -0700457 }
458}
459
460void Cellular::CreateService() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700461 SLOG(Cellular, 2) << __func__;
Darin Petkovd9661952011-08-03 16:25:42 -0700462 CHECK(!service_.get());
Prathmesh Prabhu0d36b4f2013-04-01 11:45:54 -0700463 service_ = new CellularService(modem_info_, this);
Darin Petkovae0c64e2011-11-15 15:50:27 +0100464 capability_->OnServiceCreated();
Darin Petkov31332412012-01-28 01:50:02 +0100465 manager()->RegisterService(service_);
Darin Petkovf5f61e02011-07-29 11:35:40 -0700466}
467
Darin Petkov2c377382012-01-11 11:40:43 +0100468void Cellular::DestroyService() {
Darin Petkov0f5dfa02012-11-14 16:35:32 +0100469 DropConnection();
Darin Petkov2c377382012-01-11 11:40:43 +0100470 if (service_) {
Darin Petkov457728b2013-01-09 09:49:08 +0100471 LOG(INFO) << "Deregistering cellular service " << service_->unique_name()
Darin Petkov0f5dfa02012-11-14 16:35:32 +0100472 << " for device " << link_name();
Darin Petkov2c377382012-01-11 11:40:43 +0100473 manager()->DeregisterService(service_);
474 service_ = NULL;
475 }
Darin Petkov2c377382012-01-11 11:40:43 +0100476}
477
Darin Petkov4d6d9412011-08-24 13:19:54 -0700478void Cellular::Connect(Error *error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700479 SLOG(Cellular, 2) << __func__;
Eric Shienbrood30bc0ec2012-03-21 18:19:46 -0400480 if (state_ == kStateConnected || state_ == kStateLinked) {
Paul Stewartbe005172011-11-02 18:10:29 -0700481 Error::PopulateAndLog(error, Error::kAlreadyConnected,
482 "Already connected; connection request ignored.");
Darin Petkovc5f56562011-08-06 16:40:05 -0700483 return;
Thieu Lec7d8cd12013-02-13 11:38:14 -0800484 } else if (state_ != kStateRegistered) {
485 Error::PopulateAndLog(error, Error::kNotRegistered,
486 "Modem not registered; connection request ignored.");
487 return;
Darin Petkovc5f56562011-08-06 16:40:05 -0700488 }
Darin Petkovd2045802011-08-23 11:09:25 -0700489
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400490 if (!capability_->AllowRoaming() &&
Darin Petkovd2045802011-08-23 11:09:25 -0700491 service_->roaming_state() == flimflam::kRoamingStateRoaming) {
Paul Stewartbe005172011-11-02 18:10:29 -0700492 Error::PopulateAndLog(error, Error::kNotOnHomeNetwork,
493 "Roaming disallowed; connection request ignored.");
Darin Petkovd2045802011-08-23 11:09:25 -0700494 return;
495 }
496
Darin Petkovc5f56562011-08-06 16:40:05 -0700497 DBusPropertiesMap properties;
Darin Petkovae0c64e2011-11-15 15:50:27 +0100498 capability_->SetupConnectProperties(&properties);
Thieu Le64b0fe52012-08-08 14:57:36 -0700499 ResultCallback cb = Bind(&Cellular::OnConnectReply,
500 weak_ptr_factory_.GetWeakPtr());
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400501 OnConnecting();
Nathan Williamsb54974f2012-04-19 11:16:30 -0400502 capability_->Connect(properties, error, cb);
Thieu Le7cf36b02013-01-30 17:15:56 -0800503 if (!error->IsSuccess())
504 return;
505
506 bool is_auto_connecting = service_.get() && service_->is_auto_connecting();
507 metrics()->NotifyDeviceConnectStarted(interface_index(), is_auto_connecting);
Nathan Williamsb54974f2012-04-19 11:16:30 -0400508}
509
510// Note that there's no ResultCallback argument to this,
511// since Connect() isn't yet passed one.
512void Cellular::OnConnectReply(const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700513 SLOG(Cellular, 2) << __func__ << "(" << error << ")";
Thieu Lecdb5a212013-01-25 11:17:18 -0800514 if (error.IsSuccess()) {
515 metrics()->NotifyDeviceConnectFinished(interface_index());
Nathan Williamsb54974f2012-04-19 11:16:30 -0400516 OnConnected();
Thieu Lecdb5a212013-01-25 11:17:18 -0800517 } else {
Thieu Leb7aa5f72013-01-31 15:57:48 -0800518 metrics()->NotifyCellularDeviceFailure(error);
Nathan Williamsb54974f2012-04-19 11:16:30 -0400519 OnConnectFailed(error);
Thieu Lecdb5a212013-01-25 11:17:18 -0800520 }
Darin Petkovc5f56562011-08-06 16:40:05 -0700521}
522
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400523void Cellular::OnConnecting() {
524 if (service_)
525 service_->SetState(Service::kStateAssociating);
526}
527
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500528void Cellular::OnConnected() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700529 SLOG(Cellular, 2) << __func__;
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400530 if (state_ == kStateConnected || state_ == kStateLinked) {
Ben Chan09fa2a02012-11-07 22:09:09 -0800531 SLOG(Cellular, 2) << "Already connected";
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400532 return;
533 }
Darin Petkov1c01bef2012-09-13 15:27:17 +0200534 Closure start_cb = Bind(&Cellular::StartTermination,
535 weak_ptr_factory_.GetWeakPtr());
Gary Moraina9fb3252012-05-31 12:05:31 -0700536 manager()->AddTerminationAction(FriendlyName(), start_cb);
Darin Petkov0828f5f2011-08-11 10:18:52 -0700537 SetState(kStateConnected);
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400538 if (!capability_->AllowRoaming() &&
Darin Petkov9c1dcef2012-02-07 15:58:26 +0100539 service_->roaming_state() == flimflam::kRoamingStateRoaming) {
Arman Uguray539c4232012-09-11 10:00:22 -0700540 LOG(INFO) << "Disconnecting due to roaming.";
Darin Petkov9c1dcef2012-02-07 15:58:26 +0100541 Disconnect(NULL);
542 } else {
543 EstablishLink();
544 }
Darin Petkovbac96002011-08-09 13:22:00 -0700545}
546
Eric Shienbrood30bc0ec2012-03-21 18:19:46 -0400547void Cellular::OnConnectFailed(const Error &error) {
Thieu Leb5954a22012-05-18 10:37:34 -0700548 if (service_)
549 service_->SetFailure(Service::kFailureUnknown);
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500550}
551
Christopher Wiley8a468902012-11-30 11:52:38 -0800552void Cellular::Disconnect(Error *error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700553 SLOG(Cellular, 2) << __func__;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500554 if (state_ != kStateConnected && state_ != kStateLinked) {
Darin Petkovfb0625e2012-01-16 13:05:56 +0100555 Error::PopulateAndLog(
Eric Shienbrood9a245532012-03-07 14:20:39 -0500556 error, Error::kNotConnected, "Not connected; request ignored.");
Darin Petkovfb0625e2012-01-16 13:05:56 +0100557 return;
558 }
Thieu Le26fc01b2013-01-28 12:08:48 -0800559 explicit_disconnect_ = true;
Thieu Le64b0fe52012-08-08 14:57:36 -0700560 ResultCallback cb = Bind(&Cellular::OnDisconnectReply,
Christopher Wiley8a468902012-11-30 11:52:38 -0800561 weak_ptr_factory_.GetWeakPtr());
Nathan Williamsb54974f2012-04-19 11:16:30 -0400562 capability_->Disconnect(error, cb);
563}
564
Christopher Wiley8a468902012-11-30 11:52:38 -0800565void Cellular::OnDisconnectReply(const Error &error) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700566 SLOG(Cellular, 2) << __func__ << "(" << error << ")";
Thieu Le26fc01b2013-01-28 12:08:48 -0800567 explicit_disconnect_ = false;
Thieu Leb7aa5f72013-01-31 15:57:48 -0800568 if (error.IsSuccess()) {
Nathan Williamsb54974f2012-04-19 11:16:30 -0400569 OnDisconnected();
Thieu Leb7aa5f72013-01-31 15:57:48 -0800570 } else {
571 metrics()->NotifyCellularDeviceFailure(error);
Nathan Williamsb54974f2012-04-19 11:16:30 -0400572 OnDisconnectFailed();
Thieu Leb7aa5f72013-01-31 15:57:48 -0800573 }
Gary Moraina9fb3252012-05-31 12:05:31 -0700574 manager()->TerminationActionComplete(FriendlyName());
575 manager()->RemoveTerminationAction(FriendlyName());
Darin Petkovfb0625e2012-01-16 13:05:56 +0100576}
577
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500578void Cellular::OnDisconnected() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700579 SLOG(Cellular, 2) << __func__;
Arman Uguray539c4232012-09-11 10:00:22 -0700580 if (!DisconnectCleanup()) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500581 LOG(WARNING) << "Disconnect occurred while in state "
582 << GetStateString(state_);
Eric Shienbroodcc95c5d2012-03-30 15:25:49 -0400583 }
Eric Shienbrood5de44ab2011-12-05 10:46:27 -0500584}
585
586void Cellular::OnDisconnectFailed() {
Arman Uguray539c4232012-09-11 10:00:22 -0700587 SLOG(Cellular, 2) << __func__;
588 // If the modem is in the disconnecting state, then
589 // the disconnect should eventually succeed, so do
590 // nothing.
591 if (modem_state_ == kModemStateDisconnecting) {
592 LOG(WARNING) << "Ignoring failed disconnect while modem is disconnecting.";
593 return;
594 }
595
596 // OnDisconnectFailed got called because no bearers
597 // to disconnect were found. Which means that we shouldn't
598 // really remain in the connected/linked state if we
599 // are in one of those.
600 if (!DisconnectCleanup()) {
601 // otherwise, no-op
602 LOG(WARNING) << "Ignoring failed disconnect while in state "
603 << GetStateString(state_);
604 }
605
606 // TODO(armansito): In either case, shill ends up thinking
607 // that it's disconnected, while for some reason the underlying
608 // modem might still actually be connected. In that case the UI
609 // would be reflecting an incorrect state and a further connection
610 // request would fail. We should perhaps tear down the modem and
611 // restart it here.
Darin Petkovfb0625e2012-01-16 13:05:56 +0100612}
613
Darin Petkovbac96002011-08-09 13:22:00 -0700614void Cellular::EstablishLink() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700615 SLOG(Cellular, 2) << __func__;
Darin Petkov0828f5f2011-08-11 10:18:52 -0700616 CHECK_EQ(kStateConnected, state_);
617 unsigned int flags = 0;
Paul Stewartac4ac002011-08-26 12:04:26 -0700618 if (manager()->device_info()->GetFlags(interface_index(), &flags) &&
Darin Petkov0828f5f2011-08-11 10:18:52 -0700619 (flags & IFF_UP) != 0) {
620 LinkEvent(flags, IFF_UP);
621 return;
622 }
623 // TODO(petkov): Provide a timeout for a failed link-up request.
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700624 rtnl_handler()->SetInterfaceFlags(interface_index(), IFF_UP, IFF_UP);
Arman Uguray32c76402012-11-27 14:01:13 -0800625
626 // Set state to associating.
627 OnConnecting();
Darin Petkov0828f5f2011-08-11 10:18:52 -0700628}
629
630void Cellular::LinkEvent(unsigned int flags, unsigned int change) {
631 Device::LinkEvent(flags, change);
632 if ((flags & IFF_UP) != 0 && state_ == kStateConnected) {
Paul Stewartac4ac002011-08-26 12:04:26 -0700633 LOG(INFO) << link_name() << " is up.";
Darin Petkov0828f5f2011-08-11 10:18:52 -0700634 SetState(kStateLinked);
Paul Stewart2bf1d352011-12-06 15:02:55 -0800635 if (AcquireIPConfig()) {
Darin Petkov60b8c3b2011-08-25 11:03:20 -0700636 SelectService(service_);
637 SetServiceState(Service::kStateConfiguring);
638 } else {
639 LOG(ERROR) << "Unable to acquire DHCP config.";
640 }
Darin Petkov0828f5f2011-08-11 10:18:52 -0700641 } else if ((flags & IFF_UP) == 0 && state_ == kStateLinked) {
Arman Uguray32c76402012-11-27 14:01:13 -0800642 LOG(INFO) << link_name() << " is down.";
Darin Petkov0828f5f2011-08-11 10:18:52 -0700643 SetState(kStateConnected);
Arman Uguray32c76402012-11-27 14:01:13 -0800644 DropConnection();
Darin Petkov0828f5f2011-08-11 10:18:52 -0700645 }
Darin Petkovc5f56562011-08-06 16:40:05 -0700646}
647
Jason Glasgow82f9ab32012-04-04 14:27:19 -0400648void Cellular::OnDBusPropertiesChanged(
649 const string &interface,
650 const DBusPropertiesMap &changed_properties,
651 const vector<string> &invalidated_properties) {
652 capability_->OnDBusPropertiesChanged(interface,
653 changed_properties,
654 invalidated_properties);
655}
656
Darin Petkovae0c64e2011-11-15 15:50:27 +0100657void Cellular::set_home_provider(const Operator &oper) {
658 home_provider_.CopyFrom(oper);
659}
660
Darin Petkovac635a82012-01-10 16:51:58 +0100661string Cellular::CreateFriendlyServiceName() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700662 SLOG(Cellular, 2) << __func__;
Darin Petkovac635a82012-01-10 16:51:58 +0100663 return capability_.get() ? capability_->CreateFriendlyServiceName() : "";
664}
665
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400666void Cellular::OnModemStateChanged(ModemState old_state,
667 ModemState new_state,
668 uint32 /*reason*/) {
669 if (old_state == new_state) {
670 return;
671 }
672 set_modem_state(new_state);
Thieu Le5218cf22012-11-26 11:52:57 -0800673 if (old_state >= kModemStateRegistered &&
674 new_state < kModemStateRegistered) {
675 capability_->SetUnregistered(new_state == kModemStateSearching);
676 HandleNewRegistrationState();
677 }
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400678 switch (new_state) {
679 case kModemStateDisabled:
680 SetEnabled(false);
681 break;
682 case kModemStateEnabled:
Thieu Le5218cf22012-11-26 11:52:57 -0800683 // Transition from Disabled to Enabled is handled in the
684 // DBusPropertiesChanged handler.
685 SLOG(Cellular, 2) << __func__ << ": Ignoring state change to Enabled";
Thieu Led0012052012-07-25 16:09:09 -0700686 // Intentionally falls through.
Thieu Le5218cf22012-11-26 11:52:57 -0800687 case kModemStateSearching:
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400688 case kModemStateRegistered:
Thieu Led0012052012-07-25 16:09:09 -0700689 // If the modem state changes from Connecting/Connected/Disconnecting
690 // to Registered/Enabled/Searching, then it's an indication that the
691 // modem has been disconnected or got disconnected by the network.
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400692 if (old_state == kModemStateConnected ||
693 old_state == kModemStateConnecting ||
694 old_state == kModemStateDisconnecting)
695 OnDisconnected();
696 break;
697 case kModemStateConnecting:
698 OnConnecting();
699 break;
700 case kModemStateConnected:
Thieu Led0012052012-07-25 16:09:09 -0700701 if (old_state == kModemStateConnecting)
702 OnConnected();
703 else
704 SLOG(Cellular, 2) << __func__ << ": Ignoring state change to Connected";
Eric Shienbrood7fce52c2012-04-13 19:11:02 -0400705 break;
706 default:
707 break;
708 }
709}
710
Christopher Wiley1582bdd2012-11-15 11:31:14 -0800711bool Cellular::IsActivating() const {
712 return capability_->IsActivating();
713}
714
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700715bool Cellular::SetAllowRoaming(const bool &value, Error */*error*/) {
Jason Glasgow7b461df2012-05-01 16:38:45 -0400716 SLOG(Cellular, 2) << __func__
717 << "(" << allow_roaming_ << "->" << value << ")";
718 if (allow_roaming_ == value) {
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700719 return false;
Jason Glasgow7b461df2012-05-01 16:38:45 -0400720 }
721 allow_roaming_ = value;
Darin Petkove7c6ad32012-06-29 10:22:09 +0200722 manager()->UpdateDevice(this);
Jason Glasgow7b461df2012-05-01 16:38:45 -0400723
724 // Use AllowRoaming() instead of allow_roaming_ in order to
725 // incorporate provider preferences when evaluating if a disconnect
726 // is required.
727 if (!capability_->AllowRoaming() &&
728 capability_->GetRoamingStateString() == flimflam::kRoamingStateRoaming) {
729 Error error;
730 Disconnect(&error);
731 }
732 adaptor()->EmitBoolChanged(flimflam::kCellularAllowRoamingProperty, value);
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700733 return true;
Jason Glasgow7b461df2012-05-01 16:38:45 -0400734}
735
Gary Moraina9fb3252012-05-31 12:05:31 -0700736void Cellular::StartTermination() {
Darin Petkov3ec55342012-09-28 14:04:44 +0200737 LOG(INFO) << __func__;
Gary Moraina9fb3252012-05-31 12:05:31 -0700738 Error error;
739 Disconnect(&error);
740}
741
Arman Uguray539c4232012-09-11 10:00:22 -0700742bool Cellular::DisconnectCleanup() {
Christopher Wiley8a468902012-11-30 11:52:38 -0800743 bool succeeded = false;
Arman Uguray539c4232012-09-11 10:00:22 -0700744 if (state_ == kStateConnected || state_ == kStateLinked) {
745 SetState(kStateRegistered);
746 SetServiceFailureSilent(Service::kFailureUnknown);
747 DestroyIPConfig();
Christopher Wiley8a468902012-11-30 11:52:38 -0800748 succeeded = true;
Arman Uguray539c4232012-09-11 10:00:22 -0700749 }
Christopher Wiley8a468902012-11-30 11:52:38 -0800750 capability_->DisconnectCleanup();
751 return succeeded;
Arman Uguray539c4232012-09-11 10:00:22 -0700752}
Gary Moraina9fb3252012-05-31 12:05:31 -0700753
Chris Masone3bd3c8c2011-06-13 08:20:26 -0700754} // namespace shill