kiosk_app_manager.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/logging.h" 12#include "base/memory/scoped_ptr.h" 13#include "base/path_service.h" 14#include "base/prefs/pref_registry_simple.h" 15#include "base/stl_util.h" 16#include "base/values.h" 17#include "chrome/browser/chromeos/app_mode/kiosk_app_data.h" 18#include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h" 19#include "chrome/browser/chromeos/login/user_manager.h" 20#include "chrome/browser/chromeos/settings/cros_settings.h" 21#include "chrome/common/chrome_notification_types.h" 22#include "chrome/common/chrome_paths.h" 23#include "chromeos/cryptohome/async_method_caller.h" 24 25namespace chromeos { 26 27namespace { 28 29std::string FormatKioskAppUserId(const std::string& app_id) { 30 return app_id + '@' + UserManager::kKioskAppUserDomain; 31} 32 33void OnRemoveAppCryptohomeComplete(const std::string& app, 34 bool success, 35 cryptohome::MountError return_code) { 36 if (!success) { 37 LOG(ERROR) << "Remove cryptohome for " << app 38 << " failed, return code: " << return_code; 39 } 40} 41 42// Decodes a device-local account dictionary and extracts the |account_id| and 43// |app_id| if decoding is successful and the entry refers to a Kiosk App. 44bool DecodeDeviceLocalAccount(const base::Value* account_spec, 45 std::string* account_id, 46 std::string* app_id) { 47 const base::DictionaryValue* account_dict = NULL; 48 if (!account_spec->GetAsDictionary(&account_dict)) { 49 NOTREACHED(); 50 return false; 51 } 52 53 if (!account_dict->GetStringWithoutPathExpansion( 54 kAccountsPrefDeviceLocalAccountsKeyId, account_id)) { 55 LOG(ERROR) << "Account ID missing"; 56 return false; 57 } 58 59 int type; 60 if (!account_dict->GetIntegerWithoutPathExpansion( 61 kAccountsPrefDeviceLocalAccountsKeyType, &type) || 62 type != DEVICE_LOCAL_ACCOUNT_TYPE_KIOSK_APP) { 63 // Not a kiosk app. 64 return false; 65 } 66 67 if (!account_dict->GetStringWithoutPathExpansion( 68 kAccountsPrefDeviceLocalAccountsKeyKioskAppId, app_id)) { 69 LOG(ERROR) << "Kiosk app id missing for " << *account_id; 70 return false; 71 } 72 73 return true; 74} 75 76} // namespace 77 78// static 79const char KioskAppManager::kKioskDictionaryName[] = "kiosk"; 80const char KioskAppManager::kKeyApps[] = "apps"; 81const char KioskAppManager::kIconCacheDir[] = "kiosk"; 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) 103 : id(data.id()), 104 name(data.name()), 105 icon(data.icon()), 106 is_loading(data.IsLoading()) { 107} 108 109KioskAppManager::App::App() : is_loading(false) {} 110KioskAppManager::App::~App() {} 111 112std::string KioskAppManager::GetAutoLaunchApp() const { 113 return auto_launch_app_id_; 114} 115 116void KioskAppManager::SetAutoLaunchApp(const std::string& app_id) { 117 CrosSettings::Get()->SetString( 118 kAccountsPrefDeviceLocalAccountAutoLoginId, 119 app_id.empty() ? std::string() : FormatKioskAppUserId(app_id)); 120 CrosSettings::Get()->SetInteger( 121 kAccountsPrefDeviceLocalAccountAutoLoginDelay, 0); 122} 123 124void KioskAppManager::AddApp(const std::string& app_id) { 125 CrosSettings* cros_settings = CrosSettings::Get(); 126 const base::ListValue* accounts_list = NULL; 127 cros_settings->GetList(kAccountsPrefDeviceLocalAccounts, &accounts_list); 128 129 // Don't insert if the app if it's already in the list. 130 base::ListValue new_accounts_list; 131 if (accounts_list) { 132 for (base::ListValue::const_iterator entry(accounts_list->begin()); 133 entry != accounts_list->end(); ++entry) { 134 std::string account_id; 135 std::string kiosk_app_id; 136 if (DecodeDeviceLocalAccount(*entry, &account_id, &kiosk_app_id) && 137 kiosk_app_id == app_id) { 138 return; 139 } 140 new_accounts_list.Append((*entry)->DeepCopy()); 141 } 142 } 143 144 // Add the new account. 145 scoped_ptr<base::DictionaryValue> new_entry(new base::DictionaryValue()); 146 new_entry->SetStringWithoutPathExpansion( 147 kAccountsPrefDeviceLocalAccountsKeyId, FormatKioskAppUserId(app_id)); 148 new_entry->SetIntegerWithoutPathExpansion( 149 kAccountsPrefDeviceLocalAccountsKeyType, 150 DEVICE_LOCAL_ACCOUNT_TYPE_KIOSK_APP); 151 new_entry->SetStringWithoutPathExpansion( 152 kAccountsPrefDeviceLocalAccountsKeyKioskAppId, app_id); 153 new_accounts_list.Append(new_entry.release()); 154 cros_settings->Set(kAccountsPrefDeviceLocalAccounts, new_accounts_list); 155} 156 157void KioskAppManager::RemoveApp(const std::string& app_id) { 158 CrosSettings* cros_settings = CrosSettings::Get(); 159 const base::ListValue* accounts_list = NULL; 160 cros_settings->GetList(kAccountsPrefDeviceLocalAccounts, &accounts_list); 161 if (!accounts_list) 162 return; 163 164 // Duplicate the list, filtering out entries that match |app_id|. 165 base::ListValue new_accounts_list; 166 for (base::ListValue::const_iterator entry(accounts_list->begin()); 167 entry != accounts_list->end(); ++entry) { 168 std::string account_id; 169 std::string kiosk_app_id; 170 if (DecodeDeviceLocalAccount(*entry, &account_id, &kiosk_app_id) && 171 kiosk_app_id == app_id) { 172 continue; 173 } 174 new_accounts_list.Append((*entry)->DeepCopy()); 175 } 176 177 cros_settings->Set(kAccountsPrefDeviceLocalAccounts, new_accounts_list); 178} 179 180void KioskAppManager::GetApps(Apps* apps) const { 181 apps->reserve(apps_.size()); 182 for (size_t i = 0; i < apps_.size(); ++i) 183 apps->push_back(App(*apps_[i])); 184} 185 186bool KioskAppManager::GetApp(const std::string& app_id, App* app) const { 187 const KioskAppData* data = GetAppData(app_id); 188 if (!data) 189 return false; 190 191 *app = App(*data); 192 return true; 193} 194 195const base::RefCountedString* KioskAppManager::GetAppRawIcon( 196 const std::string& app_id) const { 197 const KioskAppData* data = GetAppData(app_id); 198 if (!data) 199 return NULL; 200 201 return data->raw_icon(); 202} 203 204bool KioskAppManager::GetDisableBailoutShortcut() const { 205 bool enable; 206 if (CrosSettings::Get()->GetBoolean( 207 kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled, &enable)) { 208 return !enable; 209 } 210 211 return false; 212} 213 214void KioskAppManager::AddObserver(KioskAppManagerObserver* observer) { 215 observers_.AddObserver(observer); 216} 217 218void KioskAppManager::RemoveObserver(KioskAppManagerObserver* observer) { 219 observers_.RemoveObserver(observer); 220} 221 222KioskAppManager::KioskAppManager() { 223 UpdateAppData(); 224 CrosSettings::Get()->AddSettingsObserver( 225 kAccountsPrefDeviceLocalAccounts, this); 226 CrosSettings::Get()->AddSettingsObserver( 227 kAccountsPrefDeviceLocalAccountAutoLoginId, this); 228} 229 230KioskAppManager::~KioskAppManager() {} 231 232void KioskAppManager::CleanUp() { 233 CrosSettings::Get()->RemoveSettingsObserver( 234 kAccountsPrefDeviceLocalAccounts, this); 235 CrosSettings::Get()->RemoveSettingsObserver( 236 kAccountsPrefDeviceLocalAccountAutoLoginId, this); 237 apps_.clear(); 238} 239 240const KioskAppData* KioskAppManager::GetAppData( 241 const std::string& app_id) const { 242 for (size_t i = 0; i < apps_.size(); ++i) { 243 const KioskAppData* data = apps_[i]; 244 if (data->id() == app_id) 245 return data; 246 } 247 248 return NULL; 249} 250 251void KioskAppManager::UpdateAppData() { 252 // Gets app id to data mapping for existing apps. 253 std::map<std::string, KioskAppData*> old_apps; 254 for (size_t i = 0; i < apps_.size(); ++i) 255 old_apps[apps_[i]->id()] = apps_[i]; 256 apps_.weak_clear(); // |old_apps| takes ownership 257 258 auto_launch_app_id_.clear(); 259 std::string auto_login_account_id; 260 CrosSettings::Get()->GetString(kAccountsPrefDeviceLocalAccountAutoLoginId, 261 &auto_login_account_id); 262 263 const base::ListValue* local_accounts; 264 if (CrosSettings::Get()->GetList(kAccountsPrefDeviceLocalAccounts, 265 &local_accounts)) { 266 // Re-populates |apps_| and reuses existing KioskAppData when possible. 267 for (base::ListValue::const_iterator account(local_accounts->begin()); 268 account != local_accounts->end(); ++account) { 269 std::string account_id; 270 std::string kiosk_app_id; 271 if (!DecodeDeviceLocalAccount(*account, &account_id, &kiosk_app_id)) 272 continue; 273 274 if (account_id == auto_login_account_id) 275 auto_launch_app_id_ = kiosk_app_id; 276 277 // TODO(mnissler): Support non-CWS update URLs. 278 279 std::map<std::string, KioskAppData*>::iterator old_it = 280 old_apps.find(kiosk_app_id); 281 if (old_it != old_apps.end()) { 282 apps_.push_back(old_it->second); 283 old_apps.erase(old_it); 284 } else { 285 KioskAppData* new_app = new KioskAppData(this, kiosk_app_id); 286 apps_.push_back(new_app); // Takes ownership of |new_app|. 287 new_app->Load(); 288 } 289 } 290 } 291 292 // Clears cache and deletes the remaining old data. 293 for (std::map<std::string, KioskAppData*>::iterator it = old_apps.begin(); 294 it != old_apps.end(); ++it) { 295 it->second->ClearCache(); 296 cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove( 297 it->first, base::Bind(&OnRemoveAppCryptohomeComplete, it->first)); 298 } 299 STLDeleteValues(&old_apps); 300 301 FOR_EACH_OBSERVER(KioskAppManagerObserver, observers_, 302 OnKioskAppsSettingsChanged()); 303} 304 305void KioskAppManager::Observe(int type, 306 const content::NotificationSource& source, 307 const content::NotificationDetails& details) { 308 DCHECK_EQ(chrome::NOTIFICATION_SYSTEM_SETTING_CHANGED, type); 309 UpdateAppData(); 310} 311 312void KioskAppManager::GetKioskAppIconCacheDir(base::FilePath* cache_dir) { 313 base::FilePath user_data_dir; 314 CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)); 315 *cache_dir = user_data_dir.AppendASCII(kIconCacheDir); 316} 317 318void KioskAppManager::OnKioskAppDataChanged(const std::string& app_id) { 319 FOR_EACH_OBSERVER(KioskAppManagerObserver, 320 observers_, 321 OnKioskAppDataChanged(app_id)); 322} 323 324void KioskAppManager::OnKioskAppDataLoadFailure(const std::string& app_id) { 325 FOR_EACH_OBSERVER(KioskAppManagerObserver, 326 observers_, 327 OnKioskAppDataLoadFailure(app_id)); 328 RemoveApp(app_id); 329} 330 331} // namespace chromeos 332