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 : 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 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