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 <map>
8#include <set>
9
10#include "base/bind.h"
11#include "base/files/file_path.h"
12#include "base/files/file_util.h"
13#include "base/logging.h"
14#include "base/path_service.h"
15#include "base/prefs/pref_registry_simple.h"
16#include "base/prefs/pref_service.h"
17#include "base/prefs/scoped_user_pref_update.h"
18#include "base/stl_util.h"
19#include "base/sys_info.h"
20#include "chrome/browser/browser_process.h"
21#include "chrome/browser/chromeos/app_mode/kiosk_app_data.h"
22#include "chrome/browser/chromeos/app_mode/kiosk_app_external_loader.h"
23#include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h"
24#include "chrome/browser/chromeos/app_mode/kiosk_external_updater.h"
25#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
26#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
27#include "chrome/browser/chromeos/policy/device_local_account.h"
28#include "chrome/browser/chromeos/settings/cros_settings.h"
29#include "chrome/browser/extensions/external_loader.h"
30#include "chrome/browser/extensions/external_provider_impl.h"
31#include "chrome/common/chrome_paths.h"
32#include "chrome/common/extensions/extension_constants.h"
33#include "chromeos/chromeos_paths.h"
34#include "chromeos/cryptohome/async_method_caller.h"
35#include "chromeos/settings/cros_settings_names.h"
36#include "components/ownership/owner_key_util.h"
37#include "content/public/browser/browser_thread.h"
38
39namespace chromeos {
40
41namespace {
42
43// Domain that is used for kiosk-app account IDs.
44const char kKioskAppAccountDomain[] = "kiosk-apps";
45
46std::string GenerateKioskAppAccountId(const std::string& app_id) {
47  return app_id + '@' + kKioskAppAccountDomain;
48}
49
50void OnRemoveAppCryptohomeComplete(const std::string& app,
51                                   bool success,
52                                   cryptohome::MountError return_code) {
53  if (!success) {
54    LOG(ERROR) << "Remove cryptohome for " << app
55        << " failed, return code: " << return_code;
56  }
57}
58
59// Check for presence of machine owner public key file.
60void CheckOwnerFilePresence(bool *present) {
61  scoped_refptr<ownership::OwnerKeyUtil> util =
62      OwnerSettingsServiceChromeOSFactory::GetInstance()->GetOwnerKeyUtil();
63  *present = util.get() && util->IsPublicKeyPresent();
64}
65
66scoped_refptr<base::SequencedTaskRunner> GetBackgroundTaskRunner() {
67  base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
68  CHECK(pool);
69  return pool->GetSequencedTaskRunnerWithShutdownBehavior(
70      pool->GetSequenceToken(), base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
71}
72
73}  // namespace
74
75// static
76const char KioskAppManager::kKioskDictionaryName[] = "kiosk";
77const char KioskAppManager::kKeyApps[] = "apps";
78const char KioskAppManager::kKeyAutoLoginState[] = "auto_login_state";
79const char KioskAppManager::kIconCacheDir[] = "kiosk/icon";
80const char KioskAppManager::kCrxCacheDir[] = "kiosk/crx";
81const char KioskAppManager::kCrxUnpackDir[] = "kiosk_unpack";
82
83// static
84static base::LazyInstance<KioskAppManager> instance = LAZY_INSTANCE_INITIALIZER;
85KioskAppManager* KioskAppManager::Get() {
86  return instance.Pointer();
87}
88
89// static
90void KioskAppManager::Shutdown() {
91  if (instance == NULL)
92    return;
93
94  instance.Pointer()->CleanUp();
95}
96
97// static
98void KioskAppManager::RegisterPrefs(PrefRegistrySimple* registry) {
99  registry->RegisterDictionaryPref(kKioskDictionaryName);
100}
101
102KioskAppManager::App::App(const KioskAppData& data, bool is_extension_pending)
103    : app_id(data.app_id()),
104      user_id(data.user_id()),
105      name(data.name()),
106      icon(data.icon()),
107      is_loading(data.IsLoading() || is_extension_pending) {
108}
109
110KioskAppManager::App::App() : is_loading(false) {}
111KioskAppManager::App::~App() {}
112
113std::string KioskAppManager::GetAutoLaunchApp() const {
114  return auto_launch_app_id_;
115}
116
117void KioskAppManager::SetAutoLaunchApp(const std::string& app_id) {
118  SetAutoLoginState(AUTOLOGIN_REQUESTED);
119  // Clean first, so the proper change callbacks are triggered even
120  // if we are only changing AutoLoginState here.
121  if (!auto_launch_app_id_.empty()) {
122    CrosSettings::Get()->SetString(kAccountsPrefDeviceLocalAccountAutoLoginId,
123                                   std::string());
124  }
125
126  CrosSettings::Get()->SetString(
127      kAccountsPrefDeviceLocalAccountAutoLoginId,
128      app_id.empty() ? std::string() : GenerateKioskAppAccountId(app_id));
129  CrosSettings::Get()->SetInteger(
130      kAccountsPrefDeviceLocalAccountAutoLoginDelay, 0);
131}
132
133void KioskAppManager::EnableConsumerKioskAutoLaunch(
134    const KioskAppManager::EnableKioskAutoLaunchCallback& callback) {
135  policy::BrowserPolicyConnectorChromeOS* connector =
136      g_browser_process->platform_part()->browser_policy_connector_chromeos();
137  connector->GetInstallAttributes()->LockDevice(
138      std::string(),  // user
139      policy::DEVICE_MODE_CONSUMER_KIOSK_AUTOLAUNCH,
140      std::string(),  // device_id
141      base::Bind(
142          &KioskAppManager::OnLockDevice, base::Unretained(this), callback));
143}
144
145void KioskAppManager::GetConsumerKioskAutoLaunchStatus(
146    const KioskAppManager::GetConsumerKioskAutoLaunchStatusCallback& callback) {
147  policy::BrowserPolicyConnectorChromeOS* connector =
148      g_browser_process->platform_part()->browser_policy_connector_chromeos();
149  connector->GetInstallAttributes()->ReadImmutableAttributes(
150      base::Bind(&KioskAppManager::OnReadImmutableAttributes,
151                 base::Unretained(this),
152                 callback));
153}
154
155bool KioskAppManager::IsConsumerKioskDeviceWithAutoLaunch() {
156  policy::BrowserPolicyConnectorChromeOS* connector =
157      g_browser_process->platform_part()->browser_policy_connector_chromeos();
158  return connector->GetInstallAttributes() &&
159         connector->GetInstallAttributes()
160             ->IsConsumerKioskDeviceWithAutoLaunch();
161}
162
163void KioskAppManager::OnLockDevice(
164    const KioskAppManager::EnableKioskAutoLaunchCallback& callback,
165    policy::EnterpriseInstallAttributes::LockResult result) {
166  if (callback.is_null())
167    return;
168
169  callback.Run(result == policy::EnterpriseInstallAttributes::LOCK_SUCCESS);
170}
171
172void KioskAppManager::OnOwnerFileChecked(
173    const KioskAppManager::GetConsumerKioskAutoLaunchStatusCallback& callback,
174    bool* owner_present) {
175  ownership_established_ = *owner_present;
176
177  if (callback.is_null())
178    return;
179
180  // If we have owner already established on the machine, don't let
181  // consumer kiosk to be enabled.
182  if (ownership_established_)
183    callback.Run(CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED);
184  else
185    callback.Run(CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE);
186}
187
188void KioskAppManager::OnReadImmutableAttributes(
189    const KioskAppManager::GetConsumerKioskAutoLaunchStatusCallback&
190        callback) {
191  if (callback.is_null())
192    return;
193
194  ConsumerKioskAutoLaunchStatus status =
195      CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED;
196  policy::BrowserPolicyConnectorChromeOS* connector =
197      g_browser_process->platform_part()->browser_policy_connector_chromeos();
198  policy::EnterpriseInstallAttributes* attributes =
199      connector->GetInstallAttributes();
200  switch (attributes->GetMode()) {
201    case policy::DEVICE_MODE_NOT_SET: {
202      if (!base::SysInfo::IsRunningOnChromeOS()) {
203        status = CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE;
204      } else if (!ownership_established_) {
205        bool* owner_present = new bool(false);
206        content::BrowserThread::PostBlockingPoolTaskAndReply(
207            FROM_HERE,
208            base::Bind(&CheckOwnerFilePresence,
209                       owner_present),
210            base::Bind(&KioskAppManager::OnOwnerFileChecked,
211                       base::Unretained(this),
212                       callback,
213                       base::Owned(owner_present)));
214        return;
215      }
216      break;
217    }
218    case policy::DEVICE_MODE_CONSUMER_KIOSK_AUTOLAUNCH:
219      status = CONSUMER_KIOSK_AUTO_LAUNCH_ENABLED;
220      break;
221    default:
222      break;
223  }
224
225  callback.Run(status);
226}
227
228void KioskAppManager::SetEnableAutoLaunch(bool value) {
229  SetAutoLoginState(value ? AUTOLOGIN_APPROVED : AUTOLOGIN_REJECTED);
230}
231
232bool KioskAppManager::IsAutoLaunchRequested() const {
233  if (GetAutoLaunchApp().empty())
234    return false;
235
236  // Apps that were installed by the policy don't require machine owner
237  // consent through UI.
238  policy::BrowserPolicyConnectorChromeOS* connector =
239      g_browser_process->platform_part()->browser_policy_connector_chromeos();
240  if (connector->IsEnterpriseManaged())
241    return false;
242
243  return GetAutoLoginState() == AUTOLOGIN_REQUESTED;
244}
245
246bool KioskAppManager::IsAutoLaunchEnabled() const {
247  if (GetAutoLaunchApp().empty())
248    return false;
249
250  // Apps that were installed by the policy don't require machine owner
251  // consent through UI.
252  policy::BrowserPolicyConnectorChromeOS* connector =
253      g_browser_process->platform_part()->browser_policy_connector_chromeos();
254  if (connector->IsEnterpriseManaged())
255    return true;
256
257  return GetAutoLoginState() == AUTOLOGIN_APPROVED;
258}
259
260void KioskAppManager::AddApp(const std::string& app_id) {
261  std::vector<policy::DeviceLocalAccount> device_local_accounts =
262      policy::GetDeviceLocalAccounts(CrosSettings::Get());
263
264  // Don't insert the app if it's already in the list.
265  for (std::vector<policy::DeviceLocalAccount>::const_iterator
266           it = device_local_accounts.begin();
267       it != device_local_accounts.end(); ++it) {
268    if (it->type == policy::DeviceLocalAccount::TYPE_KIOSK_APP &&
269        it->kiosk_app_id == app_id) {
270      return;
271    }
272  }
273
274  // Add the new account.
275  device_local_accounts.push_back(policy::DeviceLocalAccount(
276      policy::DeviceLocalAccount::TYPE_KIOSK_APP,
277      GenerateKioskAppAccountId(app_id),
278      app_id));
279
280  policy::SetDeviceLocalAccounts(CrosSettings::Get(), device_local_accounts);
281}
282
283void KioskAppManager::RemoveApp(const std::string& app_id) {
284  // Resets auto launch app if it is the removed app.
285  if (auto_launch_app_id_ == app_id)
286    SetAutoLaunchApp(std::string());
287
288  std::vector<policy::DeviceLocalAccount> device_local_accounts =
289      policy::GetDeviceLocalAccounts(CrosSettings::Get());
290  if (device_local_accounts.empty())
291    return;
292
293  // Remove entries that match |app_id|.
294  for (std::vector<policy::DeviceLocalAccount>::iterator
295           it = device_local_accounts.begin();
296       it != device_local_accounts.end(); ++it) {
297    if (it->type == policy::DeviceLocalAccount::TYPE_KIOSK_APP &&
298        it->kiosk_app_id == app_id) {
299      device_local_accounts.erase(it);
300      break;
301    }
302  }
303
304  policy::SetDeviceLocalAccounts(CrosSettings::Get(), device_local_accounts);
305}
306
307void KioskAppManager::GetApps(Apps* apps) const {
308  apps->clear();
309  apps->reserve(apps_.size());
310  for (size_t i = 0; i < apps_.size(); ++i) {
311    const KioskAppData& app_data = *apps_[i];
312    if (app_data.status() != KioskAppData::STATUS_ERROR)
313      apps->push_back(App(
314          app_data, external_cache_->IsExtensionPending(app_data.app_id())));
315  }
316}
317
318bool KioskAppManager::GetApp(const std::string& app_id, App* app) const {
319  const KioskAppData* data = GetAppData(app_id);
320  if (!data)
321    return false;
322
323  *app = App(*data, external_cache_->IsExtensionPending(app_id));
324  return true;
325}
326
327const base::RefCountedString* KioskAppManager::GetAppRawIcon(
328    const std::string& app_id) const {
329  const KioskAppData* data = GetAppData(app_id);
330  if (!data)
331    return NULL;
332
333  return data->raw_icon();
334}
335
336bool KioskAppManager::GetDisableBailoutShortcut() const {
337  bool enable;
338  if (CrosSettings::Get()->GetBoolean(
339          kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled, &enable)) {
340    return !enable;
341  }
342
343  return false;
344}
345
346void KioskAppManager::ClearAppData(const std::string& app_id) {
347  KioskAppData* app_data = GetAppDataMutable(app_id);
348  if (!app_data)
349    return;
350
351  app_data->ClearCache();
352}
353
354void KioskAppManager::UpdateAppDataFromProfile(
355    const std::string& app_id,
356    Profile* profile,
357    const extensions::Extension* app) {
358  KioskAppData* app_data = GetAppDataMutable(app_id);
359  if (!app_data)
360    return;
361
362  app_data->LoadFromInstalledApp(profile, app);
363}
364
365void KioskAppManager::RetryFailedAppDataFetch() {
366  for (size_t i = 0; i < apps_.size(); ++i) {
367    if (apps_[i]->status() == KioskAppData::STATUS_ERROR)
368      apps_[i]->Load();
369  }
370}
371
372bool KioskAppManager::HasCachedCrx(const std::string& app_id) const {
373  base::FilePath crx_path;
374  std::string version;
375  return GetCachedCrx(app_id, &crx_path, &version);
376}
377
378bool KioskAppManager::GetCachedCrx(const std::string& app_id,
379                                   base::FilePath* file_path,
380                                   std::string* version) const {
381  return external_cache_->GetExtension(app_id, file_path, version);
382}
383
384void KioskAppManager::AddObserver(KioskAppManagerObserver* observer) {
385  observers_.AddObserver(observer);
386}
387
388void KioskAppManager::RemoveObserver(KioskAppManagerObserver* observer) {
389  observers_.RemoveObserver(observer);
390}
391
392extensions::ExternalLoader* KioskAppManager::CreateExternalLoader() {
393  if (external_loader_created_) {
394    NOTREACHED();
395    return NULL;
396  }
397  external_loader_created_ = true;
398  KioskAppExternalLoader* loader = new KioskAppExternalLoader();
399  external_loader_ = loader->AsWeakPtr();
400
401  return loader;
402}
403
404void KioskAppManager::InstallFromCache(const std::string& id) {
405  const base::DictionaryValue* extension = NULL;
406  if (external_cache_->cached_extensions()->GetDictionary(id, &extension)) {
407    scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
408    base::DictionaryValue* extension_copy = extension->DeepCopy();
409    prefs->Set(id, extension_copy);
410    external_loader_->SetCurrentAppExtensions(prefs.Pass());
411  } else {
412    LOG(ERROR) << "Can't find app in the cached externsions"
413               << " id = " << id;
414  }
415}
416
417void KioskAppManager::UpdateExternalCache() {
418  UpdateAppData();
419}
420
421void KioskAppManager::OnKioskAppCacheUpdated(const std::string& app_id) {
422  FOR_EACH_OBSERVER(
423      KioskAppManagerObserver, observers_, OnKioskAppCacheUpdated(app_id));
424}
425
426void KioskAppManager::OnKioskAppExternalUpdateComplete(bool success) {
427  FOR_EACH_OBSERVER(KioskAppManagerObserver,
428                    observers_,
429                    OnKioskAppExternalUpdateComplete(success));
430}
431
432void KioskAppManager::PutValidatedExternalExtension(
433    const std::string& app_id,
434    const base::FilePath& crx_path,
435    const std::string& version,
436    const ExternalCache::PutExternalExtensionCallback& callback) {
437  external_cache_->PutExternalExtension(app_id, crx_path, version, callback);
438}
439
440KioskAppManager::KioskAppManager()
441    : ownership_established_(false), external_loader_created_(false) {
442  base::FilePath cache_dir;
443  GetCrxCacheDir(&cache_dir);
444  external_cache_.reset(
445      new ExternalCache(cache_dir,
446                        g_browser_process->system_request_context(),
447                        GetBackgroundTaskRunner(),
448                        this,
449                        true /* always_check_updates */,
450                        false /* wait_for_cache_initialization */));
451  UpdateAppData();
452  local_accounts_subscription_ =
453      CrosSettings::Get()->AddSettingsObserver(
454          kAccountsPrefDeviceLocalAccounts,
455          base::Bind(&KioskAppManager::UpdateAppData, base::Unretained(this)));
456  local_account_auto_login_id_subscription_ =
457      CrosSettings::Get()->AddSettingsObserver(
458          kAccountsPrefDeviceLocalAccountAutoLoginId,
459          base::Bind(&KioskAppManager::UpdateAppData, base::Unretained(this)));
460}
461
462KioskAppManager::~KioskAppManager() {}
463
464void KioskAppManager::MonitorKioskExternalUpdate() {
465  base::FilePath cache_dir;
466  GetCrxCacheDir(&cache_dir);
467  base::FilePath unpack_dir;
468  GetCrxUnpackDir(&unpack_dir);
469  usb_stick_updater_.reset(new KioskExternalUpdater(
470      GetBackgroundTaskRunner(), cache_dir, unpack_dir));
471}
472
473void KioskAppManager::CleanUp() {
474  local_accounts_subscription_.reset();
475  local_account_auto_login_id_subscription_.reset();
476  apps_.clear();
477  usb_stick_updater_.reset();
478  external_cache_.reset();
479}
480
481const KioskAppData* KioskAppManager::GetAppData(
482    const std::string& app_id) const {
483  for (size_t i = 0; i < apps_.size(); ++i) {
484    const KioskAppData* data = apps_[i];
485    if (data->app_id() == app_id)
486      return data;
487  }
488
489  return NULL;
490}
491
492KioskAppData* KioskAppManager::GetAppDataMutable(const std::string& app_id) {
493  return const_cast<KioskAppData*>(GetAppData(app_id));
494}
495
496void KioskAppManager::UpdateAppData() {
497  // Gets app id to data mapping for existing apps.
498  std::map<std::string, KioskAppData*> old_apps;
499  for (size_t i = 0; i < apps_.size(); ++i)
500    old_apps[apps_[i]->app_id()] = apps_[i];
501  apps_.weak_clear();  // |old_apps| takes ownership
502
503  auto_launch_app_id_.clear();
504  std::string auto_login_account_id;
505  CrosSettings::Get()->GetString(kAccountsPrefDeviceLocalAccountAutoLoginId,
506                                 &auto_login_account_id);
507
508  // Re-populates |apps_| and reuses existing KioskAppData when possible.
509  const std::vector<policy::DeviceLocalAccount> device_local_accounts =
510      policy::GetDeviceLocalAccounts(CrosSettings::Get());
511  for (std::vector<policy::DeviceLocalAccount>::const_iterator
512           it = device_local_accounts.begin();
513       it != device_local_accounts.end(); ++it) {
514    if (it->type != policy::DeviceLocalAccount::TYPE_KIOSK_APP)
515      continue;
516
517    if (it->account_id == auto_login_account_id)
518      auto_launch_app_id_ = it->kiosk_app_id;
519
520    // TODO(mnissler): Support non-CWS update URLs.
521
522    std::map<std::string, KioskAppData*>::iterator old_it =
523        old_apps.find(it->kiosk_app_id);
524    if (old_it != old_apps.end()) {
525      apps_.push_back(old_it->second);
526      old_apps.erase(old_it);
527    } else {
528      KioskAppData* new_app =
529          new KioskAppData(this, it->kiosk_app_id, it->user_id);
530      apps_.push_back(new_app);  // Takes ownership of |new_app|.
531      new_app->Load();
532    }
533  }
534
535  // Clears cache and deletes the remaining old data.
536  std::vector<std::string> apps_to_remove;
537  for (std::map<std::string, KioskAppData*>::iterator it = old_apps.begin();
538       it != old_apps.end(); ++it) {
539    it->second->ClearCache();
540    cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove(
541        it->second->user_id(),
542        base::Bind(&OnRemoveAppCryptohomeComplete, it->first));
543    apps_to_remove.push_back(it->second->app_id());
544  }
545  STLDeleteValues(&old_apps);
546  external_cache_->RemoveExtensions(apps_to_remove);
547
548  // Request external_cache_ to download new apps and update the existing
549  // apps.
550  scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
551  for (size_t i = 0; i < apps_.size(); ++i) {
552    scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue);
553    entry->SetBoolean(extensions::ExternalProviderImpl::kIsFromWebstore, true);
554    prefs->Set(apps_[i]->app_id(), entry.release());
555  }
556  external_cache_->UpdateExtensionsList(prefs.Pass());
557
558  RetryFailedAppDataFetch();
559
560  FOR_EACH_OBSERVER(KioskAppManagerObserver, observers_,
561                    OnKioskAppsSettingsChanged());
562}
563
564void KioskAppManager::GetKioskAppIconCacheDir(base::FilePath* cache_dir) {
565  base::FilePath user_data_dir;
566  CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
567  *cache_dir = user_data_dir.AppendASCII(kIconCacheDir);
568}
569
570void KioskAppManager::OnKioskAppDataChanged(const std::string& app_id) {
571  FOR_EACH_OBSERVER(KioskAppManagerObserver,
572                    observers_,
573                    OnKioskAppDataChanged(app_id));
574}
575
576void KioskAppManager::OnKioskAppDataLoadFailure(const std::string& app_id) {
577  FOR_EACH_OBSERVER(KioskAppManagerObserver,
578                    observers_,
579                    OnKioskAppDataLoadFailure(app_id));
580}
581
582void KioskAppManager::OnExtensionListsUpdated(
583    const base::DictionaryValue* prefs) {
584}
585
586void KioskAppManager::OnExtensionLoadedInCache(const std::string& id) {
587  KioskAppData* app_data = GetAppDataMutable(id);
588  if (!app_data)
589    return;
590  FOR_EACH_OBSERVER(KioskAppManagerObserver,
591                    observers_,
592                    OnKioskExtensionLoadedInCache(id));
593
594}
595
596void KioskAppManager::OnExtensionDownloadFailed(
597    const std::string& id,
598    extensions::ExtensionDownloaderDelegate::Error error) {
599  KioskAppData* app_data = GetAppDataMutable(id);
600  if (!app_data)
601    return;
602  FOR_EACH_OBSERVER(KioskAppManagerObserver,
603                    observers_,
604                    OnKioskExtensionDownloadFailed(id));
605}
606
607KioskAppManager::AutoLoginState KioskAppManager::GetAutoLoginState() const {
608  PrefService* prefs = g_browser_process->local_state();
609  const base::DictionaryValue* dict =
610      prefs->GetDictionary(KioskAppManager::kKioskDictionaryName);
611  int value;
612  if (!dict->GetInteger(kKeyAutoLoginState, &value))
613    return AUTOLOGIN_NONE;
614
615  return static_cast<AutoLoginState>(value);
616}
617
618void KioskAppManager::SetAutoLoginState(AutoLoginState state) {
619  PrefService* prefs = g_browser_process->local_state();
620  DictionaryPrefUpdate dict_update(prefs,
621                                   KioskAppManager::kKioskDictionaryName);
622  dict_update->SetInteger(kKeyAutoLoginState, state);
623  prefs->CommitPendingWrite();
624}
625
626void KioskAppManager::GetCrxCacheDir(base::FilePath* cache_dir) {
627  base::FilePath user_data_dir;
628  CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
629  *cache_dir = user_data_dir.AppendASCII(kCrxCacheDir);
630}
631
632void KioskAppManager::GetCrxUnpackDir(base::FilePath* unpack_dir) {
633  base::FilePath temp_dir;
634  base::GetTempDir(&temp_dir);
635  *unpack_dir = temp_dir.AppendASCII(kCrxUnpackDir);
636}
637
638}  // namespace chromeos
639