kiosk_app_manager.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <map>
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <set>
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/logging.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/path_service.h"
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/prefs/pref_registry_simple.h"
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/prefs/pref_service.h"
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/prefs/scoped_user_pref_update.h"
16d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "base/stl_util.h"
17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/sys_info.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/browser_process.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/chromeos/app_mode/kiosk_app_data.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/chromeos/login/user_manager.h"
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/chromeos/policy/device_local_account.h"
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/chromeos/settings/cros_settings.h"
25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/chromeos/settings/owner_key_util.h"
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/common/chrome_paths.h"
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chromeos/cryptohome/async_method_caller.h"
28d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "chromeos/settings/cros_settings_names.h"
29d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "content/public/browser/browser_thread.h"
30d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace chromeos {
32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace {
34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Domain that is used for kiosk-app account IDs.
36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const char kKioskAppAccountDomain[] = "kiosk-apps";
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)std::string GenerateKioskAppAccountId(const std::string& app_id) {
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return app_id + '@' + kKioskAppAccountDomain;
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void OnRemoveAppCryptohomeComplete(const std::string& app,
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                   bool success,
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                   cryptohome::MountError return_code) {
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!success) {
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    LOG(ERROR) << "Remove cryptohome for " << app
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        << " failed, return code: " << return_code;
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Check for presence of machine owner public key file.
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void CheckOwnerFilePresence(bool *present) {
53d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  scoped_refptr<OwnerKeyUtil> util = OwnerKeyUtil::Create();
54d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  *present = util->IsPublicKeyPresent();
55d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
56d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}  // namespace
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// static
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const char KioskAppManager::kKioskDictionaryName[] = "kiosk";
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const char KioskAppManager::kKeyApps[] = "apps";
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const char KioskAppManager::kKeyAutoLoginState[] = "auto_login_state";
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const char KioskAppManager::kIconCacheDir[] = "kiosk";
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// static
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)static base::LazyInstance<KioskAppManager> instance = LAZY_INSTANCE_INITIALIZER;
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)KioskAppManager* KioskAppManager::Get() {
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return instance.Pointer();
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// static
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void KioskAppManager::Shutdown() {
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (instance == NULL)
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  instance.Pointer()->CleanUp();
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// static
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void KioskAppManager::RegisterPrefs(PrefRegistrySimple* registry) {
81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  registry->RegisterDictionaryPref(kKioskDictionaryName);
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)KioskAppManager::App::App(const KioskAppData& data)
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : app_id(data.app_id()),
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      user_id(data.user_id()),
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      name(data.name()),
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      icon(data.icon()),
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      is_loading(data.IsLoading()) {
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)KioskAppManager::App::App() : is_loading(false) {}
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)KioskAppManager::App::~App() {}
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)std::string KioskAppManager::GetAutoLaunchApp() const {
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return auto_launch_app_id_;
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void KioskAppManager::SetAutoLaunchApp(const std::string& app_id) {
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SetAutoLoginState(AUTOLOGIN_REQUESTED);
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Clean first, so the proper change callbacks are triggered even
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // if we are only changing AutoLoginState here.
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!auto_launch_app_id_.empty()) {
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    CrosSettings::Get()->SetString(kAccountsPrefDeviceLocalAccountAutoLoginId,
105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                   std::string());
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CrosSettings::Get()->SetString(
109d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      kAccountsPrefDeviceLocalAccountAutoLoginId,
110d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      app_id.empty() ? std::string() : GenerateKioskAppAccountId(app_id));
111d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  CrosSettings::Get()->SetInteger(
112d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      kAccountsPrefDeviceLocalAccountAutoLoginDelay, 0);
113d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
114d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
115d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void KioskAppManager::EnableConsumerKioskAutoLaunch(
116d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const KioskAppManager::EnableKioskAutoLaunchCallback& callback) {
117d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  policy::BrowserPolicyConnectorChromeOS* connector =
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      g_browser_process->platform_part()->browser_policy_connector_chromeos();
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  connector->GetInstallAttributes()->LockDevice(
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::string(),  // user
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      policy::DEVICE_MODE_CONSUMER_KIOSK_AUTOLAUNCH,
122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      std::string(),  // device_id
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          &KioskAppManager::OnLockDevice, base::Unretained(this), callback));
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void KioskAppManager::GetConsumerKioskAutoLaunchStatus(
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const KioskAppManager::GetConsumerKioskAutoLaunchStatusCallback& callback) {
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  policy::BrowserPolicyConnectorChromeOS* connector =
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      g_browser_process->platform_part()->browser_policy_connector_chromeos();
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  connector->GetInstallAttributes()->ReadImmutableAttributes(
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&KioskAppManager::OnReadImmutableAttributes,
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 base::Unretained(this),
134                 callback));
135}
136
137bool KioskAppManager::IsConsumerKioskDeviceWithAutoLaunch() {
138  policy::BrowserPolicyConnectorChromeOS* connector =
139      g_browser_process->platform_part()->browser_policy_connector_chromeos();
140  return connector->GetInstallAttributes() &&
141         connector->GetInstallAttributes()
142             ->IsConsumerKioskDeviceWithAutoLaunch();
143}
144
145void KioskAppManager::OnLockDevice(
146    const KioskAppManager::EnableKioskAutoLaunchCallback& callback,
147    policy::EnterpriseInstallAttributes::LockResult result) {
148  if (callback.is_null())
149    return;
150
151  callback.Run(result == policy::EnterpriseInstallAttributes::LOCK_SUCCESS);
152}
153
154void KioskAppManager::OnOwnerFileChecked(
155    const KioskAppManager::GetConsumerKioskAutoLaunchStatusCallback& callback,
156    bool* owner_present) {
157  ownership_established_ = *owner_present;
158
159  if (callback.is_null())
160    return;
161
162  // If we have owner already established on the machine, don't let
163  // consumer kiosk to be enabled.
164  if (ownership_established_)
165    callback.Run(CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED);
166  else
167    callback.Run(CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE);
168}
169
170void KioskAppManager::OnReadImmutableAttributes(
171    const KioskAppManager::GetConsumerKioskAutoLaunchStatusCallback&
172        callback) {
173  if (callback.is_null())
174    return;
175
176  ConsumerKioskAutoLaunchStatus status =
177      CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED;
178  policy::BrowserPolicyConnectorChromeOS* connector =
179      g_browser_process->platform_part()->browser_policy_connector_chromeos();
180  policy::EnterpriseInstallAttributes* attributes =
181      connector->GetInstallAttributes();
182  switch (attributes->GetMode()) {
183    case policy::DEVICE_MODE_NOT_SET: {
184      if (!base::SysInfo::IsRunningOnChromeOS()) {
185        status = CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE;
186      } else if (!ownership_established_) {
187        bool* owner_present = new bool(false);
188        content::BrowserThread::PostBlockingPoolTaskAndReply(
189            FROM_HERE,
190            base::Bind(&CheckOwnerFilePresence,
191                       owner_present),
192            base::Bind(&KioskAppManager::OnOwnerFileChecked,
193                       base::Unretained(this),
194                       callback,
195                       base::Owned(owner_present)));
196        return;
197      }
198      break;
199    }
200    case policy::DEVICE_MODE_CONSUMER_KIOSK_AUTOLAUNCH:
201      status = CONSUMER_KIOSK_AUTO_LAUNCH_ENABLED;
202      break;
203    default:
204      break;
205  }
206
207  callback.Run(status);
208}
209
210void KioskAppManager::SetEnableAutoLaunch(bool value) {
211  SetAutoLoginState(value ? AUTOLOGIN_APPROVED : AUTOLOGIN_REJECTED);
212}
213
214bool KioskAppManager::IsAutoLaunchRequested() const {
215  if (GetAutoLaunchApp().empty())
216    return false;
217
218  // Apps that were installed by the policy don't require machine owner
219  // consent through UI.
220  policy::BrowserPolicyConnectorChromeOS* connector =
221      g_browser_process->platform_part()->browser_policy_connector_chromeos();
222  if (connector->IsEnterpriseManaged())
223    return false;
224
225  return GetAutoLoginState() == AUTOLOGIN_REQUESTED;
226}
227
228bool KioskAppManager::IsAutoLaunchEnabled() const {
229  if (GetAutoLaunchApp().empty())
230    return false;
231
232  // Apps that were installed by the policy don't require machine owner
233  // consent through UI.
234  policy::BrowserPolicyConnectorChromeOS* connector =
235      g_browser_process->platform_part()->browser_policy_connector_chromeos();
236  if (connector->IsEnterpriseManaged())
237    return true;
238
239  return GetAutoLoginState() == AUTOLOGIN_APPROVED;
240}
241
242void KioskAppManager::AddApp(const std::string& app_id) {
243  std::vector<policy::DeviceLocalAccount> device_local_accounts =
244      policy::GetDeviceLocalAccounts(CrosSettings::Get());
245
246  // Don't insert the app if it's already in the list.
247  for (std::vector<policy::DeviceLocalAccount>::const_iterator
248           it = device_local_accounts.begin();
249       it != device_local_accounts.end(); ++it) {
250    if (it->type == policy::DeviceLocalAccount::TYPE_KIOSK_APP &&
251        it->kiosk_app_id == app_id) {
252      return;
253    }
254  }
255
256  // Add the new account.
257  device_local_accounts.push_back(policy::DeviceLocalAccount(
258      policy::DeviceLocalAccount::TYPE_KIOSK_APP,
259      GenerateKioskAppAccountId(app_id),
260      app_id));
261
262  policy::SetDeviceLocalAccounts(CrosSettings::Get(), device_local_accounts);
263}
264
265void KioskAppManager::RemoveApp(const std::string& app_id) {
266  // Resets auto launch app if it is the removed app.
267  if (auto_launch_app_id_ == app_id)
268    SetAutoLaunchApp(std::string());
269
270  std::vector<policy::DeviceLocalAccount> device_local_accounts =
271      policy::GetDeviceLocalAccounts(CrosSettings::Get());
272  if (device_local_accounts.empty())
273    return;
274
275  // Remove entries that match |app_id|.
276  for (std::vector<policy::DeviceLocalAccount>::iterator
277           it = device_local_accounts.begin();
278       it != device_local_accounts.end(); ++it) {
279    if (it->type == policy::DeviceLocalAccount::TYPE_KIOSK_APP &&
280        it->kiosk_app_id == app_id) {
281      device_local_accounts.erase(it);
282      break;
283    }
284  }
285
286  policy::SetDeviceLocalAccounts(CrosSettings::Get(), device_local_accounts);
287}
288
289void KioskAppManager::GetApps(Apps* apps) const {
290  apps->clear();
291  apps->reserve(apps_.size());
292  for (size_t i = 0; i < apps_.size(); ++i) {
293    const KioskAppData& app_data = *apps_[i];
294    if (app_data.status() != KioskAppData::STATUS_ERROR)
295      apps->push_back(App(app_data));
296  }
297}
298
299bool KioskAppManager::GetApp(const std::string& app_id, App* app) const {
300  const KioskAppData* data = GetAppData(app_id);
301  if (!data)
302    return false;
303
304  *app = App(*data);
305  return true;
306}
307
308const base::RefCountedString* KioskAppManager::GetAppRawIcon(
309    const std::string& app_id) const {
310  const KioskAppData* data = GetAppData(app_id);
311  if (!data)
312    return NULL;
313
314  return data->raw_icon();
315}
316
317bool KioskAppManager::GetDisableBailoutShortcut() const {
318  bool enable;
319  if (CrosSettings::Get()->GetBoolean(
320          kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled, &enable)) {
321    return !enable;
322  }
323
324  return false;
325}
326
327void KioskAppManager::ClearAppData(const std::string& app_id) {
328  KioskAppData* app_data = GetAppDataMutable(app_id);
329  if (!app_data)
330    return;
331
332  app_data->ClearCache();
333}
334
335void KioskAppManager::UpdateAppDataFromProfile(
336    const std::string& app_id,
337    Profile* profile,
338    const extensions::Extension* app) {
339  KioskAppData* app_data = GetAppDataMutable(app_id);
340  if (!app_data)
341    return;
342
343  app_data->LoadFromInstalledApp(profile, app);
344}
345
346void KioskAppManager::AddObserver(KioskAppManagerObserver* observer) {
347  observers_.AddObserver(observer);
348}
349
350void KioskAppManager::RemoveObserver(KioskAppManagerObserver* observer) {
351  observers_.RemoveObserver(observer);
352}
353
354KioskAppManager::KioskAppManager() : ownership_established_(false) {
355  UpdateAppData();
356  local_accounts_subscription_ =
357      CrosSettings::Get()->AddSettingsObserver(
358          kAccountsPrefDeviceLocalAccounts,
359          base::Bind(&KioskAppManager::UpdateAppData, base::Unretained(this)));
360  local_account_auto_login_id_subscription_ =
361      CrosSettings::Get()->AddSettingsObserver(
362          kAccountsPrefDeviceLocalAccountAutoLoginId,
363          base::Bind(&KioskAppManager::UpdateAppData, base::Unretained(this)));
364}
365
366KioskAppManager::~KioskAppManager() {}
367
368void KioskAppManager::CleanUp() {
369  local_accounts_subscription_.reset();
370  local_account_auto_login_id_subscription_.reset();
371  apps_.clear();
372}
373
374const KioskAppData* KioskAppManager::GetAppData(
375    const std::string& app_id) const {
376  for (size_t i = 0; i < apps_.size(); ++i) {
377    const KioskAppData* data = apps_[i];
378    if (data->app_id() == app_id)
379      return data;
380  }
381
382  return NULL;
383}
384
385KioskAppData* KioskAppManager::GetAppDataMutable(const std::string& app_id) {
386  return const_cast<KioskAppData*>(GetAppData(app_id));
387}
388
389void KioskAppManager::UpdateAppData() {
390  // Gets app id to data mapping for existing apps.
391  std::map<std::string, KioskAppData*> old_apps;
392  for (size_t i = 0; i < apps_.size(); ++i)
393    old_apps[apps_[i]->app_id()] = apps_[i];
394  apps_.weak_clear();  // |old_apps| takes ownership
395
396  auto_launch_app_id_.clear();
397  std::string auto_login_account_id;
398  CrosSettings::Get()->GetString(kAccountsPrefDeviceLocalAccountAutoLoginId,
399                                 &auto_login_account_id);
400
401  // Re-populates |apps_| and reuses existing KioskAppData when possible.
402  const std::vector<policy::DeviceLocalAccount> device_local_accounts =
403      policy::GetDeviceLocalAccounts(CrosSettings::Get());
404  for (std::vector<policy::DeviceLocalAccount>::const_iterator
405           it = device_local_accounts.begin();
406       it != device_local_accounts.end(); ++it) {
407    if (it->type != policy::DeviceLocalAccount::TYPE_KIOSK_APP)
408      continue;
409
410    if (it->account_id == auto_login_account_id)
411      auto_launch_app_id_ = it->kiosk_app_id;
412
413    // TODO(mnissler): Support non-CWS update URLs.
414
415    std::map<std::string, KioskAppData*>::iterator old_it =
416        old_apps.find(it->kiosk_app_id);
417    if (old_it != old_apps.end()) {
418      apps_.push_back(old_it->second);
419      old_apps.erase(old_it);
420    } else {
421      KioskAppData* new_app =
422          new KioskAppData(this, it->kiosk_app_id, it->user_id);
423      apps_.push_back(new_app);  // Takes ownership of |new_app|.
424      new_app->Load();
425    }
426  }
427
428  // Clears cache and deletes the remaining old data.
429  for (std::map<std::string, KioskAppData*>::iterator it = old_apps.begin();
430       it != old_apps.end(); ++it) {
431    it->second->ClearCache();
432    cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove(
433        it->second->user_id(),
434        base::Bind(&OnRemoveAppCryptohomeComplete, it->first));
435  }
436  STLDeleteValues(&old_apps);
437
438  FOR_EACH_OBSERVER(KioskAppManagerObserver, observers_,
439                    OnKioskAppsSettingsChanged());
440}
441
442void KioskAppManager::GetKioskAppIconCacheDir(base::FilePath* cache_dir) {
443  base::FilePath user_data_dir;
444  CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
445  *cache_dir = user_data_dir.AppendASCII(kIconCacheDir);
446}
447
448void KioskAppManager::OnKioskAppDataChanged(const std::string& app_id) {
449  FOR_EACH_OBSERVER(KioskAppManagerObserver,
450                    observers_,
451                    OnKioskAppDataChanged(app_id));
452}
453
454void KioskAppManager::OnKioskAppDataLoadFailure(const std::string& app_id) {
455  FOR_EACH_OBSERVER(KioskAppManagerObserver,
456                    observers_,
457                    OnKioskAppDataLoadFailure(app_id));
458}
459
460KioskAppManager::AutoLoginState KioskAppManager::GetAutoLoginState() const {
461  PrefService* prefs = g_browser_process->local_state();
462  const base::DictionaryValue* dict =
463      prefs->GetDictionary(KioskAppManager::kKioskDictionaryName);
464  int value;
465  if (!dict->GetInteger(kKeyAutoLoginState, &value))
466    return AUTOLOGIN_NONE;
467
468  return static_cast<AutoLoginState>(value);
469}
470
471void KioskAppManager::SetAutoLoginState(AutoLoginState state) {
472  PrefService* prefs = g_browser_process->local_state();
473  DictionaryPrefUpdate dict_update(prefs,
474                                   KioskAppManager::kKioskDictionaryName);
475  dict_update->SetInteger(kKeyAutoLoginState, state);
476  prefs->CommitPendingWrite();
477}
478
479}  // namespace chromeos
480