// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/chromeos/policy/app_pack_updater.h" #include "base/bind.h" #include "base/memory/ref_counted.h" #include "base/sequenced_task_runner.h" #include "base/threading/sequenced_worker_pool.h" #include "base/values.h" #include "chrome/browser/chromeos/policy/enterprise_install_attributes.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/extensions/external_loader.h" #include "chrome/browser/extensions/external_provider_impl.h" #include "chromeos/settings/cros_settings_names.h" #include "content/public/browser/browser_thread.h" using content::BrowserThread; namespace policy { namespace { // Directory where the AppPack extensions are cached. const char kAppPackCacheDir[] = "/var/cache/app_pack"; } // namespace // A custom extensions::ExternalLoader that the AppPackUpdater creates and uses // to publish AppPack updates to the extensions system. class AppPackExternalLoader : public extensions::ExternalLoader, public base::SupportsWeakPtr { public: AppPackExternalLoader() {} // Used by the AppPackUpdater to update the current list of extensions. // The format of |prefs| is detailed in the extensions::ExternalLoader/ // Provider headers. void SetCurrentAppPackExtensions(scoped_ptr prefs) { app_pack_prefs_.Swap(prefs.get()); StartLoading(); } // Implementation of extensions::ExternalLoader: virtual void StartLoading() OVERRIDE { prefs_.reset(app_pack_prefs_.DeepCopy()); VLOG(1) << "AppPack extension loader publishing " << app_pack_prefs_.size() << " crx files."; LoadFinished(); } protected: virtual ~AppPackExternalLoader() {} private: base::DictionaryValue app_pack_prefs_; DISALLOW_COPY_AND_ASSIGN(AppPackExternalLoader); }; AppPackUpdater::AppPackUpdater(net::URLRequestContextGetter* request_context, EnterpriseInstallAttributes* install_attributes) : weak_ptr_factory_(this), created_extension_loader_(false), install_attributes_(install_attributes), external_cache_(base::FilePath(kAppPackCacheDir), request_context, content::BrowserThread::GetBlockingPool()-> GetSequencedTaskRunnerWithShutdownBehavior( content::BrowserThread::GetBlockingPool()-> GetSequenceToken(), base::SequencedWorkerPool::SKIP_ON_SHUTDOWN), this, false, false) { app_pack_subscription_ = chromeos::CrosSettings::Get()->AddSettingsObserver( chromeos::kAppPack, base::Bind(&AppPackUpdater::AppPackChanged, base::Unretained(this))); if (install_attributes_->GetMode() == DEVICE_MODE_RETAIL_KIOSK) { // Already in Kiosk mode, start loading. BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(&AppPackUpdater::LoadPolicy, weak_ptr_factory_.GetWeakPtr())); } else { // Linger until the device switches to DEVICE_MODE_RETAIL_KIOSK and the // app pack device setting appears. } } AppPackUpdater::~AppPackUpdater() { } extensions::ExternalLoader* AppPackUpdater::CreateExternalLoader() { if (created_extension_loader_) { NOTREACHED(); return NULL; } created_extension_loader_ = true; AppPackExternalLoader* loader = new AppPackExternalLoader(); extension_loader_ = loader->AsWeakPtr(); // The cache may have been already checked. In that case, load the current // extensions into the loader immediately. UpdateExtensionLoader(); return loader; } void AppPackUpdater::SetScreenSaverUpdateCallback( const AppPackUpdater::ScreenSaverUpdateCallback& callback) { screen_saver_update_callback_ = callback; if (!screen_saver_update_callback_.is_null() && !screen_saver_path_.empty()) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(screen_saver_update_callback_, screen_saver_path_)); } } void AppPackUpdater::AppPackChanged() { if (install_attributes_->GetMode() == DEVICE_MODE_RETAIL_KIOSK) LoadPolicy(); } void AppPackUpdater::LoadPolicy() { chromeos::CrosSettings* settings = chromeos::CrosSettings::Get(); if (chromeos::CrosSettingsProvider::TRUSTED != settings->PrepareTrustedValues( base::Bind(&AppPackUpdater::LoadPolicy, weak_ptr_factory_.GetWeakPtr()))) { return; } scoped_ptr prefs(new base::DictionaryValue()); const base::Value* value = settings->GetPref(chromeos::kAppPack); const base::ListValue* list = NULL; if (value && value->GetAsList(&list)) { for (base::ListValue::const_iterator it = list->begin(); it != list->end(); ++it) { base::DictionaryValue* dict = NULL; if (!(*it)->GetAsDictionary(&dict)) { LOG(WARNING) << "AppPack entry is not a dictionary, ignoring."; continue; } std::string id; std::string update_url; if (dict->GetString(chromeos::kAppPackKeyExtensionId, &id) && dict->GetString(chromeos::kAppPackKeyUpdateUrl, &update_url)) { base::DictionaryValue* entry = new base::DictionaryValue(); entry->SetString(extensions::ExternalProviderImpl::kExternalUpdateUrl, update_url); prefs->Set(id, entry); } else { LOG(WARNING) << "Failed to read required fields for an AppPack entry, " << "ignoring."; } } } VLOG(1) << "Refreshed AppPack policy, got " << prefs->size() << " entries."; value = settings->GetPref(chromeos::kScreenSaverExtensionId); if (!value || !value->GetAsString(&screen_saver_id_)) { screen_saver_id_.clear(); SetScreenSaverPath(base::FilePath()); } external_cache_.UpdateExtensionsList(prefs.Pass()); } void AppPackUpdater::OnExtensionListsUpdated( const base::DictionaryValue* prefs) { std::string crx_path; const base::DictionaryValue* screen_saver = NULL; if (prefs->GetDictionary(screen_saver_id_, &screen_saver)) { screen_saver->GetString(extensions::ExternalProviderImpl::kExternalCrx, &crx_path); } if (!crx_path.empty()) SetScreenSaverPath(base::FilePath(crx_path)); else SetScreenSaverPath(base::FilePath()); UpdateExtensionLoader(); } void AppPackUpdater::UpdateExtensionLoader() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (!extension_loader_) { VLOG(1) << "No AppPack loader created yet, not pushing extensions."; return; } scoped_ptr prefs( external_cache_.cached_extensions()->DeepCopy()); // The screensaver isn't installed into the Profile. prefs->Remove(screen_saver_id_, NULL); extension_loader_->SetCurrentAppPackExtensions(prefs.Pass()); } void AppPackUpdater::OnDamagedFileDetected(const base::FilePath& path) { external_cache_.OnDamagedFileDetected(path); } void AppPackUpdater::SetScreenSaverPath(const base::FilePath& path) { // Don't invoke the callback if the path isn't changing. if (path != screen_saver_path_) { screen_saver_path_ = path; if (!screen_saver_update_callback_.is_null()) screen_saver_update_callback_.Run(screen_saver_path_); } } } // namespace policy