blob: 1f6684c11e6a5890049aac7f6bc3700efb5016b4 [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"
dchenga0ee5fb82016-04-26 02:46:5520#include "base/memory/ptr_util.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"
waffles77255cc2016-08-02 17:25:1224#include "base/strings/utf_string_conversions.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"
gab7966d312016-05-11 20:35:0127#include "base/threading/thread_task_runner_handle.h"
sorin85953dc2016-03-10 00:32:4828#include "base/time/time.h"
[email protected]41a17c52013-06-28 00:27:5329#include "base/timer/timer.h"
sorin7c717622015-05-26 19:59:0930#include "components/component_updater/component_updater_service_internal.h"
31#include "components/component_updater/timer.h"
sorin52ac0882015-01-24 01:15:0032#include "components/update_client/configurator.h"
sorin52ac0882015-01-24 01:15:0033#include "components/update_client/crx_update_item.h"
sorin52ac0882015-01-24 01:15:0034#include "components/update_client/update_client.h"
sorin52ac0882015-01-24 01:15:0035#include "components/update_client/utils.h"
[email protected]761fa4702013-07-02 15:25:1536#include "url/gurl.h"
[email protected]e8f96ff2011-08-03 05:07:3337
sorin7c717622015-05-26 19:59:0938using CrxInstaller = update_client::CrxInstaller;
39using UpdateClient = update_client::UpdateClient;
sorin52ac0882015-01-24 01:15:0040
sorin85953dc2016-03-10 00:32:4841namespace {
42
43enum UpdateType {
44 UPDATE_TYPE_MANUAL = 0,
45 UPDATE_TYPE_AUTOMATIC,
46 UPDATE_TYPE_COUNT,
47};
48
49} // namespace
50
[email protected]055981f2014-01-17 20:22:3251namespace component_updater {
[email protected]3a0092d2013-12-18 03:04:3552
waffles77255cc2016-08-02 17:25:1253ComponentInfo::ComponentInfo(const std::string& id, const base::string16& name)
54 : id(id), name(name) {}
55ComponentInfo::~ComponentInfo() {}
56
sorin7c717622015-05-26 19:59:0957CrxUpdateService::CrxUpdateService(
58 const scoped_refptr<Configurator>& config,
59 const scoped_refptr<UpdateClient>& update_client)
[email protected]e8f96ff2011-08-03 05:07:3360 : config_(config),
sorin7c717622015-05-26 19:59:0961 update_client_(update_client),
62 blocking_task_runner_(config->GetSequencedTaskRunner()) {
63 AddObserver(this);
[email protected]1ea21ad2013-09-02 04:20:3964}
[email protected]e8f96ff2011-08-03 05:07:3365
66CrxUpdateService::~CrxUpdateService() {
sorin7c717622015-05-26 19:59:0967 DCHECK(thread_checker_.CalledOnValidThread());
68
69 for (const auto item : ready_callbacks_) {
70 item.second.Run();
71 }
72
73 RemoveObserver(this);
74
[email protected]e8f96ff2011-08-03 05:07:3375 Stop();
[email protected]1ea21ad2013-09-02 04:20:3976}
[email protected]e8f96ff2011-08-03 05:07:3377
[email protected]d3268fe2014-04-25 02:14:2378void CrxUpdateService::AddObserver(Observer* observer) {
[email protected]ed6fb982014-07-23 16:56:5279 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:0980 update_client_->AddObserver(observer);
[email protected]d3268fe2014-04-25 02:14:2381}
82
83void CrxUpdateService::RemoveObserver(Observer* observer) {
[email protected]ed6fb982014-07-23 16:56:5284 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:0985 update_client_->RemoveObserver(observer);
[email protected]d3268fe2014-04-25 02:14:2386}
87
sorin7c717622015-05-26 19:59:0988void CrxUpdateService::Start() {
89 DCHECK(thread_checker_.CalledOnValidThread());
90 VLOG(1) << "CrxUpdateService starting up. "
91 << "First update attempt will take place in "
92 << config_->InitialDelay() << " seconds. "
93 << "Next update attempt will take place in "
94 << config_->NextCheckDelay() << " seconds. ";
[email protected]e8f96ff2011-08-03 05:07:3395
sorin7c717622015-05-26 19:59:0996 timer_.Start(
97 base::TimeDelta::FromSeconds(config_->InitialDelay()),
98 base::TimeDelta::FromSeconds(config_->NextCheckDelay()),
99 base::Bind(base::IgnoreResult(&CrxUpdateService::CheckForUpdates),
100 base::Unretained(this)));
[email protected]e8f96ff2011-08-03 05:07:33101}
102
sorin7c717622015-05-26 19:59:09103// Stops the update loop. In flight operations will be completed.
104void CrxUpdateService::Stop() {
105 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]fb53e652014-04-30 11:27:19106 VLOG(1) << "CrxUpdateService stopping";
[email protected]e8f96ff2011-08-03 05:07:33107 timer_.Stop();
sorinecaad3e2015-11-13 19:15:52108 update_client_->Stop();
[email protected]e8f96ff2011-08-03 05:07:33109}
110
111// Adds a component to be checked for upgrades. If the component exists it
sorin7c717622015-05-26 19:59:09112// it will be replaced.
113bool CrxUpdateService::RegisterComponent(const CrxComponent& component) {
[email protected]ed6fb982014-07-23 16:56:52114 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]d0c8b8b42014-05-06 05:11:45115 if (component.pk_hash.empty() || !component.version.IsValid() ||
sorin7c717622015-05-26 19:59:09116 !component.installer) {
117 return false;
[email protected]e8f96ff2011-08-03 05:07:33118 }
119
sorin7c717622015-05-26 19:59:09120 // Update the registration data if the component has been registered before.
121 const std::string id(GetCrxComponentID(component));
122 auto it = components_.find(id);
123 if (it != components_.end()) {
124 it->second = component;
125 return true;
[email protected]10bc0222014-06-11 13:44:38126 }
[email protected]e8f96ff2011-08-03 05:07:33127
sorin7c717622015-05-26 19:59:09128 components_.insert(std::make_pair(id, component));
129 components_order_.push_back(id);
waffles77255cc2016-08-02 17:25:12130 for (const auto& mime_type : component.handled_mime_types)
131 component_ids_by_mime_type_[mime_type] = id;
sorin7c717622015-05-26 19:59:09132
133 // Create an initial state for this component. The state is mutated in
134 // response to events from the UpdateClient instance.
135 CrxUpdateItem item;
136 item.id = id;
137 item.component = component;
138 const auto inserted = component_states_.insert(std::make_pair(id, item));
139 DCHECK(inserted.second);
140
141 // Start the timer if this is the first component registered. The first timer
142 // event occurs after an interval defined by the component update
143 // configurator. The subsequent timer events are repeated with a period
144 // defined by the same configurator.
145 if (components_.size() == 1)
146 Start();
147
148 return true;
[email protected]e8f96ff2011-08-03 05:07:33149}
150
sorin7c717622015-05-26 19:59:09151bool CrxUpdateService::UnregisterComponent(const std::string& id) {
152 DCHECK(thread_checker_.CalledOnValidThread());
153 auto it = components_.find(id);
154 if (it == components_.end())
155 return false;
bauerb1f6657e72015-02-09 00:00:27156
sorin7c717622015-05-26 19:59:09157 DCHECK_EQ(id, it->first);
bauerb1f6657e72015-02-09 00:00:27158
sorin7c717622015-05-26 19:59:09159 // Delay the uninstall of the component if the component is being updated.
160 if (update_client_->IsUpdating(id)) {
161 components_pending_unregistration_.push_back(id);
162 return true;
163 }
164
165 return DoUnregisterComponent(it->second);
166}
167
168bool CrxUpdateService::DoUnregisterComponent(const CrxComponent& component) {
169 DCHECK(thread_checker_.CalledOnValidThread());
170
171 const auto id = GetCrxComponentID(component);
172 DCHECK(ready_callbacks_.find(id) == ready_callbacks_.end());
173
174 const bool result = component.installer->Uninstall();
175
176 const auto pos =
177 std::find(components_order_.begin(), components_order_.end(), id);
178 if (pos != components_order_.end())
179 components_order_.erase(pos);
180
181 components_.erase(id);
182 component_states_.erase(id);
183
184 return result;
bauerb1f6657e72015-02-09 00:00:27185}
186
[email protected]68bf09e2014-06-03 00:10:56187std::vector<std::string> CrxUpdateService::GetComponentIDs() const {
[email protected]ed6fb982014-07-23 16:56:52188 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:09189 std::vector<std::string> ids;
190 for (const auto& it : components_)
191 ids.push_back(it.first);
192 return ids;
[email protected]68bf09e2014-06-03 00:10:56193}
194
waffles77255cc2016-08-02 17:25:12195std::unique_ptr<ComponentInfo> CrxUpdateService::GetComponentForMimeType(
196 const std::string& mime_type) const {
197 DCHECK(thread_checker_.CalledOnValidThread());
198 const auto it = component_ids_by_mime_type_.find(mime_type);
199 if (it == component_ids_by_mime_type_.end())
200 return nullptr;
201 const auto component = GetComponent(it->second);
202 if (!component)
203 return nullptr;
204 return base::MakeUnique<ComponentInfo>(GetCrxComponentID(*component),
205 base::UTF8ToUTF16(component->name));
206}
207
[email protected]78efe2e92014-08-08 15:53:22208OnDemandUpdater& CrxUpdateService::GetOnDemandUpdater() {
sorin7c717622015-05-26 19:59:09209 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]78efe2e92014-08-08 15:53:22210 return *this;
211}
212
sorin7c717622015-05-26 19:59:09213const CrxComponent* CrxUpdateService::GetComponent(
214 const std::string& id) const {
215 DCHECK(thread_checker_.CalledOnValidThread());
216 const auto it(components_.find(id));
217 return it != components_.end() ? &(it->second) : NULL;
218}
219
220const CrxUpdateItem* CrxUpdateService::GetComponentState(
221 const std::string& id) const {
222 DCHECK(thread_checker_.CalledOnValidThread());
223 const auto it(component_states_.find(id));
224 return it != component_states_.end() ? &it->second : NULL;
225}
226
227void CrxUpdateService::MaybeThrottle(const std::string& id,
[email protected]78efe2e92014-08-08 15:53:22228 const base::Closure& callback) {
229 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:09230 auto it = components_.find(id);
231 if (it != components_.end()) {
232 DCHECK_EQ(it->first, id);
233 if (OnDemandUpdateWithCooldown(id)) {
234 ready_callbacks_.insert(std::make_pair(id, callback));
235 return;
236 }
[email protected]78efe2e92014-08-08 15:53:22237 }
sorin7c717622015-05-26 19:59:09238
239 callback.Run(); // Unblock the request if the request can't be throttled.
240}
241
sorin4c520182016-08-19 17:27:44242void CrxUpdateService::OnDemandUpdate(const std::string& id,
243 CompletionCallback callback) {
sorin7c717622015-05-26 19:59:09244 DCHECK(thread_checker_.CalledOnValidThread());
245
sorin4c520182016-08-19 17:27:44246 if (!GetComponent(id)) {
247 base::ThreadTaskRunnerHandle::Get()->PostTask(
248 FROM_HERE,
249 base::Bind(callback,
250 update_client::Error::ERROR_UPDATE_INVALID_ARGUMENT));
251 return;
252 }
sorin7c717622015-05-26 19:59:09253
sorin4c520182016-08-19 17:27:44254 OnDemandUpdateInternal(id, callback);
sorin7c717622015-05-26 19:59:09255}
256
257bool CrxUpdateService::OnDemandUpdateWithCooldown(const std::string& id) {
258 DCHECK(thread_checker_.CalledOnValidThread());
259
260 DCHECK(GetComponent(id));
261
262 // Check if the request is too soon.
vmpstr2de366b2016-07-20 21:35:48263 const auto* component_state(GetComponentState(id));
sorin7c717622015-05-26 19:59:09264 if (component_state) {
265 base::TimeDelta delta = base::Time::Now() - component_state->last_check;
266 if (delta < base::TimeDelta::FromSeconds(config_->OnDemandDelay()))
267 return false;
268 }
269
sorin4c520182016-08-19 17:27:44270 OnDemandUpdateInternal(id, CompletionCallback());
271 return true;
sorin7c717622015-05-26 19:59:09272}
273
sorin4c520182016-08-19 17:27:44274void CrxUpdateService::OnDemandUpdateInternal(const std::string& id,
275 CompletionCallback callback) {
sorin7c717622015-05-26 19:59:09276 DCHECK(thread_checker_.CalledOnValidThread());
277
sorin85953dc2016-03-10 00:32:48278 UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.Calls", UPDATE_TYPE_MANUAL,
279 UPDATE_TYPE_COUNT);
sorin7c717622015-05-26 19:59:09280 update_client_->Install(
281 id, base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)),
sorin85953dc2016-03-10 00:32:48282 base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this),
sorin4c520182016-08-19 17:27:44283 callback, base::TimeTicks::Now()));
sorin7c717622015-05-26 19:59:09284}
285
286bool CrxUpdateService::CheckForUpdates() {
287 DCHECK(thread_checker_.CalledOnValidThread());
sorin85953dc2016-03-10 00:32:48288
289 UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.Calls", UPDATE_TYPE_AUTOMATIC,
290 UPDATE_TYPE_COUNT);
291
sorinfccbf2d2016-04-04 20:34:34292 std::vector<std::string> secure_ids; // Requires HTTPS for update checks.
293 std::vector<std::string> unsecure_ids; // Can fallback to HTTP.
sorin7c717622015-05-26 19:59:09294 for (const auto id : components_order_) {
295 DCHECK(components_.find(id) != components_.end());
sorinfccbf2d2016-04-04 20:34:34296
vmpstr2de366b2016-07-20 21:35:48297 auto* component(GetComponent(id));
sorinfccbf2d2016-04-04 20:34:34298 if (!component || component->requires_network_encryption)
299 secure_ids.push_back(id);
300 else
301 unsecure_ids.push_back(id);
sorin7c717622015-05-26 19:59:09302 }
303
sorinfccbf2d2016-04-04 20:34:34304 if (!unsecure_ids.empty()) {
305 update_client_->Update(
306 unsecure_ids,
307 base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)),
308 base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this),
sorin4c520182016-08-19 17:27:44309 CompletionCallback(), base::TimeTicks::Now()));
sorinfccbf2d2016-04-04 20:34:34310 }
311
312 if (!secure_ids.empty()) {
313 update_client_->Update(
314 secure_ids,
315 base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)),
316 base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this),
sorin4c520182016-08-19 17:27:44317 CompletionCallback(), base::TimeTicks::Now()));
sorinfccbf2d2016-04-04 20:34:34318 }
sorin7c717622015-05-26 19:59:09319
320 return true;
[email protected]78efe2e92014-08-08 15:53:22321}
322
323scoped_refptr<base::SequencedTaskRunner>
324CrxUpdateService::GetSequencedTaskRunner() {
sorin7c717622015-05-26 19:59:09325 DCHECK(thread_checker_.CalledOnValidThread());
bauerb1f6657e72015-02-09 00:00:27326 return blocking_task_runner_;
[email protected]78efe2e92014-08-08 15:53:22327}
328
sorin7c717622015-05-26 19:59:09329bool CrxUpdateService::GetComponentDetails(const std::string& id,
[email protected]f392e372014-06-12 07:25:57330 CrxUpdateItem* item) const {
[email protected]ed6fb982014-07-23 16:56:52331 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:09332
333 // First, if this component is currently being updated, return its state from
334 // the update client.
335 if (update_client_->GetCrxUpdateState(id, item))
336 return true;
337
338 // Otherwise, return the last seen state of the component, if such a
339 // state exists.
340 const auto component_states_it = component_states_.find(id);
341 if (component_states_it != component_states_.end()) {
342 *item = component_states_it->second;
343 return true;
344 }
345
346 return false;
[email protected]2e919ddd2013-08-21 05:05:17347}
348
sorin7c717622015-05-26 19:59:09349void CrxUpdateService::OnUpdate(const std::vector<std::string>& ids,
350 std::vector<CrxComponent>* components) {
351 DCHECK(thread_checker_.CalledOnValidThread());
352 DCHECK(components->empty());
353
354 for (const auto& id : ids) {
vmpstr2de366b2016-07-20 21:35:48355 const auto* registered_component(GetComponent(id));
sorin7c717622015-05-26 19:59:09356 if (registered_component) {
357 components->push_back(*registered_component);
358 }
359 }
[email protected]ed6fb982014-07-23 16:56:52360}
361
sorin4c520182016-08-19 17:27:44362void CrxUpdateService::OnUpdateComplete(CompletionCallback callback,
363 const base::TimeTicks& start_time,
sorin85953dc2016-03-10 00:32:48364 int error) {
sorin7c717622015-05-26 19:59:09365 DCHECK(thread_checker_.CalledOnValidThread());
366 VLOG(1) << "Update completed with error " << error;
367
sorin85953dc2016-03-10 00:32:48368 UMA_HISTOGRAM_BOOLEAN("ComponentUpdater.UpdateCompleteResult", error != 0);
369 UMA_HISTOGRAM_LONG_TIMES_100("ComponentUpdater.UpdateCompleteTime",
370 base::TimeTicks::Now() - start_time);
371
sorin7c717622015-05-26 19:59:09372 for (const auto id : components_pending_unregistration_) {
373 if (!update_client_->IsUpdating(id)) {
vmpstr2de366b2016-07-20 21:35:48374 const auto* component = GetComponent(id);
sorin7c717622015-05-26 19:59:09375 if (component)
376 DoUnregisterComponent(*component);
377 }
378 }
sorin4c520182016-08-19 17:27:44379
380 if (!callback.is_null()) {
381 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
382 base::Bind(callback, error));
383 }
sorin7c717622015-05-26 19:59:09384}
385
386void CrxUpdateService::OnEvent(Events event, const std::string& id) {
[email protected]ed6fb982014-07-23 16:56:52387 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]93e8e2c2014-01-04 12:29:23388
sorin7c717622015-05-26 19:59:09389 // Unblock all throttles for the component.
390 if (event == Observer::Events::COMPONENT_UPDATED ||
391 event == Observer::Events::COMPONENT_NOT_UPDATED) {
392 auto callbacks = ready_callbacks_.equal_range(id);
393 for (auto it = callbacks.first; it != callbacks.second; ++it) {
394 it->second.Run();
395 }
396 ready_callbacks_.erase(id);
397 }
398
399 CrxUpdateItem update_item;
400 if (!update_client_->GetCrxUpdateState(id, &update_item))
[email protected]e8f96ff2011-08-03 05:07:33401 return;
[email protected]93e8e2c2014-01-04 12:29:23402
sorin7c717622015-05-26 19:59:09403 // Update the state of the item.
404 auto it = component_states_.find(id);
405 DCHECK(it != component_states_.end());
406 it->second = update_item;
bauerb1f6657e72015-02-09 00:00:27407
sorin7c717622015-05-26 19:59:09408 // Update the component registration with the new version.
409 if (event == Observer::Events::COMPONENT_UPDATED) {
vmpstr2de366b2016-07-20 21:35:48410 auto* component(const_cast<CrxComponent*>(GetComponent(id)));
sorin7c717622015-05-26 19:59:09411 if (component) {
412 component->version = update_item.next_version;
413 component->fingerprint = update_item.next_fp;
bauerb1f6657e72015-02-09 00:00:27414 }
415 }
[email protected]68bf09e2014-06-03 00:10:56416}
417
[email protected]00a77fa2013-11-02 04:18:46418///////////////////////////////////////////////////////////////////////////////
419
[email protected]e8f96ff2011-08-03 05:07:33420// The component update factory. Using the component updater as a singleton
421// is the job of the browser process.
sorin7c717622015-05-26 19:59:09422// TODO(sorin): consider making this a singleton.
dchenga0ee5fb82016-04-26 02:46:55423std::unique_ptr<ComponentUpdateService> ComponentUpdateServiceFactory(
sorin9797aba2015-04-17 17:15:03424 const scoped_refptr<Configurator>& config) {
[email protected]e8f96ff2011-08-03 05:07:33425 DCHECK(config);
sorin7c717622015-05-26 19:59:09426 auto update_client = update_client::UpdateClientFactory(config);
dchenga0ee5fb82016-04-26 02:46:55427 return base::WrapUnique(
danakj4b041ab2015-12-04 20:12:27428 new CrxUpdateService(config, std::move(update_client)));
[email protected]e8f96ff2011-08-03 05:07:33429}
[email protected]2cddef42013-11-22 08:23:22430
[email protected]055981f2014-01-17 20:22:32431} // namespace component_updater