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.
5 #include "chromeos/dbus/fake_shill_service_client.h"
8 #include "base/bind_helpers.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/values.h"
13 #include "chromeos/dbus/dbus_thread_manager.h"
14 #include "chromeos/dbus/shill_device_client.h"
15 #include "chromeos/dbus/shill_manager_client.h"
16 #include "chromeos/dbus/shill_profile_client.h"
17 #include "chromeos/dbus/shill_property_changed_observer.h"
18 #include "chromeos/network/shill_property_util.h"
20 #include "dbus/message.h"
21 #include "dbus/object_path.h"
22 #include "third_party/cros_system_api/dbus/service_constants.h"
28 void PassStubListValue(const ShillServiceClient::ListValueCallback
& callback
,
29 base::ListValue
* value
) {
33 void PassStubServiceProperties(
34 const ShillServiceClient::DictionaryValueCallback
& callback
,
35 DBusMethodCallStatus call_status
,
36 const base::DictionaryValue
* properties
) {
37 callback
.Run(call_status
, *properties
);
40 void CallSortManagerServices() {
41 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
42 SortManagerServices(true);
45 int GetInteractiveDelay() {
46 return DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
47 GetInteractiveDelay();
52 FakeShillServiceClient::FakeShillServiceClient() : weak_ptr_factory_(this) {
55 FakeShillServiceClient::~FakeShillServiceClient() {
56 STLDeleteContainerPairSecondPointers(
57 observer_list_
.begin(), observer_list_
.end());
61 // ShillServiceClient overrides.
63 void FakeShillServiceClient::Init(dbus::Bus
* bus
) {
66 void FakeShillServiceClient::AddPropertyChangedObserver(
67 const dbus::ObjectPath
& service_path
,
68 ShillPropertyChangedObserver
* observer
) {
69 GetObserverList(service_path
).AddObserver(observer
);
72 void FakeShillServiceClient::RemovePropertyChangedObserver(
73 const dbus::ObjectPath
& service_path
,
74 ShillPropertyChangedObserver
* observer
) {
75 GetObserverList(service_path
).RemoveObserver(observer
);
78 void FakeShillServiceClient::GetProperties(
79 const dbus::ObjectPath
& service_path
,
80 const DictionaryValueCallback
& callback
) {
81 base::DictionaryValue
* nested_dict
= NULL
;
82 scoped_ptr
<base::DictionaryValue
> result_properties
;
83 DBusMethodCallStatus call_status
;
84 stub_services_
.GetDictionaryWithoutPathExpansion(service_path
.value(),
87 result_properties
.reset(nested_dict
->DeepCopy());
88 // Remove credentials that Shill wouldn't send.
89 result_properties
->RemoveWithoutPathExpansion(shill::kPassphraseProperty
,
91 call_status
= DBUS_METHOD_CALL_SUCCESS
;
93 // This may happen if we remove services from the list.
94 VLOG(2) << "Properties not found for: " << service_path
.value();
95 result_properties
.reset(new base::DictionaryValue
);
96 call_status
= DBUS_METHOD_CALL_FAILURE
;
99 base::MessageLoop::current()->PostTask(
101 base::Bind(&PassStubServiceProperties
,
104 base::Owned(result_properties
.release())));
107 void FakeShillServiceClient::SetProperty(const dbus::ObjectPath
& service_path
,
108 const std::string
& name
,
109 const base::Value
& value
,
110 const base::Closure
& callback
,
111 const ErrorCallback
& error_callback
) {
112 if (!SetServiceProperty(service_path
.value(), name
, value
)) {
113 LOG(ERROR
) << "Service not found: " << service_path
.value();
114 error_callback
.Run("Error.InvalidService", "Invalid Service");
117 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
120 void FakeShillServiceClient::SetProperties(
121 const dbus::ObjectPath
& service_path
,
122 const base::DictionaryValue
& properties
,
123 const base::Closure
& callback
,
124 const ErrorCallback
& error_callback
) {
125 for (base::DictionaryValue::Iterator
iter(properties
);
126 !iter
.IsAtEnd(); iter
.Advance()) {
127 if (!SetServiceProperty(service_path
.value(), iter
.key(), iter
.value())) {
128 LOG(ERROR
) << "Service not found: " << service_path
.value();
129 error_callback
.Run("Error.InvalidService", "Invalid Service");
133 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
136 void FakeShillServiceClient::ClearProperty(
137 const dbus::ObjectPath
& service_path
,
138 const std::string
& name
,
139 const base::Closure
& callback
,
140 const ErrorCallback
& error_callback
) {
141 base::DictionaryValue
* dict
= NULL
;
142 if (!stub_services_
.GetDictionaryWithoutPathExpansion(
143 service_path
.value(), &dict
)) {
144 error_callback
.Run("Error.InvalidService", "Invalid Service");
147 dict
->RemoveWithoutPathExpansion(name
, NULL
);
148 // Note: Shill does not send notifications when properties are cleared.
149 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
152 void FakeShillServiceClient::ClearProperties(
153 const dbus::ObjectPath
& service_path
,
154 const std::vector
<std::string
>& names
,
155 const ListValueCallback
& callback
,
156 const ErrorCallback
& error_callback
) {
157 base::DictionaryValue
* dict
= NULL
;
158 if (!stub_services_
.GetDictionaryWithoutPathExpansion(
159 service_path
.value(), &dict
)) {
160 error_callback
.Run("Error.InvalidService", "Invalid Service");
163 scoped_ptr
<base::ListValue
> results(new base::ListValue
);
164 for (std::vector
<std::string
>::const_iterator iter
= names
.begin();
165 iter
!= names
.end(); ++iter
) {
166 dict
->RemoveWithoutPathExpansion(*iter
, NULL
);
167 // Note: Shill does not send notifications when properties are cleared.
168 results
->AppendBoolean(true);
170 base::MessageLoop::current()->PostTask(
172 base::Bind(&PassStubListValue
,
173 callback
, base::Owned(results
.release())));
176 void FakeShillServiceClient::Connect(const dbus::ObjectPath
& service_path
,
177 const base::Closure
& callback
,
178 const ErrorCallback
& error_callback
) {
179 VLOG(1) << "FakeShillServiceClient::Connect: " << service_path
.value();
180 base::DictionaryValue
* service_properties
= NULL
;
181 if (!stub_services_
.GetDictionary(service_path
.value(),
182 &service_properties
)) {
183 LOG(ERROR
) << "Service not found: " << service_path
.value();
184 error_callback
.Run("Error.InvalidService", "Invalid Service");
188 // Set any other services of the same Type to 'offline' first, before setting
189 // State to Association which will trigger sorting Manager.Services and
190 // sending an update.
191 SetOtherServicesOffline(service_path
.value());
194 service_properties
->SetStringWithoutPathExpansion(shill::kErrorProperty
, "");
197 base::StringValue
associating_value(shill::kStateAssociation
);
198 SetServiceProperty(service_path
.value(), shill::kStateProperty
,
201 // Stay Associating until the state is changed again after a delay.
202 base::MessageLoop::current()->PostDelayedTask(
204 base::Bind(&FakeShillServiceClient::ContinueConnect
,
205 weak_ptr_factory_
.GetWeakPtr(), service_path
.value()),
206 base::TimeDelta::FromSeconds(GetInteractiveDelay()));
208 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
211 void FakeShillServiceClient::Disconnect(const dbus::ObjectPath
& service_path
,
212 const base::Closure
& callback
,
213 const ErrorCallback
& error_callback
) {
214 base::Value
* service
;
215 if (!stub_services_
.Get(service_path
.value(), &service
)) {
216 error_callback
.Run("Error.InvalidService", "Invalid Service");
219 // Set Idle after a delay
220 base::StringValue
idle_value(shill::kStateIdle
);
221 base::MessageLoop::current()->PostDelayedTask(
223 base::Bind(&FakeShillServiceClient::SetProperty
,
224 weak_ptr_factory_
.GetWeakPtr(),
226 shill::kStateProperty
,
228 base::Bind(&base::DoNothing
),
230 base::TimeDelta::FromSeconds(GetInteractiveDelay()));
234 void FakeShillServiceClient::Remove(const dbus::ObjectPath
& service_path
,
235 const base::Closure
& callback
,
236 const ErrorCallback
& error_callback
) {
237 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
240 void FakeShillServiceClient::ActivateCellularModem(
241 const dbus::ObjectPath
& service_path
,
242 const std::string
& carrier
,
243 const base::Closure
& callback
,
244 const ErrorCallback
& error_callback
) {
245 base::DictionaryValue
* service_properties
=
246 GetModifiableServiceProperties(service_path
.value(), false);
247 if (!service_properties
) {
248 LOG(ERROR
) << "Service not found: " << service_path
.value();
249 error_callback
.Run("Error.InvalidService", "Invalid Service");
251 SetServiceProperty(service_path
.value(),
252 shill::kActivationStateProperty
,
253 base::StringValue(shill::kActivationStateActivating
));
254 // Set Activated after a delay
255 base::MessageLoop::current()->PostDelayedTask(
257 base::Bind(&FakeShillServiceClient::SetCellularActivated
,
258 weak_ptr_factory_
.GetWeakPtr(),
261 base::TimeDelta::FromSeconds(GetInteractiveDelay()));
263 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
266 void FakeShillServiceClient::CompleteCellularActivation(
267 const dbus::ObjectPath
& service_path
,
268 const base::Closure
& callback
,
269 const ErrorCallback
& error_callback
) {
270 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
273 void FakeShillServiceClient::GetLoadableProfileEntries(
274 const dbus::ObjectPath
& service_path
,
275 const DictionaryValueCallback
& callback
) {
276 // Provide a dictionary with a single { profile_path, service_path } entry
277 // if the Profile property is set, or an empty dictionary.
278 scoped_ptr
<base::DictionaryValue
> result_properties(
279 new base::DictionaryValue
);
280 base::DictionaryValue
* service_properties
=
281 GetModifiableServiceProperties(service_path
.value(), false);
282 if (service_properties
) {
283 std::string profile_path
;
284 if (service_properties
->GetStringWithoutPathExpansion(
285 shill::kProfileProperty
, &profile_path
)) {
286 result_properties
->SetStringWithoutPathExpansion(
287 profile_path
, service_path
.value());
290 LOG(WARNING
) << "Service not in profile: " << service_path
.value();
293 DBusMethodCallStatus call_status
= DBUS_METHOD_CALL_SUCCESS
;
294 base::MessageLoop::current()->PostTask(
296 base::Bind(&PassStubServiceProperties
,
299 base::Owned(result_properties
.release())));
302 ShillServiceClient::TestInterface
* FakeShillServiceClient::GetTestInterface() {
306 // ShillServiceClient::TestInterface overrides.
308 void FakeShillServiceClient::AddService(const std::string
& service_path
,
309 const std::string
& guid
,
310 const std::string
& name
,
311 const std::string
& type
,
312 const std::string
& state
,
314 AddServiceWithIPConfig(service_path
, guid
, name
,
315 type
, state
, "" /* ipconfig_path */,
319 void FakeShillServiceClient::AddServiceWithIPConfig(
320 const std::string
& service_path
,
321 const std::string
& guid
,
322 const std::string
& name
,
323 const std::string
& type
,
324 const std::string
& state
,
325 const std::string
& ipconfig_path
,
327 base::DictionaryValue
* properties
= SetServiceProperties(
328 service_path
, guid
, name
, type
, state
, visible
);
330 std::string profile_path
;
331 if (properties
->GetStringWithoutPathExpansion(shill::kProfileProperty
,
333 !profile_path
.empty()) {
334 DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface()->
335 UpdateService(profile_path
, service_path
);
338 if (!ipconfig_path
.empty()) {
339 properties
->SetWithoutPathExpansion(
340 shill::kIPConfigProperty
,
341 new base::StringValue(ipconfig_path
));
344 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
345 AddManagerService(service_path
, true);
348 base::DictionaryValue
* FakeShillServiceClient::SetServiceProperties(
349 const std::string
& service_path
,
350 const std::string
& guid
,
351 const std::string
& name
,
352 const std::string
& type
,
353 const std::string
& state
,
355 base::DictionaryValue
* properties
=
356 GetModifiableServiceProperties(service_path
, true);
357 connect_behavior_
.erase(service_path
);
359 std::string profile_path
;
360 base::DictionaryValue profile_properties
;
361 if (DBusThreadManager::Get()
362 ->GetShillProfileClient()
364 ->GetService(service_path
, &profile_path
, &profile_properties
)) {
365 properties
->SetStringWithoutPathExpansion(shill::kProfileProperty
,
369 // If |guid| is provided, set Service.GUID to that. Otherwise if a GUID is
370 // stored in a profile entry, use that. Otherwise leave it blank. Shill does
371 // not enforce a valid guid, we do that at the NetworkStateHandler layer.
372 std::string guid_to_set
= guid
;
373 if (guid_to_set
.empty()) {
374 profile_properties
.GetStringWithoutPathExpansion(shill::kGuidProperty
,
377 if (!guid_to_set
.empty()) {
378 properties
->SetStringWithoutPathExpansion(shill::kGuidProperty
,
381 properties
->SetStringWithoutPathExpansion(shill::kSSIDProperty
, name
);
382 shill_property_util::SetSSID(name
, properties
); // Sets kWifiHexSsid
383 properties
->SetStringWithoutPathExpansion(shill::kNameProperty
, name
);
384 std::string device_path
= DBusThreadManager::Get()
385 ->GetShillDeviceClient()
387 ->GetDevicePathForType(type
);
388 properties
->SetStringWithoutPathExpansion(shill::kDeviceProperty
,
390 properties
->SetStringWithoutPathExpansion(shill::kTypeProperty
, type
);
391 properties
->SetStringWithoutPathExpansion(shill::kStateProperty
, state
);
392 properties
->SetBooleanWithoutPathExpansion(shill::kVisibleProperty
, visible
);
393 if (type
== shill::kTypeWifi
) {
394 properties
->SetStringWithoutPathExpansion(shill::kSecurityClassProperty
,
395 shill::kSecurityNone
);
396 properties
->SetStringWithoutPathExpansion(shill::kModeProperty
,
397 shill::kModeManaged
);
402 void FakeShillServiceClient::RemoveService(const std::string
& service_path
) {
403 stub_services_
.RemoveWithoutPathExpansion(service_path
, NULL
);
404 connect_behavior_
.erase(service_path
);
405 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
406 RemoveManagerService(service_path
);
409 bool FakeShillServiceClient::SetServiceProperty(const std::string
& service_path
,
410 const std::string
& property
,
411 const base::Value
& value
) {
412 base::DictionaryValue
* dict
= NULL
;
413 if (!stub_services_
.GetDictionaryWithoutPathExpansion(service_path
, &dict
))
416 VLOG(1) << "Service.SetProperty: " << property
<< " = " << value
417 << " For: " << service_path
;
419 base::DictionaryValue new_properties
;
420 std::string changed_property
;
421 bool case_sensitive
= true;
422 if (StartsWithASCII(property
, "Provider.", case_sensitive
) ||
423 StartsWithASCII(property
, "OpenVPN.", case_sensitive
) ||
424 StartsWithASCII(property
, "L2TPIPsec.", case_sensitive
)) {
425 // These properties are only nested within the Provider dictionary if read
426 // from Shill. Properties that start with "Provider" need to have that
427 // stripped off, other properties are nested in the "Provider" dictionary
429 std::string key
= property
;
430 if (StartsWithASCII(property
, "Provider.", case_sensitive
))
431 key
= property
.substr(strlen("Provider."));
432 base::DictionaryValue
* provider
= new base::DictionaryValue
;
433 provider
->SetWithoutPathExpansion(key
, value
.DeepCopy());
434 new_properties
.SetWithoutPathExpansion(shill::kProviderProperty
, provider
);
435 changed_property
= shill::kProviderProperty
;
436 } else if (value
.GetType() == base::Value::TYPE_DICTIONARY
) {
437 const base::DictionaryValue
* new_dict
= NULL
;
438 value
.GetAsDictionary(&new_dict
);
440 scoped_ptr
<base::Value
> cur_value
;
441 base::DictionaryValue
* cur_dict
;
442 if (dict
->RemoveWithoutPathExpansion(property
, &cur_value
) &&
443 cur_value
->GetAsDictionary(&cur_dict
)) {
445 cur_dict
->MergeDictionary(new_dict
);
446 new_properties
.SetWithoutPathExpansion(property
, cur_value
.release());
448 new_properties
.SetWithoutPathExpansion(property
, value
.DeepCopy());
450 changed_property
= property
;
452 new_properties
.SetWithoutPathExpansion(property
, value
.DeepCopy());
453 changed_property
= property
;
456 dict
->MergeDictionary(&new_properties
);
458 // Add or update the profile entry.
459 ShillProfileClient::TestInterface
* profile_test
=
460 DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface();
461 if (property
== shill::kProfileProperty
) {
462 std::string profile_path
;
463 if (value
.GetAsString(&profile_path
)) {
464 if (!profile_path
.empty())
465 profile_test
->AddService(profile_path
, service_path
);
467 LOG(ERROR
) << "Profile value is not a String!";
470 std::string profile_path
;
471 if (dict
->GetStringWithoutPathExpansion(
472 shill::kProfileProperty
, &profile_path
) && !profile_path
.empty()) {
473 profile_test
->UpdateService(profile_path
, service_path
);
477 // Notify the Manager if the state changed (affects DefaultService).
478 if (property
== shill::kStateProperty
) {
480 value
.GetAsString(&state
);
481 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
482 ServiceStateChanged(service_path
, state
);
485 // If the State or Visibility changes, the sort order of service lists may
486 // change and the DefaultService property may change.
487 if (property
== shill::kStateProperty
||
488 property
== shill::kVisibleProperty
) {
489 base::MessageLoop::current()->PostTask(
490 FROM_HERE
, base::Bind(&CallSortManagerServices
));
493 // Notifiy Chrome of the property change.
494 base::MessageLoop::current()->PostTask(
496 base::Bind(&FakeShillServiceClient::NotifyObserversPropertyChanged
,
497 weak_ptr_factory_
.GetWeakPtr(),
498 dbus::ObjectPath(service_path
), changed_property
));
502 const base::DictionaryValue
* FakeShillServiceClient::GetServiceProperties(
503 const std::string
& service_path
) const {
504 const base::DictionaryValue
* properties
= NULL
;
505 stub_services_
.GetDictionaryWithoutPathExpansion(service_path
, &properties
);
509 void FakeShillServiceClient::ClearServices() {
510 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
511 ClearManagerServices();
513 stub_services_
.Clear();
514 connect_behavior_
.clear();
517 void FakeShillServiceClient::SetConnectBehavior(const std::string
& service_path
,
518 const base::Closure
& behavior
) {
519 connect_behavior_
[service_path
] = behavior
;
522 void FakeShillServiceClient::NotifyObserversPropertyChanged(
523 const dbus::ObjectPath
& service_path
,
524 const std::string
& property
) {
525 base::DictionaryValue
* dict
= NULL
;
526 std::string path
= service_path
.value();
527 if (!stub_services_
.GetDictionaryWithoutPathExpansion(path
, &dict
)) {
528 LOG(ERROR
) << "Notify for unknown service: " << path
;
531 base::Value
* value
= NULL
;
532 if (!dict
->GetWithoutPathExpansion(property
, &value
)) {
533 LOG(ERROR
) << "Notify for unknown property: "
534 << path
<< " : " << property
;
537 FOR_EACH_OBSERVER(ShillPropertyChangedObserver
,
538 GetObserverList(service_path
),
539 OnPropertyChanged(property
, *value
));
542 base::DictionaryValue
* FakeShillServiceClient::GetModifiableServiceProperties(
543 const std::string
& service_path
, bool create_if_missing
) {
544 base::DictionaryValue
* properties
= NULL
;
545 if (!stub_services_
.GetDictionaryWithoutPathExpansion(service_path
,
548 properties
= new base::DictionaryValue
;
549 stub_services_
.Set(service_path
, properties
);
554 FakeShillServiceClient::PropertyObserverList
&
555 FakeShillServiceClient::GetObserverList(const dbus::ObjectPath
& device_path
) {
556 std::map
<dbus::ObjectPath
, PropertyObserverList
*>::iterator iter
=
557 observer_list_
.find(device_path
);
558 if (iter
!= observer_list_
.end())
559 return *(iter
->second
);
560 PropertyObserverList
* observer_list
= new PropertyObserverList();
561 observer_list_
[device_path
] = observer_list
;
562 return *observer_list
;
565 void FakeShillServiceClient::SetOtherServicesOffline(
566 const std::string
& service_path
) {
567 const base::DictionaryValue
* service_properties
= GetServiceProperties(
569 if (!service_properties
) {
570 LOG(ERROR
) << "Missing service: " << service_path
;
573 std::string service_type
;
574 service_properties
->GetString(shill::kTypeProperty
, &service_type
);
575 // Set all other services of the same type to offline (Idle).
576 for (base::DictionaryValue::Iterator
iter(stub_services_
);
577 !iter
.IsAtEnd(); iter
.Advance()) {
578 std::string path
= iter
.key();
579 if (path
== service_path
)
581 base::DictionaryValue
* properties
;
582 if (!stub_services_
.GetDictionaryWithoutPathExpansion(path
, &properties
))
586 properties
->GetString(shill::kTypeProperty
, &type
);
587 if (type
!= service_type
)
589 properties
->SetWithoutPathExpansion(
590 shill::kStateProperty
,
591 new base::StringValue(shill::kStateIdle
));
595 void FakeShillServiceClient::SetCellularActivated(
596 const dbus::ObjectPath
& service_path
,
597 const ErrorCallback
& error_callback
) {
598 SetProperty(service_path
,
599 shill::kActivationStateProperty
,
600 base::StringValue(shill::kActivationStateActivated
),
601 base::Bind(&base::DoNothing
),
603 SetProperty(service_path
,
604 shill::kConnectableProperty
,
605 base::FundamentalValue(true),
606 base::Bind(&base::DoNothing
),
610 void FakeShillServiceClient::ContinueConnect(const std::string
& service_path
) {
611 VLOG(1) << "FakeShillServiceClient::ContinueConnect: " << service_path
;
612 base::DictionaryValue
* service_properties
= NULL
;
613 if (!stub_services_
.GetDictionary(service_path
, &service_properties
)) {
614 LOG(ERROR
) << "Service not found: " << service_path
;
618 if (ContainsKey(connect_behavior_
, service_path
)) {
619 const base::Closure
& custom_connect_behavior
=
620 connect_behavior_
[service_path
];
621 VLOG(1) << "Running custom connect behavior for " << service_path
;
622 custom_connect_behavior
.Run();
626 // No custom connect behavior set, continue with the default connect behavior.
627 std::string passphrase
;
628 service_properties
->GetStringWithoutPathExpansion(shill::kPassphraseProperty
,
630 if (passphrase
== "failure") {
631 // Simulate a password failure.
632 SetServiceProperty(service_path
, shill::kErrorProperty
,
633 base::StringValue(shill::kErrorBadPassphrase
));
634 SetServiceProperty(service_path
, shill::kStateProperty
,
635 base::StringValue(shill::kStateFailure
));
636 base::MessageLoop::current()->PostTask(
639 base::IgnoreResult(&FakeShillServiceClient::SetServiceProperty
),
640 weak_ptr_factory_
.GetWeakPtr(), service_path
, shill::kErrorProperty
,
641 base::StringValue(shill::kErrorBadPassphrase
)));
644 VLOG(1) << "Setting state to Online " << service_path
;
645 SetServiceProperty(service_path
, shill::kStateProperty
,
646 base::StringValue(shill::kStateOnline
));
650 } // namespace chromeos