blob: 7e2508ceb7eab0e3cfc866ed2fc8b34158221b9e [file] [log] [blame]
// 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<AppPackExternalLoader> {
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<base::DictionaryValue> 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<base::DictionaryValue> 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<base::DictionaryValue> 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