blob: 1c49d262e8ddcb518fccc5077a33f1370441d2f9 [file] [log] [blame]
[email protected]de0fdca22014-08-19 05:26:091// Copyright 2014 The Chromium Authors. All rights reserved.
[email protected]e8f96ff2011-08-03 05:07:332// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]de0fdca22014-08-19 05:26:095#include "components/component_updater/component_updater_service.h"
[email protected]e8f96ff2011-08-03 05:07:336
7#include <algorithm>
sorin7c717622015-05-26 19:59:098#include <map>
9#include <string>
10#include <utility>
[email protected]e8f96ff2011-08-03 05:07:3311#include <vector>
12
[email protected]44da56e2011-11-21 19:59:1413#include "base/bind.h"
sorin395c2ac2014-09-16 21:31:0714#include "base/bind_helpers.h"
[email protected]78efe2e92014-08-08 15:53:2215#include "base/callback.h"
[email protected]e3e696d32013-06-21 20:41:3616#include "base/compiler_specific.h"
[email protected]57999812013-02-24 05:40:5217#include "base/files/file_path.h"
thestig819adcc82014-09-10 22:24:5318#include "base/files/file_util.h"
[email protected]e8f96ff2011-08-03 05:07:3319#include "base/logging.h"
sorin5cb1f5492014-09-23 04:07:4420#include "base/macros.h"
[email protected]7226b33c2011-08-18 08:44:2221#include "base/memory/scoped_ptr.h"
[email protected]f5d27e32014-01-31 06:48:5322#include "base/sequenced_task_runner.h"
sorin7c717622015-05-26 19:59:0923#include "base/single_thread_task_runner.h"
24#include "base/thread_task_runner_handle.h"
[email protected]8f5f2ea2013-10-31 09:39:1025#include "base/threading/sequenced_worker_pool.h"
[email protected]ed6fb982014-07-23 16:56:5226#include "base/threading/thread_checker.h"
[email protected]41a17c52013-06-28 00:27:5327#include "base/timer/timer.h"
sorin7c717622015-05-26 19:59:0928#include "components/component_updater/component_updater_service_internal.h"
29#include "components/component_updater/timer.h"
sorin52ac0882015-01-24 01:15:0030#include "components/update_client/configurator.h"
sorin52ac0882015-01-24 01:15:0031#include "components/update_client/crx_update_item.h"
sorin52ac0882015-01-24 01:15:0032#include "components/update_client/update_client.h"
sorin52ac0882015-01-24 01:15:0033#include "components/update_client/utils.h"
[email protected]761fa4702013-07-02 15:25:1534#include "url/gurl.h"
[email protected]e8f96ff2011-08-03 05:07:3335
sorin7c717622015-05-26 19:59:0936using CrxInstaller = update_client::CrxInstaller;
37using UpdateClient = update_client::UpdateClient;
sorin52ac0882015-01-24 01:15:0038
[email protected]055981f2014-01-17 20:22:3239namespace component_updater {
[email protected]3a0092d2013-12-18 03:04:3540
sorin7c717622015-05-26 19:59:0941CrxUpdateService::CrxUpdateService(
42 const scoped_refptr<Configurator>& config,
43 const scoped_refptr<UpdateClient>& update_client)
[email protected]e8f96ff2011-08-03 05:07:3344 : config_(config),
sorin7c717622015-05-26 19:59:0945 update_client_(update_client),
46 blocking_task_runner_(config->GetSequencedTaskRunner()) {
47 AddObserver(this);
[email protected]1ea21ad2013-09-02 04:20:3948}
[email protected]e8f96ff2011-08-03 05:07:3349
50CrxUpdateService::~CrxUpdateService() {
sorin7c717622015-05-26 19:59:0951 DCHECK(thread_checker_.CalledOnValidThread());
52
53 for (const auto item : ready_callbacks_) {
54 item.second.Run();
55 }
56
57 RemoveObserver(this);
58
[email protected]e8f96ff2011-08-03 05:07:3359 Stop();
[email protected]1ea21ad2013-09-02 04:20:3960}
[email protected]e8f96ff2011-08-03 05:07:3361
[email protected]d3268fe2014-04-25 02:14:2362void CrxUpdateService::AddObserver(Observer* observer) {
[email protected]ed6fb982014-07-23 16:56:5263 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:0964 update_client_->AddObserver(observer);
[email protected]d3268fe2014-04-25 02:14:2365}
66
67void CrxUpdateService::RemoveObserver(Observer* observer) {
[email protected]ed6fb982014-07-23 16:56:5268 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:0969 update_client_->RemoveObserver(observer);
[email protected]d3268fe2014-04-25 02:14:2370}
71
sorin7c717622015-05-26 19:59:0972void CrxUpdateService::Start() {
73 DCHECK(thread_checker_.CalledOnValidThread());
74 VLOG(1) << "CrxUpdateService starting up. "
75 << "First update attempt will take place in "
76 << config_->InitialDelay() << " seconds. "
77 << "Next update attempt will take place in "
78 << config_->NextCheckDelay() << " seconds. ";
[email protected]e8f96ff2011-08-03 05:07:3379
sorin7c717622015-05-26 19:59:0980 timer_.Start(
81 base::TimeDelta::FromSeconds(config_->InitialDelay()),
82 base::TimeDelta::FromSeconds(config_->NextCheckDelay()),
83 base::Bind(base::IgnoreResult(&CrxUpdateService::CheckForUpdates),
84 base::Unretained(this)));
[email protected]e8f96ff2011-08-03 05:07:3385}
86
sorin7c717622015-05-26 19:59:0987// Stops the update loop. In flight operations will be completed.
88void CrxUpdateService::Stop() {
89 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]fb53e652014-04-30 11:27:1990 VLOG(1) << "CrxUpdateService stopping";
[email protected]e8f96ff2011-08-03 05:07:3391 timer_.Stop();
sorinecaad3e2015-11-13 19:15:5292 update_client_->Stop();
[email protected]e8f96ff2011-08-03 05:07:3393}
94
95// Adds a component to be checked for upgrades. If the component exists it
sorin7c717622015-05-26 19:59:0996// it will be replaced.
97bool CrxUpdateService::RegisterComponent(const CrxComponent& component) {
[email protected]ed6fb982014-07-23 16:56:5298 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]d0c8b8b42014-05-06 05:11:4599 if (component.pk_hash.empty() || !component.version.IsValid() ||
sorin7c717622015-05-26 19:59:09100 !component.installer) {
101 return false;
[email protected]e8f96ff2011-08-03 05:07:33102 }
103
sorin7c717622015-05-26 19:59:09104 // Update the registration data if the component has been registered before.
105 const std::string id(GetCrxComponentID(component));
106 auto it = components_.find(id);
107 if (it != components_.end()) {
108 it->second = component;
109 return true;
[email protected]10bc0222014-06-11 13:44:38110 }
[email protected]e8f96ff2011-08-03 05:07:33111
sorin7c717622015-05-26 19:59:09112 components_.insert(std::make_pair(id, component));
113 components_order_.push_back(id);
114
115 // Create an initial state for this component. The state is mutated in
116 // response to events from the UpdateClient instance.
117 CrxUpdateItem item;
118 item.id = id;
119 item.component = component;
120 const auto inserted = component_states_.insert(std::make_pair(id, item));
121 DCHECK(inserted.second);
122
123 // Start the timer if this is the first component registered. The first timer
124 // event occurs after an interval defined by the component update
125 // configurator. The subsequent timer events are repeated with a period
126 // defined by the same configurator.
127 if (components_.size() == 1)
128 Start();
129
130 return true;
[email protected]e8f96ff2011-08-03 05:07:33131}
132
sorin7c717622015-05-26 19:59:09133bool CrxUpdateService::UnregisterComponent(const std::string& id) {
134 DCHECK(thread_checker_.CalledOnValidThread());
135 auto it = components_.find(id);
136 if (it == components_.end())
137 return false;
bauerb1f6657e72015-02-09 00:00:27138
sorin7c717622015-05-26 19:59:09139 DCHECK_EQ(id, it->first);
bauerb1f6657e72015-02-09 00:00:27140
sorin7c717622015-05-26 19:59:09141 // Delay the uninstall of the component if the component is being updated.
142 if (update_client_->IsUpdating(id)) {
143 components_pending_unregistration_.push_back(id);
144 return true;
145 }
146
147 return DoUnregisterComponent(it->second);
148}
149
150bool CrxUpdateService::DoUnregisterComponent(const CrxComponent& component) {
151 DCHECK(thread_checker_.CalledOnValidThread());
152
153 const auto id = GetCrxComponentID(component);
154 DCHECK(ready_callbacks_.find(id) == ready_callbacks_.end());
155
156 const bool result = component.installer->Uninstall();
157
158 const auto pos =
159 std::find(components_order_.begin(), components_order_.end(), id);
160 if (pos != components_order_.end())
161 components_order_.erase(pos);
162
163 components_.erase(id);
164 component_states_.erase(id);
165
166 return result;
bauerb1f6657e72015-02-09 00:00:27167}
168
[email protected]68bf09e2014-06-03 00:10:56169std::vector<std::string> CrxUpdateService::GetComponentIDs() const {
[email protected]ed6fb982014-07-23 16:56:52170 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:09171 std::vector<std::string> ids;
172 for (const auto& it : components_)
173 ids.push_back(it.first);
174 return ids;
[email protected]68bf09e2014-06-03 00:10:56175}
176
[email protected]78efe2e92014-08-08 15:53:22177OnDemandUpdater& CrxUpdateService::GetOnDemandUpdater() {
sorin7c717622015-05-26 19:59:09178 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]78efe2e92014-08-08 15:53:22179 return *this;
180}
181
sorin7c717622015-05-26 19:59:09182const CrxComponent* CrxUpdateService::GetComponent(
183 const std::string& id) const {
184 DCHECK(thread_checker_.CalledOnValidThread());
185 const auto it(components_.find(id));
186 return it != components_.end() ? &(it->second) : NULL;
187}
188
189const CrxUpdateItem* CrxUpdateService::GetComponentState(
190 const std::string& id) const {
191 DCHECK(thread_checker_.CalledOnValidThread());
192 const auto it(component_states_.find(id));
193 return it != component_states_.end() ? &it->second : NULL;
194}
195
196void CrxUpdateService::MaybeThrottle(const std::string& id,
[email protected]78efe2e92014-08-08 15:53:22197 const base::Closure& callback) {
198 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:09199 auto it = components_.find(id);
200 if (it != components_.end()) {
201 DCHECK_EQ(it->first, id);
202 if (OnDemandUpdateWithCooldown(id)) {
203 ready_callbacks_.insert(std::make_pair(id, callback));
204 return;
205 }
[email protected]78efe2e92014-08-08 15:53:22206 }
sorin7c717622015-05-26 19:59:09207
208 callback.Run(); // Unblock the request if the request can't be throttled.
209}
210
211bool CrxUpdateService::OnDemandUpdate(const std::string& id) {
212 DCHECK(thread_checker_.CalledOnValidThread());
213
214 if (!GetComponent(id))
215 return false;
216
217 return OnDemandUpdateInternal(id);
218}
219
220bool CrxUpdateService::OnDemandUpdateWithCooldown(const std::string& id) {
221 DCHECK(thread_checker_.CalledOnValidThread());
222
223 DCHECK(GetComponent(id));
224
225 // Check if the request is too soon.
226 const auto component_state(GetComponentState(id));
227 if (component_state) {
228 base::TimeDelta delta = base::Time::Now() - component_state->last_check;
229 if (delta < base::TimeDelta::FromSeconds(config_->OnDemandDelay()))
230 return false;
231 }
232
233 return OnDemandUpdateInternal(id);
234}
235
236bool CrxUpdateService::OnDemandUpdateInternal(const std::string& id) {
237 DCHECK(thread_checker_.CalledOnValidThread());
238
239 update_client_->Install(
240 id, base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)),
241 base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this)));
242
243 return true;
244}
245
246bool CrxUpdateService::CheckForUpdates() {
247 DCHECK(thread_checker_.CalledOnValidThread());
248 std::vector<std::string> ids;
249 for (const auto id : components_order_) {
250 DCHECK(components_.find(id) != components_.end());
251 ids.push_back(id);
252 }
253
254 update_client_->Update(
255 ids, base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)),
256 base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this)));
257
258 return true;
[email protected]78efe2e92014-08-08 15:53:22259}
260
261scoped_refptr<base::SequencedTaskRunner>
262CrxUpdateService::GetSequencedTaskRunner() {
sorin7c717622015-05-26 19:59:09263 DCHECK(thread_checker_.CalledOnValidThread());
bauerb1f6657e72015-02-09 00:00:27264 return blocking_task_runner_;
[email protected]78efe2e92014-08-08 15:53:22265}
266
sorin7c717622015-05-26 19:59:09267bool CrxUpdateService::GetComponentDetails(const std::string& id,
[email protected]f392e372014-06-12 07:25:57268 CrxUpdateItem* item) const {
[email protected]ed6fb982014-07-23 16:56:52269 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:09270
271 // First, if this component is currently being updated, return its state from
272 // the update client.
273 if (update_client_->GetCrxUpdateState(id, item))
274 return true;
275
276 // Otherwise, return the last seen state of the component, if such a
277 // state exists.
278 const auto component_states_it = component_states_.find(id);
279 if (component_states_it != component_states_.end()) {
280 *item = component_states_it->second;
281 return true;
282 }
283
284 return false;
[email protected]2e919ddd2013-08-21 05:05:17285}
286
sorin7c717622015-05-26 19:59:09287void CrxUpdateService::OnUpdate(const std::vector<std::string>& ids,
288 std::vector<CrxComponent>* components) {
289 DCHECK(thread_checker_.CalledOnValidThread());
290 DCHECK(components->empty());
291
292 for (const auto& id : ids) {
293 const auto registered_component(GetComponent(id));
294 if (registered_component) {
295 components->push_back(*registered_component);
296 }
297 }
[email protected]ed6fb982014-07-23 16:56:52298}
299
sorin7c717622015-05-26 19:59:09300void CrxUpdateService::OnUpdateComplete(int error) {
301 DCHECK(thread_checker_.CalledOnValidThread());
302 VLOG(1) << "Update completed with error " << error;
303
304 for (const auto id : components_pending_unregistration_) {
305 if (!update_client_->IsUpdating(id)) {
306 const auto component = GetComponent(id);
307 if (component)
308 DoUnregisterComponent(*component);
309 }
310 }
311}
312
313void CrxUpdateService::OnEvent(Events event, const std::string& id) {
[email protected]ed6fb982014-07-23 16:56:52314 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]93e8e2c2014-01-04 12:29:23315
sorin7c717622015-05-26 19:59:09316 // Unblock all throttles for the component.
317 if (event == Observer::Events::COMPONENT_UPDATED ||
318 event == Observer::Events::COMPONENT_NOT_UPDATED) {
319 auto callbacks = ready_callbacks_.equal_range(id);
320 for (auto it = callbacks.first; it != callbacks.second; ++it) {
321 it->second.Run();
322 }
323 ready_callbacks_.erase(id);
324 }
325
326 CrxUpdateItem update_item;
327 if (!update_client_->GetCrxUpdateState(id, &update_item))
[email protected]e8f96ff2011-08-03 05:07:33328 return;
[email protected]93e8e2c2014-01-04 12:29:23329
sorin7c717622015-05-26 19:59:09330 // Update the state of the item.
331 auto it = component_states_.find(id);
332 DCHECK(it != component_states_.end());
333 it->second = update_item;
bauerb1f6657e72015-02-09 00:00:27334
sorin7c717622015-05-26 19:59:09335 // Update the component registration with the new version.
336 if (event == Observer::Events::COMPONENT_UPDATED) {
337 auto component(const_cast<CrxComponent*>(GetComponent(id)));
338 if (component) {
339 component->version = update_item.next_version;
340 component->fingerprint = update_item.next_fp;
bauerb1f6657e72015-02-09 00:00:27341 }
342 }
[email protected]68bf09e2014-06-03 00:10:56343}
344
[email protected]00a77fa2013-11-02 04:18:46345///////////////////////////////////////////////////////////////////////////////
346
[email protected]e8f96ff2011-08-03 05:07:33347// The component update factory. Using the component updater as a singleton
348// is the job of the browser process.
sorin7c717622015-05-26 19:59:09349// TODO(sorin): consider making this a singleton.
sorin9797aba2015-04-17 17:15:03350scoped_ptr<ComponentUpdateService> ComponentUpdateServiceFactory(
351 const scoped_refptr<Configurator>& config) {
[email protected]e8f96ff2011-08-03 05:07:33352 DCHECK(config);
sorin7c717622015-05-26 19:59:09353 auto update_client = update_client::UpdateClientFactory(config);
354 return scoped_ptr<ComponentUpdateService>(
355 new CrxUpdateService(config, update_client.Pass()));
[email protected]e8f96ff2011-08-03 05:07:33356}
[email protected]2cddef42013-11-22 08:23:22357
[email protected]055981f2014-01-17 20:22:32358} // namespace component_updater