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