1 // Copyright (c) 2012 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/policy/app_pack_updater.h"
6
7 #include "base/bind.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/sequenced_task_runner.h"
10 #include "base/threading/sequenced_worker_pool.h"
11 #include "base/values.h"
12 #include "chrome/browser/chromeos/policy/enterprise_install_attributes.h"
13 #include "chrome/browser/chromeos/settings/cros_settings.h"
14 #include "chrome/browser/extensions/external_loader.h"
15 #include "chrome/browser/extensions/external_provider_impl.h"
16 #include "chromeos/settings/cros_settings_names.h"
17 #include "content/public/browser/browser_thread.h"
18
19 using content::BrowserThread;
20
21 namespace policy {
22
23 namespace {
24
25 // Directory where the AppPack extensions are cached.
26 const char kAppPackCacheDir[] = "/var/cache/app_pack";
27
28 } // namespace
29
30 // A custom extensions::ExternalLoader that the AppPackUpdater creates and uses
31 // to publish AppPack updates to the extensions system.
32 class AppPackExternalLoader
33 : public extensions::ExternalLoader,
34 public base::SupportsWeakPtr<AppPackExternalLoader> {
35 public:
AppPackExternalLoader()36 AppPackExternalLoader() {}
37
38 // Used by the AppPackUpdater to update the current list of extensions.
39 // The format of |prefs| is detailed in the extensions::ExternalLoader/
40 // Provider headers.
SetCurrentAppPackExtensions(scoped_ptr<base::DictionaryValue> prefs)41 void SetCurrentAppPackExtensions(scoped_ptr<base::DictionaryValue> prefs) {
42 app_pack_prefs_.Swap(prefs.get());
43 StartLoading();
44 }
45
46 // Implementation of extensions::ExternalLoader:
StartLoading()47 virtual void StartLoading() OVERRIDE {
48 prefs_.reset(app_pack_prefs_.DeepCopy());
49 VLOG(1) << "AppPack extension loader publishing "
50 << app_pack_prefs_.size() << " crx files.";
51 LoadFinished();
52 }
53
54 protected:
~AppPackExternalLoader()55 virtual ~AppPackExternalLoader() {}
56
57 private:
58 base::DictionaryValue app_pack_prefs_;
59
60 DISALLOW_COPY_AND_ASSIGN(AppPackExternalLoader);
61 };
62
AppPackUpdater(net::URLRequestContextGetter * request_context,EnterpriseInstallAttributes * install_attributes)63 AppPackUpdater::AppPackUpdater(net::URLRequestContextGetter* request_context,
64 EnterpriseInstallAttributes* install_attributes)
65 : created_extension_loader_(false),
66 install_attributes_(install_attributes),
67 external_cache_(base::FilePath(kAppPackCacheDir),
68 request_context,
69 content::BrowserThread::GetBlockingPool()->
70 GetSequencedTaskRunnerWithShutdownBehavior(
71 content::BrowserThread::GetBlockingPool()->
72 GetSequenceToken(),
73 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN),
74 this,
75 false,
76 false),
77 weak_ptr_factory_(this) {
78 app_pack_subscription_ = chromeos::CrosSettings::Get()->AddSettingsObserver(
79 chromeos::kAppPack,
80 base::Bind(&AppPackUpdater::AppPackChanged, base::Unretained(this)));
81
82 if (install_attributes_->GetMode() == DEVICE_MODE_RETAIL_KIOSK) {
83 // Already in Kiosk mode, start loading.
84 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
85 base::Bind(&AppPackUpdater::LoadPolicy,
86 weak_ptr_factory_.GetWeakPtr()));
87 } else {
88 // Linger until the device switches to DEVICE_MODE_RETAIL_KIOSK and the
89 // app pack device setting appears.
90 }
91 }
92
~AppPackUpdater()93 AppPackUpdater::~AppPackUpdater() {
94 }
95
CreateExternalLoader()96 extensions::ExternalLoader* AppPackUpdater::CreateExternalLoader() {
97 if (created_extension_loader_) {
98 NOTREACHED();
99 return NULL;
100 }
101 created_extension_loader_ = true;
102 AppPackExternalLoader* loader = new AppPackExternalLoader();
103 extension_loader_ = loader->AsWeakPtr();
104
105 // The cache may have been already checked. In that case, load the current
106 // extensions into the loader immediately.
107 UpdateExtensionLoader();
108
109 return loader;
110 }
111
SetScreenSaverUpdateCallback(const AppPackUpdater::ScreenSaverUpdateCallback & callback)112 void AppPackUpdater::SetScreenSaverUpdateCallback(
113 const AppPackUpdater::ScreenSaverUpdateCallback& callback) {
114 screen_saver_update_callback_ = callback;
115 if (!screen_saver_update_callback_.is_null() && !screen_saver_path_.empty()) {
116 BrowserThread::PostTask(
117 BrowserThread::UI, FROM_HERE,
118 base::Bind(screen_saver_update_callback_, screen_saver_path_));
119 }
120 }
121
AppPackChanged()122 void AppPackUpdater::AppPackChanged() {
123 if (install_attributes_->GetMode() == DEVICE_MODE_RETAIL_KIOSK)
124 LoadPolicy();
125 }
126
LoadPolicy()127 void AppPackUpdater::LoadPolicy() {
128 chromeos::CrosSettings* settings = chromeos::CrosSettings::Get();
129 if (chromeos::CrosSettingsProvider::TRUSTED != settings->PrepareTrustedValues(
130 base::Bind(&AppPackUpdater::LoadPolicy,
131 weak_ptr_factory_.GetWeakPtr()))) {
132 return;
133 }
134
135 scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue());
136 const base::Value* value = settings->GetPref(chromeos::kAppPack);
137 const base::ListValue* list = NULL;
138 if (value && value->GetAsList(&list)) {
139 for (base::ListValue::const_iterator it = list->begin();
140 it != list->end(); ++it) {
141 base::DictionaryValue* dict = NULL;
142 if (!(*it)->GetAsDictionary(&dict)) {
143 LOG(WARNING) << "AppPack entry is not a dictionary, ignoring.";
144 continue;
145 }
146 std::string id;
147 std::string update_url;
148 if (dict->GetString(chromeos::kAppPackKeyExtensionId, &id) &&
149 dict->GetString(chromeos::kAppPackKeyUpdateUrl, &update_url)) {
150 base::DictionaryValue* entry = new base::DictionaryValue();
151 entry->SetString(extensions::ExternalProviderImpl::kExternalUpdateUrl,
152 update_url);
153 prefs->Set(id, entry);
154 } else {
155 LOG(WARNING) << "Failed to read required fields for an AppPack entry, "
156 << "ignoring.";
157 }
158 }
159 }
160
161 VLOG(1) << "Refreshed AppPack policy, got " << prefs->size()
162 << " entries.";
163
164 value = settings->GetPref(chromeos::kScreenSaverExtensionId);
165 if (!value || !value->GetAsString(&screen_saver_id_)) {
166 screen_saver_id_.clear();
167 SetScreenSaverPath(base::FilePath());
168 }
169
170 external_cache_.UpdateExtensionsList(prefs.Pass());
171 }
172
OnExtensionListsUpdated(const base::DictionaryValue * prefs)173 void AppPackUpdater::OnExtensionListsUpdated(
174 const base::DictionaryValue* prefs) {
175 std::string crx_path;
176 const base::DictionaryValue* screen_saver = NULL;
177 if (prefs->GetDictionary(screen_saver_id_, &screen_saver)) {
178 screen_saver->GetString(extensions::ExternalProviderImpl::kExternalCrx,
179 &crx_path);
180 }
181 if (!crx_path.empty())
182 SetScreenSaverPath(base::FilePath(crx_path));
183 else
184 SetScreenSaverPath(base::FilePath());
185
186 UpdateExtensionLoader();
187 }
188
UpdateExtensionLoader()189 void AppPackUpdater::UpdateExtensionLoader() {
190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
191 if (!extension_loader_) {
192 VLOG(1) << "No AppPack loader created yet, not pushing extensions.";
193 return;
194 }
195
196 scoped_ptr<base::DictionaryValue> prefs(
197 external_cache_.cached_extensions()->DeepCopy());
198
199 // The screensaver isn't installed into the Profile.
200 prefs->Remove(screen_saver_id_, NULL);
201
202 extension_loader_->SetCurrentAppPackExtensions(prefs.Pass());
203 }
204
OnDamagedFileDetected(const base::FilePath & path)205 void AppPackUpdater::OnDamagedFileDetected(const base::FilePath& path) {
206 external_cache_.OnDamagedFileDetected(path);
207 }
208
SetScreenSaverPath(const base::FilePath & path)209 void AppPackUpdater::SetScreenSaverPath(const base::FilePath& path) {
210 // Don't invoke the callback if the path isn't changing.
211 if (path != screen_saver_path_) {
212 screen_saver_path_ = path;
213 if (!screen_saver_update_callback_.is_null())
214 screen_saver_update_callback_.Run(screen_saver_path_);
215 }
216 }
217
218 } // namespace policy
219