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