blob: 8f0515bda3760619c54dea4cdda42e0b50419804 [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]57999812013-02-24 05:40:5216#include "base/files/file_path.h"
thestig819adcc82014-09-10 22:24:5317#include "base/files/file_util.h"
[email protected]e8f96ff2011-08-03 05:07:3318#include "base/logging.h"
sorin5cb1f5492014-09-23 04:07:4419#include "base/macros.h"
[email protected]7226b33c2011-08-18 08:44:2220#include "base/memory/scoped_ptr.h"
sorin85953dc2016-03-10 00:32:4821#include "base/metrics/histogram_macros.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"
sorin85953dc2016-03-10 00:32:4827#include "base/time/time.h"
[email protected]41a17c52013-06-28 00:27:5328#include "base/timer/timer.h"
sorin7c717622015-05-26 19:59:0929#include "components/component_updater/component_updater_service_internal.h"
30#include "components/component_updater/timer.h"
sorin52ac0882015-01-24 01:15:0031#include "components/update_client/configurator.h"
sorin52ac0882015-01-24 01:15:0032#include "components/update_client/crx_update_item.h"
sorin52ac0882015-01-24 01:15:0033#include "components/update_client/update_client.h"
sorin52ac0882015-01-24 01:15:0034#include "components/update_client/utils.h"
[email protected]761fa4702013-07-02 15:25:1535#include "url/gurl.h"
[email protected]e8f96ff2011-08-03 05:07:3336
sorin7c717622015-05-26 19:59:0937using CrxInstaller = update_client::CrxInstaller;
38using UpdateClient = update_client::UpdateClient;
sorin52ac0882015-01-24 01:15:0039
sorin85953dc2016-03-10 00:32:4840namespace {
41
42enum UpdateType {
43 UPDATE_TYPE_MANUAL = 0,
44 UPDATE_TYPE_AUTOMATIC,
45 UPDATE_TYPE_COUNT,
46};
47
48} // namespace
49
[email protected]055981f2014-01-17 20:22:3250namespace component_updater {
[email protected]3a0092d2013-12-18 03:04:3551
sorin7c717622015-05-26 19:59:0952CrxUpdateService::CrxUpdateService(
53 const scoped_refptr<Configurator>& config,
54 const scoped_refptr<UpdateClient>& update_client)
[email protected]e8f96ff2011-08-03 05:07:3355 : config_(config),
sorin7c717622015-05-26 19:59:0956 update_client_(update_client),
57 blocking_task_runner_(config->GetSequencedTaskRunner()) {
58 AddObserver(this);
[email protected]1ea21ad2013-09-02 04:20:3959}
[email protected]e8f96ff2011-08-03 05:07:3360
61CrxUpdateService::~CrxUpdateService() {
sorin7c717622015-05-26 19:59:0962 DCHECK(thread_checker_.CalledOnValidThread());
63
64 for (const auto item : ready_callbacks_) {
65 item.second.Run();
66 }
67
68 RemoveObserver(this);
69
[email protected]e8f96ff2011-08-03 05:07:3370 Stop();
[email protected]1ea21ad2013-09-02 04:20:3971}
[email protected]e8f96ff2011-08-03 05:07:3372
[email protected]d3268fe2014-04-25 02:14:2373void CrxUpdateService::AddObserver(Observer* observer) {
[email protected]ed6fb982014-07-23 16:56:5274 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:0975 update_client_->AddObserver(observer);
[email protected]d3268fe2014-04-25 02:14:2376}
77
78void CrxUpdateService::RemoveObserver(Observer* observer) {
[email protected]ed6fb982014-07-23 16:56:5279 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:0980 update_client_->RemoveObserver(observer);
[email protected]d3268fe2014-04-25 02:14:2381}
82
sorin7c717622015-05-26 19:59:0983void CrxUpdateService::Start() {
84 DCHECK(thread_checker_.CalledOnValidThread());
85 VLOG(1) << "CrxUpdateService starting up. "
86 << "First update attempt will take place in "
87 << config_->InitialDelay() << " seconds. "
88 << "Next update attempt will take place in "
89 << config_->NextCheckDelay() << " seconds. ";
[email protected]e8f96ff2011-08-03 05:07:3390
sorin7c717622015-05-26 19:59:0991 timer_.Start(
92 base::TimeDelta::FromSeconds(config_->InitialDelay()),
93 base::TimeDelta::FromSeconds(config_->NextCheckDelay()),
94 base::Bind(base::IgnoreResult(&CrxUpdateService::CheckForUpdates),
95 base::Unretained(this)));
[email protected]e8f96ff2011-08-03 05:07:3396}
97
sorin7c717622015-05-26 19:59:0998// Stops the update loop. In flight operations will be completed.
99void CrxUpdateService::Stop() {
100 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]fb53e652014-04-30 11:27:19101 VLOG(1) << "CrxUpdateService stopping";
[email protected]e8f96ff2011-08-03 05:07:33102 timer_.Stop();
sorinecaad3e2015-11-13 19:15:52103 update_client_->Stop();
[email protected]e8f96ff2011-08-03 05:07:33104}
105
106// Adds a component to be checked for upgrades. If the component exists it
sorin7c717622015-05-26 19:59:09107// it will be replaced.
108bool CrxUpdateService::RegisterComponent(const CrxComponent& component) {
[email protected]ed6fb982014-07-23 16:56:52109 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]d0c8b8b42014-05-06 05:11:45110 if (component.pk_hash.empty() || !component.version.IsValid() ||
sorin7c717622015-05-26 19:59:09111 !component.installer) {
112 return false;
[email protected]e8f96ff2011-08-03 05:07:33113 }
114
sorin7c717622015-05-26 19:59:09115 // Update the registration data if the component has been registered before.
116 const std::string id(GetCrxComponentID(component));
117 auto it = components_.find(id);
118 if (it != components_.end()) {
119 it->second = component;
120 return true;
[email protected]10bc0222014-06-11 13:44:38121 }
[email protected]e8f96ff2011-08-03 05:07:33122
sorin7c717622015-05-26 19:59:09123 components_.insert(std::make_pair(id, component));
124 components_order_.push_back(id);
125
126 // Create an initial state for this component. The state is mutated in
127 // response to events from the UpdateClient instance.
128 CrxUpdateItem item;
129 item.id = id;
130 item.component = component;
131 const auto inserted = component_states_.insert(std::make_pair(id, item));
132 DCHECK(inserted.second);
133
134 // Start the timer if this is the first component registered. The first timer
135 // event occurs after an interval defined by the component update
136 // configurator. The subsequent timer events are repeated with a period
137 // defined by the same configurator.
138 if (components_.size() == 1)
139 Start();
140
141 return true;
[email protected]e8f96ff2011-08-03 05:07:33142}
143
sorin7c717622015-05-26 19:59:09144bool CrxUpdateService::UnregisterComponent(const std::string& id) {
145 DCHECK(thread_checker_.CalledOnValidThread());
146 auto it = components_.find(id);
147 if (it == components_.end())
148 return false;
bauerb1f6657e72015-02-09 00:00:27149
sorin7c717622015-05-26 19:59:09150 DCHECK_EQ(id, it->first);
bauerb1f6657e72015-02-09 00:00:27151
sorin7c717622015-05-26 19:59:09152 // Delay the uninstall of the component if the component is being updated.
153 if (update_client_->IsUpdating(id)) {
154 components_pending_unregistration_.push_back(id);
155 return true;
156 }
157
158 return DoUnregisterComponent(it->second);
159}
160
161bool CrxUpdateService::DoUnregisterComponent(const CrxComponent& component) {
162 DCHECK(thread_checker_.CalledOnValidThread());
163
164 const auto id = GetCrxComponentID(component);
165 DCHECK(ready_callbacks_.find(id) == ready_callbacks_.end());
166
167 const bool result = component.installer->Uninstall();
168
169 const auto pos =
170 std::find(components_order_.begin(), components_order_.end(), id);
171 if (pos != components_order_.end())
172 components_order_.erase(pos);
173
174 components_.erase(id);
175 component_states_.erase(id);
176
177 return result;
bauerb1f6657e72015-02-09 00:00:27178}
179
[email protected]68bf09e2014-06-03 00:10:56180std::vector<std::string> CrxUpdateService::GetComponentIDs() const {
[email protected]ed6fb982014-07-23 16:56:52181 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:09182 std::vector<std::string> ids;
183 for (const auto& it : components_)
184 ids.push_back(it.first);
185 return ids;
[email protected]68bf09e2014-06-03 00:10:56186}
187
[email protected]78efe2e92014-08-08 15:53:22188OnDemandUpdater& CrxUpdateService::GetOnDemandUpdater() {
sorin7c717622015-05-26 19:59:09189 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]78efe2e92014-08-08 15:53:22190 return *this;
191}
192
sorin7c717622015-05-26 19:59:09193const CrxComponent* CrxUpdateService::GetComponent(
194 const std::string& id) const {
195 DCHECK(thread_checker_.CalledOnValidThread());
196 const auto it(components_.find(id));
197 return it != components_.end() ? &(it->second) : NULL;
198}
199
200const CrxUpdateItem* CrxUpdateService::GetComponentState(
201 const std::string& id) const {
202 DCHECK(thread_checker_.CalledOnValidThread());
203 const auto it(component_states_.find(id));
204 return it != component_states_.end() ? &it->second : NULL;
205}
206
207void CrxUpdateService::MaybeThrottle(const std::string& id,
[email protected]78efe2e92014-08-08 15:53:22208 const base::Closure& callback) {
209 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:09210 auto it = components_.find(id);
211 if (it != components_.end()) {
212 DCHECK_EQ(it->first, id);
213 if (OnDemandUpdateWithCooldown(id)) {
214 ready_callbacks_.insert(std::make_pair(id, callback));
215 return;
216 }
[email protected]78efe2e92014-08-08 15:53:22217 }
sorin7c717622015-05-26 19:59:09218
219 callback.Run(); // Unblock the request if the request can't be throttled.
220}
221
222bool CrxUpdateService::OnDemandUpdate(const std::string& id) {
223 DCHECK(thread_checker_.CalledOnValidThread());
224
225 if (!GetComponent(id))
226 return false;
227
228 return OnDemandUpdateInternal(id);
229}
230
231bool CrxUpdateService::OnDemandUpdateWithCooldown(const std::string& id) {
232 DCHECK(thread_checker_.CalledOnValidThread());
233
234 DCHECK(GetComponent(id));
235
236 // Check if the request is too soon.
237 const auto component_state(GetComponentState(id));
238 if (component_state) {
239 base::TimeDelta delta = base::Time::Now() - component_state->last_check;
240 if (delta < base::TimeDelta::FromSeconds(config_->OnDemandDelay()))
241 return false;
242 }
243
244 return OnDemandUpdateInternal(id);
245}
246
247bool CrxUpdateService::OnDemandUpdateInternal(const std::string& id) {
248 DCHECK(thread_checker_.CalledOnValidThread());
249
sorin85953dc2016-03-10 00:32:48250 UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.Calls", UPDATE_TYPE_MANUAL,
251 UPDATE_TYPE_COUNT);
252
sorin7c717622015-05-26 19:59:09253 update_client_->Install(
254 id, base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)),
sorin85953dc2016-03-10 00:32:48255 base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this),
256 base::TimeTicks::Now()));
sorin7c717622015-05-26 19:59:09257
258 return true;
259}
260
261bool CrxUpdateService::CheckForUpdates() {
262 DCHECK(thread_checker_.CalledOnValidThread());
sorin85953dc2016-03-10 00:32:48263
264 UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.Calls", UPDATE_TYPE_AUTOMATIC,
265 UPDATE_TYPE_COUNT);
266
sorin7c717622015-05-26 19:59:09267 std::vector<std::string> ids;
268 for (const auto id : components_order_) {
269 DCHECK(components_.find(id) != components_.end());
270 ids.push_back(id);
271 }
272
273 update_client_->Update(
274 ids, base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)),
sorin85953dc2016-03-10 00:32:48275 base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this),
276 base::TimeTicks::Now()));
sorin7c717622015-05-26 19:59:09277
278 return true;
[email protected]78efe2e92014-08-08 15:53:22279}
280
281scoped_refptr<base::SequencedTaskRunner>
282CrxUpdateService::GetSequencedTaskRunner() {
sorin7c717622015-05-26 19:59:09283 DCHECK(thread_checker_.CalledOnValidThread());
bauerb1f6657e72015-02-09 00:00:27284 return blocking_task_runner_;
[email protected]78efe2e92014-08-08 15:53:22285}
286
sorin7c717622015-05-26 19:59:09287bool CrxUpdateService::GetComponentDetails(const std::string& id,
[email protected]f392e372014-06-12 07:25:57288 CrxUpdateItem* item) const {
[email protected]ed6fb982014-07-23 16:56:52289 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:09290
291 // First, if this component is currently being updated, return its state from
292 // the update client.
293 if (update_client_->GetCrxUpdateState(id, item))
294 return true;
295
296 // Otherwise, return the last seen state of the component, if such a
297 // state exists.
298 const auto component_states_it = component_states_.find(id);
299 if (component_states_it != component_states_.end()) {
300 *item = component_states_it->second;
301 return true;
302 }
303
304 return false;
[email protected]2e919ddd2013-08-21 05:05:17305}
306
sorin7c717622015-05-26 19:59:09307void CrxUpdateService::OnUpdate(const std::vector<std::string>& ids,
308 std::vector<CrxComponent>* components) {
309 DCHECK(thread_checker_.CalledOnValidThread());
310 DCHECK(components->empty());
311
312 for (const auto& id : ids) {
313 const auto registered_component(GetComponent(id));
314 if (registered_component) {
315 components->push_back(*registered_component);
316 }
317 }
[email protected]ed6fb982014-07-23 16:56:52318}
319
sorin85953dc2016-03-10 00:32:48320void CrxUpdateService::OnUpdateComplete(const base::TimeTicks& start_time,
321 int error) {
sorin7c717622015-05-26 19:59:09322 DCHECK(thread_checker_.CalledOnValidThread());
323 VLOG(1) << "Update completed with error " << error;
324
sorin85953dc2016-03-10 00:32:48325 UMA_HISTOGRAM_BOOLEAN("ComponentUpdater.UpdateCompleteResult", error != 0);
326 UMA_HISTOGRAM_LONG_TIMES_100("ComponentUpdater.UpdateCompleteTime",
327 base::TimeTicks::Now() - start_time);
328
sorin7c717622015-05-26 19:59:09329 for (const auto id : components_pending_unregistration_) {
330 if (!update_client_->IsUpdating(id)) {
331 const auto component = GetComponent(id);
332 if (component)
333 DoUnregisterComponent(*component);
334 }
335 }
336}
337
338void CrxUpdateService::OnEvent(Events event, const std::string& id) {
[email protected]ed6fb982014-07-23 16:56:52339 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]93e8e2c2014-01-04 12:29:23340
sorin7c717622015-05-26 19:59:09341 // Unblock all throttles for the component.
342 if (event == Observer::Events::COMPONENT_UPDATED ||
343 event == Observer::Events::COMPONENT_NOT_UPDATED) {
344 auto callbacks = ready_callbacks_.equal_range(id);
345 for (auto it = callbacks.first; it != callbacks.second; ++it) {
346 it->second.Run();
347 }
348 ready_callbacks_.erase(id);
349 }
350
351 CrxUpdateItem update_item;
352 if (!update_client_->GetCrxUpdateState(id, &update_item))
[email protected]e8f96ff2011-08-03 05:07:33353 return;
[email protected]93e8e2c2014-01-04 12:29:23354
sorin7c717622015-05-26 19:59:09355 // Update the state of the item.
356 auto it = component_states_.find(id);
357 DCHECK(it != component_states_.end());
358 it->second = update_item;
bauerb1f6657e72015-02-09 00:00:27359
sorin7c717622015-05-26 19:59:09360 // Update the component registration with the new version.
361 if (event == Observer::Events::COMPONENT_UPDATED) {
362 auto component(const_cast<CrxComponent*>(GetComponent(id)));
363 if (component) {
364 component->version = update_item.next_version;
365 component->fingerprint = update_item.next_fp;
bauerb1f6657e72015-02-09 00:00:27366 }
367 }
[email protected]68bf09e2014-06-03 00:10:56368}
369
[email protected]00a77fa2013-11-02 04:18:46370///////////////////////////////////////////////////////////////////////////////
371
[email protected]e8f96ff2011-08-03 05:07:33372// The component update factory. Using the component updater as a singleton
373// is the job of the browser process.
sorin7c717622015-05-26 19:59:09374// TODO(sorin): consider making this a singleton.
sorin9797aba2015-04-17 17:15:03375scoped_ptr<ComponentUpdateService> ComponentUpdateServiceFactory(
376 const scoped_refptr<Configurator>& config) {
[email protected]e8f96ff2011-08-03 05:07:33377 DCHECK(config);
sorin7c717622015-05-26 19:59:09378 auto update_client = update_client::UpdateClientFactory(config);
379 return scoped_ptr<ComponentUpdateService>(
danakj4b041ab2015-12-04 20:12:27380 new CrxUpdateService(config, std::move(update_client)));
[email protected]e8f96ff2011-08-03 05:07:33381}
[email protected]2cddef42013-11-22 08:23:22382
[email protected]055981f2014-01-17 20:22:32383} // namespace component_updater