1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chromeos/dbus/fake_shill_service_client.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_util.h"
13 #include "base/values.h"
14 #include "chromeos/chromeos_switches.h"
15 #include "chromeos/dbus/dbus_thread_manager.h"
16 #include "chromeos/dbus/shill_manager_client.h"
17 #include "chromeos/dbus/shill_property_changed_observer.h"
18 #include "chromeos/dbus/shill_stub_helper.h"
19 #include "chromeos/network/shill_property_util.h"
20 #include "dbus/bus.h"
21 #include "dbus/message.h"
22 #include "dbus/object_path.h"
23 #include "third_party/cros_system_api/dbus/service_constants.h"
24
25 namespace chromeos {
26
27 namespace {
28
PassStubListValue(const ShillServiceClient::ListValueCallback & callback,base::ListValue * value)29 void PassStubListValue(const ShillServiceClient::ListValueCallback& callback,
30 base::ListValue* value) {
31 callback.Run(*value);
32 }
33
PassStubServiceProperties(const ShillServiceClient::DictionaryValueCallback & callback,DBusMethodCallStatus call_status,const base::DictionaryValue * properties)34 void PassStubServiceProperties(
35 const ShillServiceClient::DictionaryValueCallback& callback,
36 DBusMethodCallStatus call_status,
37 const base::DictionaryValue* properties) {
38 callback.Run(call_status, *properties);
39 }
40
41 } // namespace
42
FakeShillServiceClient()43 FakeShillServiceClient::FakeShillServiceClient() : weak_ptr_factory_(this) {
44 }
45
~FakeShillServiceClient()46 FakeShillServiceClient::~FakeShillServiceClient() {
47 STLDeleteContainerPairSecondPointers(
48 observer_list_.begin(), observer_list_.end());
49 }
50
51
52 // ShillServiceClient overrides.
53
Init(dbus::Bus * bus)54 void FakeShillServiceClient::Init(dbus::Bus* bus) {
55 }
56
AddPropertyChangedObserver(const dbus::ObjectPath & service_path,ShillPropertyChangedObserver * observer)57 void FakeShillServiceClient::AddPropertyChangedObserver(
58 const dbus::ObjectPath& service_path,
59 ShillPropertyChangedObserver* observer) {
60 GetObserverList(service_path).AddObserver(observer);
61 }
62
RemovePropertyChangedObserver(const dbus::ObjectPath & service_path,ShillPropertyChangedObserver * observer)63 void FakeShillServiceClient::RemovePropertyChangedObserver(
64 const dbus::ObjectPath& service_path,
65 ShillPropertyChangedObserver* observer) {
66 GetObserverList(service_path).RemoveObserver(observer);
67 }
68
GetProperties(const dbus::ObjectPath & service_path,const DictionaryValueCallback & callback)69 void FakeShillServiceClient::GetProperties(
70 const dbus::ObjectPath& service_path,
71 const DictionaryValueCallback& callback) {
72 base::DictionaryValue* nested_dict = NULL;
73 scoped_ptr<base::DictionaryValue> result_properties;
74 DBusMethodCallStatus call_status;
75 stub_services_.GetDictionaryWithoutPathExpansion(service_path.value(),
76 &nested_dict);
77 if (nested_dict) {
78 result_properties.reset(nested_dict->DeepCopy());
79 // Remove credentials that Shill wouldn't send.
80 result_properties->RemoveWithoutPathExpansion(shill::kPassphraseProperty,
81 NULL);
82 call_status = DBUS_METHOD_CALL_SUCCESS;
83 } else {
84 LOG(ERROR) << "Properties not found for: " << service_path.value();
85 result_properties.reset(new base::DictionaryValue);
86 call_status = DBUS_METHOD_CALL_FAILURE;
87 }
88
89 base::MessageLoop::current()->PostTask(
90 FROM_HERE,
91 base::Bind(&PassStubServiceProperties,
92 callback,
93 call_status,
94 base::Owned(result_properties.release())));
95 }
96
SetProperty(const dbus::ObjectPath & service_path,const std::string & name,const base::Value & value,const base::Closure & callback,const ErrorCallback & error_callback)97 void FakeShillServiceClient::SetProperty(const dbus::ObjectPath& service_path,
98 const std::string& name,
99 const base::Value& value,
100 const base::Closure& callback,
101 const ErrorCallback& error_callback) {
102 if (!SetServiceProperty(service_path.value(), name, value)) {
103 LOG(ERROR) << "Service not found: " << service_path.value();
104 error_callback.Run("Error.InvalidService", "Invalid Service");
105 return;
106 }
107 base::MessageLoop::current()->PostTask(FROM_HERE, callback);
108 }
109
SetProperties(const dbus::ObjectPath & service_path,const base::DictionaryValue & properties,const base::Closure & callback,const ErrorCallback & error_callback)110 void FakeShillServiceClient::SetProperties(
111 const dbus::ObjectPath& service_path,
112 const base::DictionaryValue& properties,
113 const base::Closure& callback,
114 const ErrorCallback& error_callback) {
115 for (base::DictionaryValue::Iterator iter(properties);
116 !iter.IsAtEnd(); iter.Advance()) {
117 if (!SetServiceProperty(service_path.value(), iter.key(), iter.value())) {
118 LOG(ERROR) << "Service not found: " << service_path.value();
119 error_callback.Run("Error.InvalidService", "Invalid Service");
120 return;
121 }
122 }
123 base::MessageLoop::current()->PostTask(FROM_HERE, callback);
124 }
125
ClearProperty(const dbus::ObjectPath & service_path,const std::string & name,const base::Closure & callback,const ErrorCallback & error_callback)126 void FakeShillServiceClient::ClearProperty(
127 const dbus::ObjectPath& service_path,
128 const std::string& name,
129 const base::Closure& callback,
130 const ErrorCallback& error_callback) {
131 base::DictionaryValue* dict = NULL;
132 if (!stub_services_.GetDictionaryWithoutPathExpansion(
133 service_path.value(), &dict)) {
134 error_callback.Run("Error.InvalidService", "Invalid Service");
135 return;
136 }
137 dict->RemoveWithoutPathExpansion(name, NULL);
138 // Note: Shill does not send notifications when properties are cleared.
139 base::MessageLoop::current()->PostTask(FROM_HERE, callback);
140 }
141
ClearProperties(const dbus::ObjectPath & service_path,const std::vector<std::string> & names,const ListValueCallback & callback,const ErrorCallback & error_callback)142 void FakeShillServiceClient::ClearProperties(
143 const dbus::ObjectPath& service_path,
144 const std::vector<std::string>& names,
145 const ListValueCallback& callback,
146 const ErrorCallback& error_callback) {
147 base::DictionaryValue* dict = NULL;
148 if (!stub_services_.GetDictionaryWithoutPathExpansion(
149 service_path.value(), &dict)) {
150 error_callback.Run("Error.InvalidService", "Invalid Service");
151 return;
152 }
153 scoped_ptr<base::ListValue> results(new base::ListValue);
154 for (std::vector<std::string>::const_iterator iter = names.begin();
155 iter != names.end(); ++iter) {
156 dict->RemoveWithoutPathExpansion(*iter, NULL);
157 // Note: Shill does not send notifications when properties are cleared.
158 results->AppendBoolean(true);
159 }
160 base::MessageLoop::current()->PostTask(
161 FROM_HERE,
162 base::Bind(&PassStubListValue,
163 callback, base::Owned(results.release())));
164 }
165
Connect(const dbus::ObjectPath & service_path,const base::Closure & callback,const ErrorCallback & error_callback)166 void FakeShillServiceClient::Connect(const dbus::ObjectPath& service_path,
167 const base::Closure& callback,
168 const ErrorCallback& error_callback) {
169 VLOG(1) << "FakeShillServiceClient::Connect: " << service_path.value();
170 base::DictionaryValue* service_properties = NULL;
171 if (!stub_services_.GetDictionary(
172 service_path.value(), &service_properties)) {
173 LOG(ERROR) << "Service not found: " << service_path.value();
174 error_callback.Run("Error.InvalidService", "Invalid Service");
175 return;
176 }
177
178 // Set any other services of the same Type to 'offline' first, before setting
179 // State to Association which will trigger sorting Manager.Services and
180 // sending an update.
181 SetOtherServicesOffline(service_path.value());
182
183 // Set Associating.
184 base::StringValue associating_value(shill::kStateAssociation);
185 SetServiceProperty(service_path.value(),
186 shill::kStateProperty,
187 associating_value);
188
189 // Stay Associating until the state is changed again after a delay.
190 base::TimeDelta delay;
191 if (CommandLine::ForCurrentProcess()->HasSwitch(
192 chromeos::switches::kEnableStubInteractive)) {
193 const int kConnectDelaySeconds = 5;
194 delay = base::TimeDelta::FromSeconds(kConnectDelaySeconds);
195 }
196 base::MessageLoop::current()->PostDelayedTask(
197 FROM_HERE,
198 base::Bind(&FakeShillServiceClient::ContinueConnect,
199 weak_ptr_factory_.GetWeakPtr(),
200 service_path.value()),
201 delay);
202
203 callback.Run();
204 }
205
Disconnect(const dbus::ObjectPath & service_path,const base::Closure & callback,const ErrorCallback & error_callback)206 void FakeShillServiceClient::Disconnect(const dbus::ObjectPath& service_path,
207 const base::Closure& callback,
208 const ErrorCallback& error_callback) {
209 base::Value* service;
210 if (!stub_services_.Get(service_path.value(), &service)) {
211 error_callback.Run("Error.InvalidService", "Invalid Service");
212 return;
213 }
214 base::TimeDelta delay;
215 if (CommandLine::ForCurrentProcess()->HasSwitch(
216 chromeos::switches::kEnableStubInteractive)) {
217 const int kConnectDelaySeconds = 2;
218 delay = base::TimeDelta::FromSeconds(kConnectDelaySeconds);
219 }
220 // Set Idle after a delay
221 base::StringValue idle_value(shill::kStateIdle);
222 base::MessageLoop::current()->PostDelayedTask(
223 FROM_HERE,
224 base::Bind(&FakeShillServiceClient::SetProperty,
225 weak_ptr_factory_.GetWeakPtr(),
226 service_path,
227 shill::kStateProperty,
228 idle_value,
229 base::Bind(&base::DoNothing),
230 error_callback),
231 delay);
232 callback.Run();
233 }
234
Remove(const dbus::ObjectPath & service_path,const base::Closure & callback,const ErrorCallback & error_callback)235 void FakeShillServiceClient::Remove(const dbus::ObjectPath& service_path,
236 const base::Closure& callback,
237 const ErrorCallback& error_callback) {
238 base::MessageLoop::current()->PostTask(FROM_HERE, callback);
239 }
240
ActivateCellularModem(const dbus::ObjectPath & service_path,const std::string & carrier,const base::Closure & callback,const ErrorCallback & error_callback)241 void FakeShillServiceClient::ActivateCellularModem(
242 const dbus::ObjectPath& service_path,
243 const std::string& carrier,
244 const base::Closure& callback,
245 const ErrorCallback& error_callback) {
246 base::DictionaryValue* service_properties =
247 GetModifiableServiceProperties(service_path.value(), false);
248 if (!service_properties) {
249 LOG(ERROR) << "Service not found: " << service_path.value();
250 error_callback.Run("Error.InvalidService", "Invalid Service");
251 }
252 SetServiceProperty(service_path.value(),
253 shill::kActivationStateProperty,
254 base::StringValue(shill::kActivationStateActivating));
255 base::TimeDelta delay;
256 if (CommandLine::ForCurrentProcess()->HasSwitch(
257 chromeos::switches::kEnableStubInteractive)) {
258 const int kConnectDelaySeconds = 2;
259 delay = base::TimeDelta::FromSeconds(kConnectDelaySeconds);
260 }
261 // Set Activated after a delay
262 base::MessageLoop::current()->PostDelayedTask(
263 FROM_HERE,
264 base::Bind(&FakeShillServiceClient::SetCellularActivated,
265 weak_ptr_factory_.GetWeakPtr(),
266 service_path,
267 error_callback),
268 delay);
269
270 base::MessageLoop::current()->PostTask(FROM_HERE, callback);
271 }
272
CompleteCellularActivation(const dbus::ObjectPath & service_path,const base::Closure & callback,const ErrorCallback & error_callback)273 void FakeShillServiceClient::CompleteCellularActivation(
274 const dbus::ObjectPath& service_path,
275 const base::Closure& callback,
276 const ErrorCallback& error_callback) {
277 base::MessageLoop::current()->PostTask(FROM_HERE, callback);
278 }
279
GetLoadableProfileEntries(const dbus::ObjectPath & service_path,const DictionaryValueCallback & callback)280 void FakeShillServiceClient::GetLoadableProfileEntries(
281 const dbus::ObjectPath& service_path,
282 const DictionaryValueCallback& callback) {
283 // Provide a dictionary with a single { profile_path, service_path } entry
284 // if the Profile property is set, or an empty dictionary.
285 scoped_ptr<base::DictionaryValue> result_properties(
286 new base::DictionaryValue);
287 base::DictionaryValue* service_properties =
288 GetModifiableServiceProperties(service_path.value(), false);
289 if (service_properties) {
290 std::string profile_path;
291 if (service_properties->GetStringWithoutPathExpansion(
292 shill::kProfileProperty, &profile_path)) {
293 result_properties->SetStringWithoutPathExpansion(
294 profile_path, service_path.value());
295 }
296 } else {
297 LOG(WARNING) << "Service not in profile: " << service_path.value();
298 }
299
300 DBusMethodCallStatus call_status = DBUS_METHOD_CALL_SUCCESS;
301 base::MessageLoop::current()->PostTask(
302 FROM_HERE,
303 base::Bind(&PassStubServiceProperties,
304 callback,
305 call_status,
306 base::Owned(result_properties.release())));
307 }
308
GetTestInterface()309 ShillServiceClient::TestInterface* FakeShillServiceClient::GetTestInterface() {
310 return this;
311 }
312
313 // ShillServiceClient::TestInterface overrides.
314
AddService(const std::string & service_path,const std::string & name,const std::string & type,const std::string & state,bool add_to_visible_list,bool add_to_watch_list)315 void FakeShillServiceClient::AddService(const std::string& service_path,
316 const std::string& name,
317 const std::string& type,
318 const std::string& state,
319 bool add_to_visible_list,
320 bool add_to_watch_list) {
321 std::string nstate = state;
322 if (CommandLine::ForCurrentProcess()->HasSwitch(
323 chromeos::switches::kDefaultStubNetworkStateIdle)) {
324 nstate = shill::kStateIdle;
325 }
326 AddServiceWithIPConfig(service_path, name, type, nstate, "",
327 add_to_visible_list, add_to_watch_list);
328 }
329
AddServiceWithIPConfig(const std::string & service_path,const std::string & name,const std::string & type,const std::string & state,const std::string & ipconfig_path,bool add_to_visible_list,bool add_to_watch_list)330 void FakeShillServiceClient::AddServiceWithIPConfig(
331 const std::string& service_path,
332 const std::string& name,
333 const std::string& type,
334 const std::string& state,
335 const std::string& ipconfig_path,
336 bool add_to_visible_list,
337 bool add_to_watch_list) {
338 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
339 AddManagerService(service_path, add_to_visible_list, add_to_watch_list);
340
341 base::DictionaryValue* properties =
342 GetModifiableServiceProperties(service_path, true);
343 connect_behavior_.erase(service_path);
344 shill_property_util::SetSSID(name, properties);
345 properties->SetWithoutPathExpansion(
346 shill::kNameProperty,
347 base::Value::CreateStringValue(name));
348 properties->SetWithoutPathExpansion(
349 shill::kDeviceProperty,
350 base::Value::CreateStringValue(
351 shill_stub_helper::DevicePathForType(type)));
352 properties->SetWithoutPathExpansion(
353 shill::kTypeProperty,
354 base::Value::CreateStringValue(type));
355 properties->SetWithoutPathExpansion(
356 shill::kStateProperty,
357 base::Value::CreateStringValue(state));
358 if (!ipconfig_path.empty())
359 properties->SetWithoutPathExpansion(
360 shill::kIPConfigProperty,
361 base::Value::CreateStringValue(ipconfig_path));
362 }
363
RemoveService(const std::string & service_path)364 void FakeShillServiceClient::RemoveService(const std::string& service_path) {
365 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
366 RemoveManagerService(service_path);
367
368 stub_services_.RemoveWithoutPathExpansion(service_path, NULL);
369 connect_behavior_.erase(service_path);
370 }
371
SetServiceProperty(const std::string & service_path,const std::string & property,const base::Value & value)372 bool FakeShillServiceClient::SetServiceProperty(const std::string& service_path,
373 const std::string& property,
374 const base::Value& value) {
375 base::DictionaryValue* dict = NULL;
376 if (!stub_services_.GetDictionaryWithoutPathExpansion(service_path, &dict))
377 return false;
378
379 VLOG(1) << "Service.SetProperty: " << property << " = " << value
380 << " For: " << service_path;
381
382 base::DictionaryValue new_properties;
383 std::string changed_property;
384 bool case_sensitive = true;
385 if (StartsWithASCII(property, "Provider.", case_sensitive) ||
386 StartsWithASCII(property, "OpenVPN.", case_sensitive) ||
387 StartsWithASCII(property, "L2TPIPsec.", case_sensitive)) {
388 // These properties are only nested within the Provider dictionary if read
389 // from Shill.
390 base::DictionaryValue* provider = new base::DictionaryValue;
391 provider->SetWithoutPathExpansion(property, value.DeepCopy());
392 new_properties.SetWithoutPathExpansion(shill::kProviderProperty, provider);
393 changed_property = shill::kProviderProperty;
394 } else {
395 new_properties.SetWithoutPathExpansion(property, value.DeepCopy());
396 changed_property = property;
397 }
398
399 dict->MergeDictionary(&new_properties);
400
401 if (property == shill::kStateProperty) {
402 // When State changes the sort order of Services may change.
403 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
404 SortManagerServices();
405 }
406
407 base::MessageLoop::current()->PostTask(
408 FROM_HERE,
409 base::Bind(&FakeShillServiceClient::NotifyObserversPropertyChanged,
410 weak_ptr_factory_.GetWeakPtr(),
411 dbus::ObjectPath(service_path), changed_property));
412 return true;
413 }
414
GetServiceProperties(const std::string & service_path) const415 const base::DictionaryValue* FakeShillServiceClient::GetServiceProperties(
416 const std::string& service_path) const {
417 const base::DictionaryValue* properties = NULL;
418 stub_services_.GetDictionaryWithoutPathExpansion(service_path, &properties);
419 return properties;
420 }
421
ClearServices()422 void FakeShillServiceClient::ClearServices() {
423 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
424 ClearManagerServices();
425
426 stub_services_.Clear();
427 connect_behavior_.clear();
428 }
429
SetConnectBehavior(const std::string & service_path,const base::Closure & behavior)430 void FakeShillServiceClient::SetConnectBehavior(const std::string& service_path,
431 const base::Closure& behavior) {
432 connect_behavior_[service_path] = behavior;
433 }
434
NotifyObserversPropertyChanged(const dbus::ObjectPath & service_path,const std::string & property)435 void FakeShillServiceClient::NotifyObserversPropertyChanged(
436 const dbus::ObjectPath& service_path,
437 const std::string& property) {
438 base::DictionaryValue* dict = NULL;
439 std::string path = service_path.value();
440 if (!stub_services_.GetDictionaryWithoutPathExpansion(path, &dict)) {
441 LOG(ERROR) << "Notify for unknown service: " << path;
442 return;
443 }
444 base::Value* value = NULL;
445 if (!dict->GetWithoutPathExpansion(property, &value)) {
446 LOG(ERROR) << "Notify for unknown property: "
447 << path << " : " << property;
448 return;
449 }
450 FOR_EACH_OBSERVER(ShillPropertyChangedObserver,
451 GetObserverList(service_path),
452 OnPropertyChanged(property, *value));
453 }
454
GetModifiableServiceProperties(const std::string & service_path,bool create_if_missing)455 base::DictionaryValue* FakeShillServiceClient::GetModifiableServiceProperties(
456 const std::string& service_path, bool create_if_missing) {
457 base::DictionaryValue* properties = NULL;
458 if (!stub_services_.GetDictionaryWithoutPathExpansion(service_path,
459 &properties) &&
460 create_if_missing) {
461 properties = new base::DictionaryValue;
462 stub_services_.Set(service_path, properties);
463 }
464 return properties;
465 }
466
467 FakeShillServiceClient::PropertyObserverList&
GetObserverList(const dbus::ObjectPath & device_path)468 FakeShillServiceClient::GetObserverList(const dbus::ObjectPath& device_path) {
469 std::map<dbus::ObjectPath, PropertyObserverList*>::iterator iter =
470 observer_list_.find(device_path);
471 if (iter != observer_list_.end())
472 return *(iter->second);
473 PropertyObserverList* observer_list = new PropertyObserverList();
474 observer_list_[device_path] = observer_list;
475 return *observer_list;
476 }
477
SetOtherServicesOffline(const std::string & service_path)478 void FakeShillServiceClient::SetOtherServicesOffline(
479 const std::string& service_path) {
480 const base::DictionaryValue* service_properties = GetServiceProperties(
481 service_path);
482 if (!service_properties) {
483 LOG(ERROR) << "Missing service: " << service_path;
484 return;
485 }
486 std::string service_type;
487 service_properties->GetString(shill::kTypeProperty, &service_type);
488 // Set all other services of the same type to offline (Idle).
489 for (base::DictionaryValue::Iterator iter(stub_services_);
490 !iter.IsAtEnd(); iter.Advance()) {
491 std::string path = iter.key();
492 if (path == service_path)
493 continue;
494 base::DictionaryValue* properties;
495 if (!stub_services_.GetDictionaryWithoutPathExpansion(path, &properties))
496 NOTREACHED();
497
498 std::string type;
499 properties->GetString(shill::kTypeProperty, &type);
500 if (type != service_type)
501 continue;
502 properties->SetWithoutPathExpansion(
503 shill::kStateProperty,
504 base::Value::CreateStringValue(shill::kStateIdle));
505 }
506 }
507
SetCellularActivated(const dbus::ObjectPath & service_path,const ErrorCallback & error_callback)508 void FakeShillServiceClient::SetCellularActivated(
509 const dbus::ObjectPath& service_path,
510 const ErrorCallback& error_callback) {
511 SetProperty(service_path,
512 shill::kActivationStateProperty,
513 base::StringValue(shill::kActivationStateActivated),
514 base::Bind(&base::DoNothing),
515 error_callback);
516 SetProperty(service_path,
517 shill::kConnectableProperty,
518 base::FundamentalValue(true),
519 base::Bind(&base::DoNothing),
520 error_callback);
521 }
522
ContinueConnect(const std::string & service_path)523 void FakeShillServiceClient::ContinueConnect(
524 const std::string& service_path) {
525 VLOG(1) << "FakeShillServiceClient::ContinueConnect: " << service_path;
526 base::DictionaryValue* service_properties = NULL;
527 if (!stub_services_.GetDictionary(service_path, &service_properties)) {
528 LOG(ERROR) << "Service not found: " << service_path;
529 return;
530 }
531
532 if (ContainsKey(connect_behavior_, service_path)) {
533 const base::Closure& custom_connect_behavior =
534 connect_behavior_[service_path];
535 custom_connect_behavior.Run();
536 return;
537 }
538
539 // No custom connect behavior set, continue with the default connect behavior.
540 std::string passphrase;
541 service_properties->GetStringWithoutPathExpansion(
542 shill::kPassphraseProperty, &passphrase);
543 if (passphrase == "failure") {
544 // Simulate a password failure.
545 SetServiceProperty(service_path,
546 shill::kStateProperty,
547 base::StringValue(shill::kStateFailure));
548 base::MessageLoop::current()->PostTask(
549 FROM_HERE,
550 base::Bind(
551 base::IgnoreResult(&FakeShillServiceClient::SetServiceProperty),
552 weak_ptr_factory_.GetWeakPtr(),
553 service_path,
554 shill::kErrorProperty,
555 base::StringValue(shill::kErrorBadPassphrase)));
556 } else {
557 // Set Online.
558 SetServiceProperty(service_path,
559 shill::kStateProperty,
560 base::StringValue(shill::kStateOnline));
561 }
562 }
563
564 } // namespace chromeos
565