managed_network_configuration_handler_unittest.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
1// Copyright (c) 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 <iostream> 6#include <sstream> 7 8#include "base/message_loop/message_loop.h" 9#include "chromeos/dbus/dbus_thread_manager.h" 10#include "chromeos/dbus/mock_dbus_thread_manager.h" 11#include "chromeos/dbus/mock_shill_manager_client.h" 12#include "chromeos/dbus/mock_shill_profile_client.h" 13#include "chromeos/dbus/mock_shill_service_client.h" 14#include "chromeos/network/managed_network_configuration_handler_impl.h" 15#include "chromeos/network/network_configuration_handler.h" 16#include "chromeos/network/network_profile_handler.h" 17#include "chromeos/network/onc/onc_test_utils.h" 18#include "chromeos/network/onc/onc_utils.h" 19#include "dbus/object_path.h" 20#include "testing/gmock/include/gmock/gmock.h" 21#include "testing/gtest/include/gtest/gtest.h" 22#include "third_party/cros_system_api/dbus/service_constants.h" 23 24using ::testing::AnyNumber; 25using ::testing::Invoke; 26using ::testing::Mock; 27using ::testing::Pointee; 28using ::testing::Return; 29using ::testing::SaveArg; 30using ::testing::StrEq; 31using ::testing::StrictMock; 32using ::testing::_; 33 34namespace test_utils = ::chromeos::onc::test_utils; 35 36namespace chromeos { 37 38namespace { 39 40std::string ValueToString(const base::Value* value) { 41 std::stringstream str; 42 str << *value; 43 return str.str(); 44} 45 46const char kSharedProfilePath[] = "/profile/default"; 47const char kUser1[] = "user1"; 48const char kUser1ProfilePath[] = "/profile/user1/shill"; 49 50// Matcher to match base::Value. 51MATCHER_P(IsEqualTo, 52 value, 53 std::string(negation ? "isn't" : "is") + " equal to " + 54 ValueToString(value)) { 55 return value->Equals(&arg); 56} 57 58class ShillProfileTestClient { 59 public: 60 typedef ShillClientHelper::DictionaryValueCallbackWithoutStatus 61 DictionaryValueCallbackWithoutStatus; 62 typedef ShillClientHelper::ErrorCallback ErrorCallback; 63 64 void AddProfile(const std::string& profile_path, 65 const std::string& userhash) { 66 if (profile_entries_.HasKey(profile_path)) 67 return; 68 69 base::DictionaryValue* profile = new base::DictionaryValue; 70 profile_entries_.SetWithoutPathExpansion(profile_path, profile); 71 profile_to_user_[profile_path] = userhash; 72 } 73 74 void AddEntry(const std::string& profile_path, 75 const std::string& entry_path, 76 const base::DictionaryValue& entry) { 77 base::DictionaryValue* entries = NULL; 78 profile_entries_.GetDictionaryWithoutPathExpansion(profile_path, &entries); 79 ASSERT_TRUE(entries); 80 81 base::DictionaryValue* new_entry = entry.DeepCopy(); 82 new_entry->SetStringWithoutPathExpansion(shill::kProfileProperty, 83 profile_path); 84 entries->SetWithoutPathExpansion(entry_path, new_entry); 85 } 86 87 void GetProperties(const dbus::ObjectPath& profile_path, 88 const DictionaryValueCallbackWithoutStatus& callback, 89 const ErrorCallback& error_callback) { 90 base::DictionaryValue* entries = NULL; 91 profile_entries_.GetDictionaryWithoutPathExpansion(profile_path.value(), 92 &entries); 93 ASSERT_TRUE(entries); 94 95 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue); 96 base::ListValue* entry_paths = new base::ListValue; 97 result->SetWithoutPathExpansion(shill::kEntriesProperty, entry_paths); 98 for (base::DictionaryValue::Iterator it(*entries); !it.IsAtEnd(); 99 it.Advance()) { 100 entry_paths->AppendString(it.key()); 101 } 102 103 ASSERT_GT(profile_to_user_.count(profile_path.value()), 0UL); 104 const std::string& userhash = profile_to_user_[profile_path.value()]; 105 result->SetStringWithoutPathExpansion(shill::kUserHashProperty, userhash); 106 107 callback.Run(*result); 108 } 109 110 void GetEntry(const dbus::ObjectPath& profile_path, 111 const std::string& entry_path, 112 const DictionaryValueCallbackWithoutStatus& callback, 113 const ErrorCallback& error_callback) { 114 base::DictionaryValue* entries = NULL; 115 profile_entries_.GetDictionaryWithoutPathExpansion(profile_path.value(), 116 &entries); 117 ASSERT_TRUE(entries); 118 119 base::DictionaryValue* entry = NULL; 120 entries->GetDictionaryWithoutPathExpansion(entry_path, &entry); 121 ASSERT_TRUE(entry); 122 callback.Run(*entry); 123 } 124 125 protected: 126 base::DictionaryValue profile_entries_; 127 std::map<std::string, std::string> profile_to_user_; 128}; 129 130class TestNetworkProfileHandler : public NetworkProfileHandler { 131 public: 132 TestNetworkProfileHandler() { 133 Init(NULL /* No NetworkStateHandler */); 134 } 135 virtual ~TestNetworkProfileHandler() {} 136 137 void AddProfileForTest(const NetworkProfile& profile) { 138 AddProfile(profile); 139 } 140 141 private: 142 DISALLOW_COPY_AND_ASSIGN(TestNetworkProfileHandler); 143}; 144 145} // namespace 146 147class ManagedNetworkConfigurationHandlerTest : public testing::Test { 148 public: 149 ManagedNetworkConfigurationHandlerTest() { 150 } 151 152 virtual ~ManagedNetworkConfigurationHandlerTest() { 153 } 154 155 virtual void SetUp() OVERRIDE { 156 MockDBusThreadManager* dbus_thread_manager = new MockDBusThreadManager; 157 EXPECT_CALL(*dbus_thread_manager, GetSystemBus()) 158 .WillRepeatedly(Return(static_cast<dbus::Bus*>(NULL))); 159 DBusThreadManager::InitializeForTesting(dbus_thread_manager); 160 161 SetNetworkConfigurationHandlerExpectations(); 162 163 EXPECT_CALL(*dbus_thread_manager, GetShillManagerClient()) 164 .WillRepeatedly(Return(&mock_manager_client_)); 165 EXPECT_CALL(*dbus_thread_manager, GetShillServiceClient()) 166 .WillRepeatedly(Return(&mock_service_client_)); 167 EXPECT_CALL(*dbus_thread_manager, GetShillProfileClient()) 168 .WillRepeatedly(Return(&mock_profile_client_)); 169 170 ON_CALL(mock_profile_client_, GetProperties(_,_,_)) 171 .WillByDefault(Invoke(&profiles_stub_, 172 &ShillProfileTestClient::GetProperties)); 173 174 ON_CALL(mock_profile_client_, GetEntry(_,_,_,_)) 175 .WillByDefault(Invoke(&profiles_stub_, 176 &ShillProfileTestClient::GetEntry)); 177 178 network_profile_handler_.reset(new TestNetworkProfileHandler()); 179 network_configuration_handler_.reset( 180 NetworkConfigurationHandler::InitializeForTest( 181 NULL /* no NetworkStateHandler */)); 182 managed_network_configuration_handler_.reset( 183 new ManagedNetworkConfigurationHandlerImpl()); 184 managed_network_configuration_handler_->Init( 185 NULL /* no NetworkStateHandler */, 186 network_profile_handler_.get(), 187 network_configuration_handler_.get()); 188 189 message_loop_.RunUntilIdle(); 190 } 191 192 virtual void TearDown() OVERRIDE { 193 managed_network_configuration_handler_.reset(); 194 network_configuration_handler_.reset(); 195 network_profile_handler_.reset(); 196 DBusThreadManager::Shutdown(); 197 } 198 199 void VerifyAndClearExpectations() { 200 Mock::VerifyAndClearExpectations(&mock_manager_client_); 201 Mock::VerifyAndClearExpectations(&mock_service_client_); 202 Mock::VerifyAndClearExpectations(&mock_profile_client_); 203 SetNetworkConfigurationHandlerExpectations(); 204 } 205 206 void InitializeStandardProfiles() { 207 profiles_stub_.AddProfile(kUser1ProfilePath, kUser1); 208 network_profile_handler_-> 209 AddProfileForTest(NetworkProfile(kUser1ProfilePath, kUser1)); 210 network_profile_handler_-> 211 AddProfileForTest(NetworkProfile(kSharedProfilePath, std::string())); 212 } 213 214 void SetUpEntry(const std::string& path_to_shill_json, 215 const std::string& profile_path, 216 const std::string& entry_path) { 217 scoped_ptr<base::DictionaryValue> entry = 218 test_utils::ReadTestDictionary(path_to_shill_json); 219 profiles_stub_.AddEntry(profile_path, entry_path, *entry); 220 } 221 222 void SetPolicy(onc::ONCSource onc_source, 223 const std::string& userhash, 224 const std::string& path_to_onc) { 225 scoped_ptr<base::DictionaryValue> policy; 226 if (path_to_onc.empty()) 227 policy = onc::ReadDictionaryFromJson(onc::kEmptyUnencryptedConfiguration); 228 else 229 policy = test_utils::ReadTestDictionary(path_to_onc); 230 231 base::ListValue* network_configs = NULL; 232 policy->GetListWithoutPathExpansion( 233 onc::toplevel_config::kNetworkConfigurations, &network_configs); 234 235 managed_handler()->SetPolicy( 236 onc::ONC_SOURCE_USER_POLICY, userhash, *network_configs); 237 } 238 239 void SetNetworkConfigurationHandlerExpectations() { 240 // These calls occur in NetworkConfigurationHandler. 241 EXPECT_CALL(mock_manager_client_, GetProperties(_)).Times(AnyNumber()); 242 EXPECT_CALL(mock_manager_client_, 243 AddPropertyChangedObserver(_)).Times(AnyNumber()); 244 EXPECT_CALL(mock_manager_client_, 245 RemovePropertyChangedObserver(_)).Times(AnyNumber()); 246 } 247 248 ManagedNetworkConfigurationHandler* managed_handler() { 249 return managed_network_configuration_handler_.get(); 250 } 251 252 protected: 253 StrictMock<MockShillManagerClient> mock_manager_client_; 254 StrictMock<MockShillServiceClient> mock_service_client_; 255 StrictMock<MockShillProfileClient> mock_profile_client_; 256 ShillProfileTestClient profiles_stub_; 257 scoped_ptr<TestNetworkProfileHandler> network_profile_handler_; 258 scoped_ptr<NetworkConfigurationHandler> network_configuration_handler_; 259 scoped_ptr<ManagedNetworkConfigurationHandlerImpl> 260 managed_network_configuration_handler_; 261 base::MessageLoop message_loop_; 262 263 private: 264 DISALLOW_COPY_AND_ASSIGN(ManagedNetworkConfigurationHandlerTest); 265}; 266 267TEST_F(ManagedNetworkConfigurationHandlerTest, ProfileInitialization) { 268 InitializeStandardProfiles(); 269 message_loop_.RunUntilIdle(); 270} 271 272TEST_F(ManagedNetworkConfigurationHandlerTest, RemoveIrrelevantFields) { 273 InitializeStandardProfiles(); 274 scoped_ptr<base::DictionaryValue> expected_shill_properties = 275 test_utils::ReadTestDictionary( 276 "policy/shill_policy_on_unconfigured_wifi1.json"); 277 278 EXPECT_CALL(mock_profile_client_, 279 GetProperties(dbus::ObjectPath(kUser1ProfilePath), _, _)); 280 281 EXPECT_CALL(mock_manager_client_, 282 ConfigureServiceForProfile( 283 dbus::ObjectPath(kUser1ProfilePath), 284 IsEqualTo(expected_shill_properties.get()), 285 _, _)); 286 287 SetPolicy(onc::ONC_SOURCE_USER_POLICY, 288 kUser1, 289 "policy/policy_wifi1_with_redundant_fields.onc"); 290 message_loop_.RunUntilIdle(); 291} 292 293TEST_F(ManagedNetworkConfigurationHandlerTest, SetPolicyManageUnconfigured) { 294 InitializeStandardProfiles(); 295 scoped_ptr<base::DictionaryValue> expected_shill_properties = 296 test_utils::ReadTestDictionary( 297 "policy/shill_policy_on_unconfigured_wifi1.json"); 298 299 EXPECT_CALL(mock_profile_client_, 300 GetProperties(dbus::ObjectPath(kUser1ProfilePath), _, _)); 301 302 EXPECT_CALL(mock_manager_client_, 303 ConfigureServiceForProfile( 304 dbus::ObjectPath(kUser1ProfilePath), 305 IsEqualTo(expected_shill_properties.get()), 306 _, _)); 307 308 SetPolicy(onc::ONC_SOURCE_USER_POLICY, kUser1, "policy/policy_wifi1.onc"); 309 message_loop_.RunUntilIdle(); 310} 311 312TEST_F(ManagedNetworkConfigurationHandlerTest, SetPolicyIgnoreUnmodified) { 313 InitializeStandardProfiles(); 314 EXPECT_CALL(mock_profile_client_, GetProperties(_, _, _)); 315 316 EXPECT_CALL(mock_manager_client_, ConfigureServiceForProfile(_, _, _, _)); 317 318 SetPolicy(onc::ONC_SOURCE_USER_POLICY, kUser1, "policy/policy_wifi1.onc"); 319 message_loop_.RunUntilIdle(); 320 VerifyAndClearExpectations(); 321 322 SetUpEntry("policy/shill_policy_on_unmanaged_user_wifi1.json", 323 kUser1ProfilePath, 324 "some_entry_path"); 325 326 EXPECT_CALL(mock_profile_client_, GetProperties(_, _, _)); 327 328 EXPECT_CALL(mock_profile_client_, 329 GetEntry(dbus::ObjectPath(kUser1ProfilePath), 330 "some_entry_path", 331 _, _)); 332 333 SetPolicy(onc::ONC_SOURCE_USER_POLICY, kUser1, "policy/policy_wifi1.onc"); 334 message_loop_.RunUntilIdle(); 335} 336 337TEST_F(ManagedNetworkConfigurationHandlerTest, SetPolicyManageUnmanaged) { 338 InitializeStandardProfiles(); 339 SetUpEntry("policy/shill_unmanaged_user_wifi1.json", 340 kUser1ProfilePath, 341 "old_entry_path"); 342 343 scoped_ptr<base::DictionaryValue> expected_shill_properties = 344 test_utils::ReadTestDictionary( 345 "policy/shill_policy_on_unmanaged_user_wifi1.json"); 346 347 EXPECT_CALL(mock_profile_client_, 348 GetProperties(dbus::ObjectPath(kUser1ProfilePath), _, _)); 349 350 EXPECT_CALL( 351 mock_profile_client_, 352 GetEntry(dbus::ObjectPath(kUser1ProfilePath), "old_entry_path", _, _)); 353 354 EXPECT_CALL( 355 mock_profile_client_, 356 DeleteEntry(dbus::ObjectPath(kUser1ProfilePath), "old_entry_path", _, _)); 357 358 EXPECT_CALL(mock_manager_client_, 359 ConfigureServiceForProfile( 360 dbus::ObjectPath(kUser1ProfilePath), 361 IsEqualTo(expected_shill_properties.get()), 362 _, _)); 363 364 SetPolicy(onc::ONC_SOURCE_USER_POLICY, kUser1, "policy/policy_wifi1.onc"); 365 message_loop_.RunUntilIdle(); 366} 367 368// Old ChromeOS versions may not have used the UIData property 369TEST_F(ManagedNetworkConfigurationHandlerTest, 370 SetPolicyManageUnmanagedWithoutUIData) { 371 InitializeStandardProfiles(); 372 SetUpEntry("policy/shill_unmanaged_user_wifi1.json", 373 kUser1ProfilePath, 374 "old_entry_path"); 375 376 scoped_ptr<base::DictionaryValue> expected_shill_properties = 377 test_utils::ReadTestDictionary( 378 "policy/shill_policy_on_unmanaged_user_wifi1.json"); 379 380 EXPECT_CALL(mock_profile_client_, 381 GetProperties(dbus::ObjectPath(kUser1ProfilePath), _, _)); 382 383 EXPECT_CALL( 384 mock_profile_client_, 385 GetEntry(dbus::ObjectPath(kUser1ProfilePath), "old_entry_path", _, _)); 386 387 EXPECT_CALL( 388 mock_profile_client_, 389 DeleteEntry(dbus::ObjectPath(kUser1ProfilePath), "old_entry_path", _, _)); 390 391 EXPECT_CALL(mock_manager_client_, 392 ConfigureServiceForProfile( 393 dbus::ObjectPath(kUser1ProfilePath), 394 IsEqualTo(expected_shill_properties.get()), 395 _, _)); 396 397 SetPolicy(onc::ONC_SOURCE_USER_POLICY, kUser1, "policy/policy_wifi1.onc"); 398 message_loop_.RunUntilIdle(); 399} 400 401TEST_F(ManagedNetworkConfigurationHandlerTest, SetPolicyUpdateManagedNewGUID) { 402 InitializeStandardProfiles(); 403 SetUpEntry("policy/shill_managed_wifi1.json", 404 kUser1ProfilePath, 405 "old_entry_path"); 406 407 scoped_ptr<base::DictionaryValue> expected_shill_properties = 408 test_utils::ReadTestDictionary( 409 "policy/shill_policy_on_unmanaged_user_wifi1.json"); 410 411 // The passphrase isn't sent again, because it's configured by the user and 412 // Shill doesn't sent it on GetProperties calls. 413 expected_shill_properties->RemoveWithoutPathExpansion( 414 shill::kPassphraseProperty, NULL); 415 416 EXPECT_CALL(mock_profile_client_, 417 GetProperties(dbus::ObjectPath(kUser1ProfilePath), _, _)); 418 419 EXPECT_CALL( 420 mock_profile_client_, 421 GetEntry(dbus::ObjectPath(kUser1ProfilePath), "old_entry_path", _, _)); 422 423 EXPECT_CALL( 424 mock_profile_client_, 425 DeleteEntry(dbus::ObjectPath(kUser1ProfilePath), "old_entry_path", _, _)); 426 427 EXPECT_CALL(mock_manager_client_, 428 ConfigureServiceForProfile( 429 dbus::ObjectPath(kUser1ProfilePath), 430 IsEqualTo(expected_shill_properties.get()), 431 _, _)); 432 433 SetPolicy(onc::ONC_SOURCE_USER_POLICY, kUser1, "policy/policy_wifi1.onc"); 434 message_loop_.RunUntilIdle(); 435} 436 437TEST_F(ManagedNetworkConfigurationHandlerTest, SetPolicyReapplyToManaged) { 438 InitializeStandardProfiles(); 439 SetUpEntry("policy/shill_policy_on_unmanaged_user_wifi1.json", 440 kUser1ProfilePath, 441 "old_entry_path"); 442 443 scoped_ptr<base::DictionaryValue> expected_shill_properties = 444 test_utils::ReadTestDictionary( 445 "policy/shill_policy_on_unmanaged_user_wifi1.json"); 446 447 // The passphrase isn't sent again, because it's configured by the user and 448 // Shill doesn't sent it on GetProperties calls. 449 expected_shill_properties->RemoveWithoutPathExpansion( 450 shill::kPassphraseProperty, NULL); 451 452 EXPECT_CALL(mock_profile_client_, 453 GetProperties(dbus::ObjectPath(kUser1ProfilePath), _, _)); 454 455 EXPECT_CALL( 456 mock_profile_client_, 457 GetEntry(dbus::ObjectPath(kUser1ProfilePath), "old_entry_path", _, _)); 458 459 EXPECT_CALL(mock_manager_client_, 460 ConfigureServiceForProfile( 461 dbus::ObjectPath(kUser1ProfilePath), 462 IsEqualTo(expected_shill_properties.get()), 463 _, _)); 464 465 SetPolicy(onc::ONC_SOURCE_USER_POLICY, kUser1, "policy/policy_wifi1.onc"); 466 message_loop_.RunUntilIdle(); 467 VerifyAndClearExpectations(); 468 469 // If we apply the policy again, without change, then the Shill profile will 470 // not be modified. 471 EXPECT_CALL(mock_profile_client_, 472 GetProperties(dbus::ObjectPath(kUser1ProfilePath), _, _)); 473 474 EXPECT_CALL( 475 mock_profile_client_, 476 GetEntry(dbus::ObjectPath(kUser1ProfilePath), "old_entry_path", _, _)); 477 478 SetPolicy(onc::ONC_SOURCE_USER_POLICY, kUser1, "policy/policy_wifi1.onc"); 479 message_loop_.RunUntilIdle(); 480} 481 482TEST_F(ManagedNetworkConfigurationHandlerTest, SetPolicyUnmanageManaged) { 483 InitializeStandardProfiles(); 484 SetUpEntry("policy/shill_policy_on_unmanaged_user_wifi1.json", 485 kUser1ProfilePath, 486 "old_entry_path"); 487 488 EXPECT_CALL(mock_profile_client_, 489 GetProperties(dbus::ObjectPath(kUser1ProfilePath), _, _)); 490 491 EXPECT_CALL(mock_profile_client_, 492 GetEntry(dbus::ObjectPath(kUser1ProfilePath), 493 "old_entry_path", 494 _, _)); 495 496 EXPECT_CALL(mock_profile_client_, 497 DeleteEntry(dbus::ObjectPath(kUser1ProfilePath), 498 "old_entry_path", 499 _, _)); 500 501 SetPolicy(onc::ONC_SOURCE_USER_POLICY, kUser1, ""); 502 message_loop_.RunUntilIdle(); 503} 504 505TEST_F(ManagedNetworkConfigurationHandlerTest, SetEmptyPolicyIgnoreUnmanaged) { 506 InitializeStandardProfiles(); 507 SetUpEntry("policy/shill_unmanaged_user_wifi1.json", 508 kUser1ProfilePath, 509 "old_entry_path"); 510 511 EXPECT_CALL(mock_profile_client_, 512 GetProperties(dbus::ObjectPath(kUser1ProfilePath), _, _)); 513 514 EXPECT_CALL(mock_profile_client_, 515 GetEntry(dbus::ObjectPath(kUser1ProfilePath), 516 "old_entry_path", 517 _, _)); 518 519 SetPolicy(onc::ONC_SOURCE_USER_POLICY, kUser1, ""); 520 message_loop_.RunUntilIdle(); 521} 522 523TEST_F(ManagedNetworkConfigurationHandlerTest, SetPolicyIgnoreUnmanaged) { 524 InitializeStandardProfiles(); 525 SetUpEntry("policy/shill_unmanaged_user_wifi2.json", 526 kUser1ProfilePath, 527 "wifi2_entry_path"); 528 529 EXPECT_CALL(mock_profile_client_, 530 GetProperties(dbus::ObjectPath(kUser1ProfilePath), _, _)); 531 532 EXPECT_CALL( 533 mock_profile_client_, 534 GetEntry(dbus::ObjectPath(kUser1ProfilePath), "wifi2_entry_path", _, _)); 535 536 scoped_ptr<base::DictionaryValue> expected_shill_properties = 537 test_utils::ReadTestDictionary( 538 "policy/shill_policy_on_unconfigured_wifi1.json"); 539 540 EXPECT_CALL(mock_manager_client_, 541 ConfigureServiceForProfile( 542 dbus::ObjectPath(kUser1ProfilePath), 543 IsEqualTo(expected_shill_properties.get()), 544 _, _)); 545 546 SetPolicy(onc::ONC_SOURCE_USER_POLICY, kUser1, "policy/policy_wifi1.onc"); 547 message_loop_.RunUntilIdle(); 548} 549 550TEST_F(ManagedNetworkConfigurationHandlerTest, LateProfileLoading) { 551 SetPolicy(onc::ONC_SOURCE_USER_POLICY, kUser1, "policy/policy_wifi1.onc"); 552 553 message_loop_.RunUntilIdle(); 554 VerifyAndClearExpectations(); 555 556 scoped_ptr<base::DictionaryValue> expected_shill_properties = 557 test_utils::ReadTestDictionary( 558 "policy/shill_policy_on_unconfigured_wifi1.json"); 559 560 EXPECT_CALL(mock_profile_client_, 561 GetProperties(dbus::ObjectPath(kUser1ProfilePath), _, _)); 562 563 EXPECT_CALL(mock_manager_client_, 564 ConfigureServiceForProfile( 565 dbus::ObjectPath(kUser1ProfilePath), 566 IsEqualTo(expected_shill_properties.get()), 567 _, _)); 568 569 InitializeStandardProfiles(); 570 message_loop_.RunUntilIdle(); 571} 572 573} // namespace chromeos 574