Home | History | Annotate | Download | only in app_mode
      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