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 "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" 6 7 #include "base/at_exit.h" 8 #include "base/command_line.h" 9 #include "base/message_loop/message_loop.h" 10 #include "base/path_service.h" 11 #include "base/strings/stringprintf.h" 12 #include "base/values.h" 13 #include "chrome/browser/browser_process.h" 14 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h" 15 #include "chrome/browser/chromeos/policy/device_local_account.h" 16 #include "chrome/browser/chromeos/settings/cros_settings.h" 17 #include "chrome/browser/chromeos/settings/cros_settings_names.h" 18 #include "chrome/browser/policy/browser_policy_connector.h" 19 #include "chrome/browser/prefs/scoped_user_pref_update.h" 20 #include "chrome/common/chrome_paths.h" 21 #include "chrome/common/chrome_switches.h" 22 #include "chrome/test/base/in_process_browser_test.h" 23 #include "content/public/test/test_utils.h" 24 #include "net/base/host_port_pair.h" 25 #include "net/dns/mock_host_resolver.h" 26 27 namespace chromeos { 28 29 namespace { 30 31 const char kWebstoreDomain[] = "cws.com"; 32 33 // Helper KioskAppManager::GetConsumerKioskModeStatusCallback implementation. 34 void ConsumerKioskModeStatusCheck( 35 KioskAppManager::ConsumerKioskModeStatus* out_status, 36 const base::Closure& runner_quit_task, 37 KioskAppManager::ConsumerKioskModeStatus in_status) { 38 LOG(INFO) << "ConsumerKioskModeStatus = " << in_status; 39 *out_status = in_status; 40 runner_quit_task.Run(); 41 } 42 43 // Helper KioskAppManager::EnableKioskModeCallback implementation. 44 void ConsumerKioskModeLockCheck( 45 bool* out_locked, 46 const base::Closure& runner_quit_task, 47 bool in_locked) { 48 LOG(INFO) << "kioks locked = " << in_locked; 49 *out_locked = in_locked; 50 runner_quit_task.Run(); 51 } 52 53 // Helper EnterpriseInstallAttributes::LockResultCallback implementation. 54 void OnEnterpriseDeviceLock( 55 policy::EnterpriseInstallAttributes::LockResult* out_locked, 56 const base::Closure& runner_quit_task, 57 policy::EnterpriseInstallAttributes::LockResult in_locked) { 58 LOG(INFO) << "Enterprise lock = " << in_locked; 59 *out_locked = in_locked; 60 runner_quit_task.Run(); 61 } 62 63 class TestKioskAppManagerObserver : public KioskAppManagerObserver { 64 public: 65 explicit TestKioskAppManagerObserver(KioskAppManager* manager) 66 : manager_(manager), 67 data_changed_count_(0), 68 load_failure_count_(0) { 69 manager_->AddObserver(this); 70 } 71 virtual ~TestKioskAppManagerObserver() { 72 manager_->RemoveObserver(this); 73 } 74 75 void Reset() { 76 data_changed_count_ = 0; 77 load_failure_count_ = 0; 78 } 79 80 int data_changed_count() const { return data_changed_count_; } 81 int load_failure_count() const { return load_failure_count_; } 82 83 private: 84 // KioskAppManagerObserver overrides: 85 virtual void OnKioskAppDataChanged(const std::string& app_id) OVERRIDE { 86 ++data_changed_count_; 87 } 88 virtual void OnKioskAppDataLoadFailure(const std::string& app_id) OVERRIDE { 89 ++load_failure_count_; 90 } 91 92 KioskAppManager* manager_; 93 int data_changed_count_; 94 int load_failure_count_; 95 96 DISALLOW_COPY_AND_ASSIGN(TestKioskAppManagerObserver); 97 }; 98 99 class AppDataLoadWaiter : public KioskAppManagerObserver { 100 public: 101 explicit AppDataLoadWaiter(KioskAppManager* manager) 102 : manager_(manager), 103 loaded_(false) { 104 manager_->AddObserver(this); 105 } 106 virtual ~AppDataLoadWaiter() { 107 manager_->RemoveObserver(this); 108 } 109 110 void Wait() { 111 base::MessageLoop::current()->Run(); 112 } 113 114 bool loaded() const { return loaded_; } 115 116 private: 117 // KioskAppManagerObserver overrides: 118 virtual void OnKioskAppDataChanged(const std::string& app_id) OVERRIDE { 119 loaded_ = true; 120 base::MessageLoop::current()->Quit(); 121 } 122 virtual void OnKioskAppDataLoadFailure(const std::string& app_id) OVERRIDE { 123 loaded_ = false; 124 base::MessageLoop::current()->Quit(); 125 } 126 127 KioskAppManager* manager_; 128 bool loaded_; 129 130 DISALLOW_COPY_AND_ASSIGN(AppDataLoadWaiter); 131 }; 132 133 } // namespace 134 135 class KioskAppManagerTest : public InProcessBrowserTest { 136 public: 137 KioskAppManagerTest() {} 138 virtual ~KioskAppManagerTest() {} 139 140 // InProcessBrowserTest overrides: 141 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 142 // We start the test server now instead of in 143 // SetUpInProcessBrowserTestFixture so that we can get its port number. 144 ASSERT_TRUE(test_server()->Start()); 145 146 net::HostPortPair host_port = test_server()->host_port_pair(); 147 test_gallery_url_ = base::StringPrintf( 148 "http://%s:%d/files/chromeos/app_mode/webstore", 149 kWebstoreDomain, host_port.port()); 150 151 command_line->AppendSwitchASCII( 152 switches::kAppsGalleryURL, test_gallery_url_); 153 } 154 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { 155 host_resolver()->AddRule(kWebstoreDomain, "127.0.0.1"); 156 } 157 158 std::string GetAppIds() const { 159 KioskAppManager::Apps apps; 160 manager()->GetApps(&apps); 161 162 std::string str; 163 for (size_t i = 0; i < apps.size(); ++i) { 164 if (i > 0) 165 str += ','; 166 str += apps[i].app_id; 167 } 168 169 return str; 170 } 171 172 KioskAppManager* manager() const { return KioskAppManager::Get(); } 173 174 // Locks device for enterprise. 175 policy::EnterpriseInstallAttributes::LockResult LockDeviceForEnterprise() { 176 scoped_ptr<policy::EnterpriseInstallAttributes::LockResult> lock_result( 177 new policy::EnterpriseInstallAttributes::LockResult( 178 policy::EnterpriseInstallAttributes::LOCK_NOT_READY)); 179 scoped_refptr<content::MessageLoopRunner> runner = 180 new content::MessageLoopRunner; 181 g_browser_process->browser_policy_connector()->GetInstallAttributes()-> 182 LockDevice( 183 "user (at) domain.com", 184 policy::DEVICE_MODE_ENTERPRISE, 185 "device-id", 186 base::Bind(&OnEnterpriseDeviceLock, 187 lock_result.get(), 188 runner->QuitClosure())); 189 runner->Run(); 190 return *lock_result.get(); 191 } 192 193 private: 194 std::string test_gallery_url_; 195 base::ShadowingAtExitManager exit_manager_; 196 197 DISALLOW_COPY_AND_ASSIGN(KioskAppManagerTest); 198 }; 199 200 IN_PROC_BROWSER_TEST_F(KioskAppManagerTest, Basic) { 201 // Add a couple of apps. Use "fake_app_x" that do not have data on the test 202 // server to avoid pending data loads that could be lingering on tear down and 203 // cause DCHECK failure in utility_process_host_impl.cc. 204 manager()->AddApp("fake_app_1"); 205 manager()->AddApp("fake_app_2"); 206 EXPECT_EQ("fake_app_1,fake_app_2", GetAppIds()); 207 208 // Set an auto launch app. 209 manager()->SetAutoLaunchApp("fake_app_1"); 210 EXPECT_EQ("fake_app_1", manager()->GetAutoLaunchApp()); 211 212 // Clear the auto launch app. 213 manager()->SetAutoLaunchApp(""); 214 EXPECT_EQ("", manager()->GetAutoLaunchApp()); 215 EXPECT_FALSE(manager()->IsAutoLaunchEnabled()); 216 217 // Set another auto launch app. 218 manager()->SetAutoLaunchApp("fake_app_2"); 219 EXPECT_EQ("fake_app_2", manager()->GetAutoLaunchApp()); 220 221 // Check auto launch permissions. 222 EXPECT_FALSE(manager()->IsAutoLaunchEnabled()); 223 manager()->SetEnableAutoLaunch(true); 224 EXPECT_TRUE(manager()->IsAutoLaunchEnabled()); 225 226 // Remove the auto launch app. 227 manager()->RemoveApp("fake_app_2"); 228 EXPECT_EQ("fake_app_1", GetAppIds()); 229 EXPECT_EQ("", manager()->GetAutoLaunchApp()); 230 231 // Add the just removed auto launch app again and it should no longer be 232 // the auto launch app. 233 manager()->AddApp("fake_app_2"); 234 EXPECT_EQ("", manager()->GetAutoLaunchApp()); 235 manager()->RemoveApp("fake_app_2"); 236 EXPECT_EQ("fake_app_1", GetAppIds()); 237 238 // Set a none exist app as auto launch. 239 manager()->SetAutoLaunchApp("none_exist_app"); 240 EXPECT_EQ("", manager()->GetAutoLaunchApp()); 241 EXPECT_FALSE(manager()->IsAutoLaunchEnabled()); 242 243 // Add an existing app again. 244 manager()->AddApp("fake_app_1"); 245 EXPECT_EQ("fake_app_1", GetAppIds()); 246 } 247 248 IN_PROC_BROWSER_TEST_F(KioskAppManagerTest, LoadCached) { 249 base::FilePath test_dir; 250 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir)); 251 base::FilePath data_dir = test_dir.AppendASCII("chromeos/app_mode/"); 252 253 scoped_ptr<base::DictionaryValue> apps_dict(new base::DictionaryValue); 254 apps_dict->SetString("app_1.name", "App1 Name"); 255 std::string icon_path = 256 base::StringPrintf("%s/red16x16.png", data_dir.value().c_str()); 257 apps_dict->SetString("app_1.icon", icon_path); 258 259 PrefService* local_state = g_browser_process->local_state(); 260 DictionaryPrefUpdate dict_update(local_state, 261 KioskAppManager::kKioskDictionaryName); 262 dict_update->Set(KioskAppManager::kKeyApps, apps_dict.release()); 263 264 // Make the app appear in device settings. 265 base::ListValue device_local_accounts; 266 scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue); 267 entry->SetStringWithoutPathExpansion( 268 kAccountsPrefDeviceLocalAccountsKeyId, 269 "app_1_id"); 270 entry->SetIntegerWithoutPathExpansion( 271 kAccountsPrefDeviceLocalAccountsKeyType, 272 policy::DeviceLocalAccount::TYPE_KIOSK_APP); 273 entry->SetStringWithoutPathExpansion( 274 kAccountsPrefDeviceLocalAccountsKeyKioskAppId, 275 "app_1"); 276 device_local_accounts.Append(entry.release()); 277 CrosSettings::Get()->Set(kAccountsPrefDeviceLocalAccounts, 278 device_local_accounts); 279 280 AppDataLoadWaiter waiter(manager()); 281 waiter.Wait(); 282 EXPECT_TRUE(waiter.loaded()); 283 284 KioskAppManager::Apps apps; 285 manager()->GetApps(&apps); 286 EXPECT_EQ(1u, apps.size()); 287 EXPECT_EQ("app_1", apps[0].app_id); 288 EXPECT_EQ("App1 Name", apps[0].name); 289 EXPECT_EQ(gfx::Size(16, 16), apps[0].icon.size()); 290 } 291 292 IN_PROC_BROWSER_TEST_F(KioskAppManagerTest, BadApp) { 293 manager()->AddApp("unknown_app"); 294 295 TestKioskAppManagerObserver observer(manager()); 296 297 AppDataLoadWaiter waiter(manager()); 298 waiter.Wait(); 299 EXPECT_FALSE(waiter.loaded()); 300 301 EXPECT_EQ("", GetAppIds()); 302 EXPECT_EQ(1, observer.load_failure_count()); 303 } 304 305 IN_PROC_BROWSER_TEST_F(KioskAppManagerTest, GoodApp) { 306 // Webstore data json is in 307 // chrome/test/data/chromeos/app_mode/webstore/inlineinstall/detail/app_1 308 manager()->AddApp("app_1"); 309 310 AppDataLoadWaiter waiter(manager()); 311 waiter.Wait(); 312 EXPECT_TRUE(waiter.loaded()); 313 314 // Check data is correct. 315 KioskAppManager::Apps apps; 316 manager()->GetApps(&apps); 317 EXPECT_EQ(1u, apps.size()); 318 EXPECT_EQ("app_1", apps[0].app_id); 319 EXPECT_EQ("Name of App 1", apps[0].name); 320 EXPECT_EQ(gfx::Size(16, 16), apps[0].icon.size()); 321 322 // Check data is cached in local state. 323 PrefService* local_state = g_browser_process->local_state(); 324 const base::DictionaryValue* dict = 325 local_state->GetDictionary(KioskAppManager::kKioskDictionaryName); 326 327 std::string name; 328 EXPECT_TRUE(dict->GetString("apps.app_1.name", &name)); 329 EXPECT_EQ(apps[0].name, name); 330 331 std::string icon_path_string; 332 EXPECT_TRUE(dict->GetString("apps.app_1.icon", &icon_path_string)); 333 334 base::FilePath expected_icon_path; 335 ASSERT_TRUE(PathService::Get(chrome::DIR_USER_DATA, &expected_icon_path)); 336 expected_icon_path = expected_icon_path. 337 AppendASCII(KioskAppManager::kIconCacheDir). 338 AppendASCII(apps[0].app_id).AddExtension(".png"); 339 EXPECT_EQ(expected_icon_path.value(), icon_path_string); 340 } 341 342 IN_PROC_BROWSER_TEST_F(KioskAppManagerTest, EnableConsumerKiosk) { 343 scoped_ptr<KioskAppManager::ConsumerKioskModeStatus> status( 344 new KioskAppManager::ConsumerKioskModeStatus( 345 KioskAppManager::CONSUMER_KIOSK_MODE_DISABLED)); 346 scoped_ptr<bool> locked(new bool(false)); 347 348 scoped_refptr<content::MessageLoopRunner> runner = 349 new content::MessageLoopRunner; 350 manager()->GetConsumerKioskModeStatus( 351 base::Bind(&ConsumerKioskModeStatusCheck, 352 status.get(), 353 runner->QuitClosure())); 354 runner->Run(); 355 EXPECT_EQ(*status.get(), KioskAppManager::CONSUMER_KIOSK_MODE_CONFIGURABLE); 356 357 scoped_refptr<content::MessageLoopRunner> runner2 = 358 new content::MessageLoopRunner; 359 manager()->EnableConsumerModeKiosk( 360 base::Bind(&ConsumerKioskModeLockCheck, 361 locked.get(), 362 runner2->QuitClosure())); 363 runner2->Run(); 364 EXPECT_TRUE(*locked.get()); 365 366 scoped_refptr<content::MessageLoopRunner> runner3 = 367 new content::MessageLoopRunner; 368 manager()->GetConsumerKioskModeStatus( 369 base::Bind(&ConsumerKioskModeStatusCheck, 370 status.get(), 371 runner3->QuitClosure())); 372 runner3->Run(); 373 EXPECT_EQ(*status.get(), KioskAppManager::CONSUMER_KIOSK_MODE_ENABLED); 374 } 375 376 IN_PROC_BROWSER_TEST_F(KioskAppManagerTest, 377 PreventEnableConsumerKioskForEnterprise) { 378 // First, lock the device as enterprise. 379 EXPECT_EQ(LockDeviceForEnterprise(), 380 policy::EnterpriseInstallAttributes::LOCK_SUCCESS); 381 382 scoped_ptr<KioskAppManager::ConsumerKioskModeStatus> status( 383 new KioskAppManager::ConsumerKioskModeStatus( 384 KioskAppManager::CONSUMER_KIOSK_MODE_DISABLED)); 385 scoped_ptr<bool> locked(new bool(true)); 386 387 scoped_refptr<content::MessageLoopRunner> runner = 388 new content::MessageLoopRunner; 389 manager()->GetConsumerKioskModeStatus( 390 base::Bind(&ConsumerKioskModeStatusCheck, 391 status.get(), 392 runner->QuitClosure())); 393 runner->Run(); 394 EXPECT_EQ(*status.get(), KioskAppManager::CONSUMER_KIOSK_MODE_DISABLED); 395 396 scoped_refptr<content::MessageLoopRunner> runner2 = 397 new content::MessageLoopRunner; 398 manager()->EnableConsumerModeKiosk( 399 base::Bind(&ConsumerKioskModeLockCheck, 400 locked.get(), 401 runner2->QuitClosure())); 402 runner2->Run(); 403 EXPECT_FALSE(*locked.get()); 404 405 scoped_refptr<content::MessageLoopRunner> runner3 = 406 new content::MessageLoopRunner; 407 manager()->GetConsumerKioskModeStatus( 408 base::Bind(&ConsumerKioskModeStatusCheck, 409 status.get(), 410 runner3->QuitClosure())); 411 runner3->Run(); 412 EXPECT_EQ(*status.get(), KioskAppManager::CONSUMER_KIOSK_MODE_DISABLED); 413 } 414 415 } // namespace chromeos 416