Home | History | Annotate | Download | only in policy
      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:
     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.
     41   void SetCurrentAppPackExtensions(scoped_ptr<base::DictionaryValue> prefs) {
     42     app_pack_prefs_.Swap(prefs.get());
     43     StartLoading();
     44   }
     45 
     46   // Implementation of extensions::ExternalLoader:
     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:
     55   virtual ~AppPackExternalLoader() {}
     56 
     57  private:
     58   base::DictionaryValue app_pack_prefs_;
     59 
     60   DISALLOW_COPY_AND_ASSIGN(AppPackExternalLoader);
     61 };
     62 
     63 AppPackUpdater::AppPackUpdater(net::URLRequestContextGetter* request_context,
     64                                EnterpriseInstallAttributes* install_attributes)
     65     : weak_ptr_factory_(this),
     66       created_extension_loader_(false),
     67       install_attributes_(install_attributes),
     68       external_cache_(base::FilePath(kAppPackCacheDir),
     69                       request_context,
     70                       content::BrowserThread::GetBlockingPool()->
     71                           GetSequencedTaskRunnerWithShutdownBehavior(
     72                               content::BrowserThread::GetBlockingPool()->
     73                                   GetSequenceToken(),
     74                               base::SequencedWorkerPool::SKIP_ON_SHUTDOWN),
     75                       this,
     76                       false,
     77                       false) {
     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 
     93 AppPackUpdater::~AppPackUpdater() {
     94 }
     95 
     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 
    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 
    122 void AppPackUpdater::AppPackChanged() {
    123   if (install_attributes_->GetMode() == DEVICE_MODE_RETAIL_KIOSK)
    124     LoadPolicy();
    125 }
    126 
    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 
    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 
    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 
    205 void AppPackUpdater::OnDamagedFileDetected(const base::FilePath& path) {
    206   external_cache_.OnDamagedFileDetected(path);
    207 }
    208 
    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