blob: 12426113df2e3f877ae4e00881a476219f3dc7d5 [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"
waffles77255cc2016-08-02 17:25:1222#include "base/strings/utf_string_conversions.h"
[email protected]ed6fb982014-07-23 16:56:5223#include "base/threading/thread_checker.h"
gab7966d312016-05-11 20:35:0124#include "base/threading/thread_task_runner_handle.h"
sorin85953dc2016-03-10 00:32:4825#include "base/time/time.h"
[email protected]41a17c52013-06-28 00:27:5326#include "base/timer/timer.h"
sorin7c717622015-05-26 19:59:0927#include "components/component_updater/component_updater_service_internal.h"
28#include "components/component_updater/timer.h"
sorin52ac0882015-01-24 01:15:0029#include "components/update_client/configurator.h"
sorin52ac0882015-01-24 01:15:0030#include "components/update_client/crx_update_item.h"
sorin52ac0882015-01-24 01:15:0031#include "components/update_client/update_client.h"
sorin7b8650522016-11-02 18:23:4132#include "components/update_client/update_client_errors.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
sorin85953dc2016-03-10 00:32:4839namespace {
40
41enum UpdateType {
42 UPDATE_TYPE_MANUAL = 0,
43 UPDATE_TYPE_AUTOMATIC,
44 UPDATE_TYPE_COUNT,
45};
46
47} // namespace
48
[email protected]055981f2014-01-17 20:22:3249namespace component_updater {
[email protected]3a0092d2013-12-18 03:04:3550
Joshua Pawlicki0499ac82017-08-17 18:29:0751ComponentInfo::ComponentInfo(const std::string& id,
52 const std::string& fingerprint,
53 const base::string16& name,
wafflese7759f72016-10-10 23:41:2554 const base::Version& version)
Joshua Pawlicki0499ac82017-08-17 18:29:0755 : id(id), fingerprint(fingerprint), name(name), version(version) {}
56ComponentInfo::ComponentInfo(const ComponentInfo& other) = default;
57ComponentInfo::ComponentInfo(ComponentInfo&& other) = default;
waffles77255cc2016-08-02 17:25:1258ComponentInfo::~ComponentInfo() {}
59
sorin7c717622015-05-26 19:59:0960CrxUpdateService::CrxUpdateService(
61 const scoped_refptr<Configurator>& config,
62 const scoped_refptr<UpdateClient>& update_client)
Sorin Jianuebd652462017-07-23 02:00:5863 : config_(config), update_client_(update_client) {
sorin7c717622015-05-26 19:59:0964 AddObserver(this);
[email protected]1ea21ad2013-09-02 04:20:3965}
[email protected]e8f96ff2011-08-03 05:07:3366
67CrxUpdateService::~CrxUpdateService() {
sorin7c717622015-05-26 19:59:0968 DCHECK(thread_checker_.CalledOnValidThread());
69
70 for (const auto item : ready_callbacks_) {
71 item.second.Run();
72 }
73
74 RemoveObserver(this);
75
[email protected]e8f96ff2011-08-03 05:07:3376 Stop();
[email protected]1ea21ad2013-09-02 04:20:3977}
[email protected]e8f96ff2011-08-03 05:07:3378
[email protected]d3268fe2014-04-25 02:14:2379void CrxUpdateService::AddObserver(Observer* observer) {
[email protected]ed6fb982014-07-23 16:56:5280 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:0981 update_client_->AddObserver(observer);
[email protected]d3268fe2014-04-25 02:14:2382}
83
84void CrxUpdateService::RemoveObserver(Observer* observer) {
[email protected]ed6fb982014-07-23 16:56:5285 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:0986 update_client_->RemoveObserver(observer);
[email protected]d3268fe2014-04-25 02:14:2387}
88
sorin7c717622015-05-26 19:59:0989void CrxUpdateService::Start() {
90 DCHECK(thread_checker_.CalledOnValidThread());
91 VLOG(1) << "CrxUpdateService starting up. "
92 << "First update attempt will take place in "
93 << config_->InitialDelay() << " seconds. "
94 << "Next update attempt will take place in "
95 << config_->NextCheckDelay() << " seconds. ";
[email protected]e8f96ff2011-08-03 05:07:3396
sorin7c717622015-05-26 19:59:0997 timer_.Start(
98 base::TimeDelta::FromSeconds(config_->InitialDelay()),
99 base::TimeDelta::FromSeconds(config_->NextCheckDelay()),
100 base::Bind(base::IgnoreResult(&CrxUpdateService::CheckForUpdates),
101 base::Unretained(this)));
[email protected]e8f96ff2011-08-03 05:07:33102}
103
sorin7c717622015-05-26 19:59:09104// Stops the update loop. In flight operations will be completed.
105void CrxUpdateService::Stop() {
106 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]fb53e652014-04-30 11:27:19107 VLOG(1) << "CrxUpdateService stopping";
[email protected]e8f96ff2011-08-03 05:07:33108 timer_.Stop();
sorinecaad3e2015-11-13 19:15:52109 update_client_->Stop();
[email protected]e8f96ff2011-08-03 05:07:33110}
111
112// Adds a component to be checked for upgrades. If the component exists it
sorin7c717622015-05-26 19:59:09113// it will be replaced.
114bool CrxUpdateService::RegisterComponent(const CrxComponent& component) {
[email protected]ed6fb982014-07-23 16:56:52115 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]d0c8b8b42014-05-06 05:11:45116 if (component.pk_hash.empty() || !component.version.IsValid() ||
sorin7c717622015-05-26 19:59:09117 !component.installer) {
118 return false;
[email protected]e8f96ff2011-08-03 05:07:33119 }
120
sorin7c717622015-05-26 19:59:09121 // Update the registration data if the component has been registered before.
122 const std::string id(GetCrxComponentID(component));
123 auto it = components_.find(id);
124 if (it != components_.end()) {
125 it->second = component;
126 return true;
[email protected]10bc0222014-06-11 13:44:38127 }
[email protected]e8f96ff2011-08-03 05:07:33128
sorin7c717622015-05-26 19:59:09129 components_.insert(std::make_pair(id, component));
130 components_order_.push_back(id);
waffles77255cc2016-08-02 17:25:12131 for (const auto& mime_type : component.handled_mime_types)
132 component_ids_by_mime_type_[mime_type] = id;
sorin7c717622015-05-26 19:59:09133
134 // Create an initial state for this component. The state is mutated in
135 // response to events from the UpdateClient instance.
136 CrxUpdateItem item;
137 item.id = id;
138 item.component = component;
139 const auto inserted = component_states_.insert(std::make_pair(id, item));
140 DCHECK(inserted.second);
141
142 // Start the timer if this is the first component registered. The first timer
143 // event occurs after an interval defined by the component update
144 // configurator. The subsequent timer events are repeated with a period
145 // defined by the same configurator.
146 if (components_.size() == 1)
147 Start();
148
149 return true;
[email protected]e8f96ff2011-08-03 05:07:33150}
151
sorin7c717622015-05-26 19:59:09152bool CrxUpdateService::UnregisterComponent(const std::string& id) {
153 DCHECK(thread_checker_.CalledOnValidThread());
154 auto it = components_.find(id);
155 if (it == components_.end())
156 return false;
bauerb1f6657e72015-02-09 00:00:27157
sorin7c717622015-05-26 19:59:09158 DCHECK_EQ(id, it->first);
bauerb1f6657e72015-02-09 00:00:27159
sorin7c717622015-05-26 19:59:09160 // Delay the uninstall of the component if the component is being updated.
161 if (update_client_->IsUpdating(id)) {
162 components_pending_unregistration_.push_back(id);
163 return true;
164 }
165
166 return DoUnregisterComponent(it->second);
167}
168
169bool CrxUpdateService::DoUnregisterComponent(const CrxComponent& component) {
170 DCHECK(thread_checker_.CalledOnValidThread());
171
172 const auto id = GetCrxComponentID(component);
173 DCHECK(ready_callbacks_.find(id) == ready_callbacks_.end());
174
175 const bool result = component.installer->Uninstall();
176
177 const auto pos =
178 std::find(components_order_.begin(), components_order_.end(), id);
179 if (pos != components_order_.end())
180 components_order_.erase(pos);
181
182 components_.erase(id);
183 component_states_.erase(id);
184
185 return result;
bauerb1f6657e72015-02-09 00:00:27186}
187
[email protected]68bf09e2014-06-03 00:10:56188std::vector<std::string> CrxUpdateService::GetComponentIDs() const {
[email protected]ed6fb982014-07-23 16:56:52189 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:09190 std::vector<std::string> ids;
191 for (const auto& it : components_)
192 ids.push_back(it.first);
193 return ids;
[email protected]68bf09e2014-06-03 00:10:56194}
195
waffles77255cc2016-08-02 17:25:12196std::unique_ptr<ComponentInfo> CrxUpdateService::GetComponentForMimeType(
197 const std::string& mime_type) const {
198 DCHECK(thread_checker_.CalledOnValidThread());
199 const auto it = component_ids_by_mime_type_.find(mime_type);
200 if (it == component_ids_by_mime_type_.end())
201 return nullptr;
vmpstr6d9996c82017-02-23 00:43:25202 auto* const component = GetComponent(it->second);
waffles77255cc2016-08-02 17:25:12203 if (!component)
204 return nullptr;
Joshua Pawlicki0499ac82017-08-17 18:29:07205 return base::MakeUnique<ComponentInfo>(
206 GetCrxComponentID(*component), component->fingerprint,
207 base::UTF8ToUTF16(component->name), component->version);
208}
209
210std::vector<ComponentInfo> CrxUpdateService::GetComponents() const {
211 DCHECK(thread_checker_.CalledOnValidThread());
212 std::vector<ComponentInfo> result;
213 for (const auto& it : components_) {
214 result.push_back(ComponentInfo(it.first, it.second.fingerprint,
215 base::UTF8ToUTF16(it.second.name),
216 it.second.version));
217 }
218 return result;
waffles77255cc2016-08-02 17:25:12219}
220
[email protected]78efe2e92014-08-08 15:53:22221OnDemandUpdater& CrxUpdateService::GetOnDemandUpdater() {
sorin7c717622015-05-26 19:59:09222 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]78efe2e92014-08-08 15:53:22223 return *this;
224}
225
sorin7c717622015-05-26 19:59:09226const CrxComponent* CrxUpdateService::GetComponent(
227 const std::string& id) const {
228 DCHECK(thread_checker_.CalledOnValidThread());
229 const auto it(components_.find(id));
230 return it != components_.end() ? &(it->second) : NULL;
231}
232
233const CrxUpdateItem* CrxUpdateService::GetComponentState(
234 const std::string& id) const {
235 DCHECK(thread_checker_.CalledOnValidThread());
236 const auto it(component_states_.find(id));
237 return it != component_states_.end() ? &it->second : NULL;
238}
239
240void CrxUpdateService::MaybeThrottle(const std::string& id,
[email protected]78efe2e92014-08-08 15:53:22241 const base::Closure& callback) {
242 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:09243 auto it = components_.find(id);
244 if (it != components_.end()) {
245 DCHECK_EQ(it->first, id);
246 if (OnDemandUpdateWithCooldown(id)) {
247 ready_callbacks_.insert(std::make_pair(id, callback));
248 return;
249 }
[email protected]78efe2e92014-08-08 15:53:22250 }
sorin7c717622015-05-26 19:59:09251
252 callback.Run(); // Unblock the request if the request can't be throttled.
253}
254
sorin4c520182016-08-19 17:27:44255void CrxUpdateService::OnDemandUpdate(const std::string& id,
sorin842703b2016-11-02 23:59:23256 const Callback& callback) {
sorin7c717622015-05-26 19:59:09257 DCHECK(thread_checker_.CalledOnValidThread());
258
sorin4c520182016-08-19 17:27:44259 if (!GetComponent(id)) {
drbasic6d4d9ce2017-02-22 10:33:59260 if (!callback.is_null()) {
261 base::ThreadTaskRunnerHandle::Get()->PostTask(
262 FROM_HERE,
Sorin Jianuebd652462017-07-23 02:00:58263 base::BindOnce(callback, update_client::Error::INVALID_ARGUMENT));
drbasic6d4d9ce2017-02-22 10:33:59264 }
sorin4c520182016-08-19 17:27:44265 return;
266 }
sorin7c717622015-05-26 19:59:09267
sorin4c520182016-08-19 17:27:44268 OnDemandUpdateInternal(id, callback);
sorin7c717622015-05-26 19:59:09269}
270
271bool CrxUpdateService::OnDemandUpdateWithCooldown(const std::string& id) {
272 DCHECK(thread_checker_.CalledOnValidThread());
273
274 DCHECK(GetComponent(id));
275
276 // Check if the request is too soon.
vmpstr2de366b2016-07-20 21:35:48277 const auto* component_state(GetComponentState(id));
sorin7c717622015-05-26 19:59:09278 if (component_state) {
sorin1827db12016-09-15 20:43:31279 base::TimeDelta delta =
280 base::TimeTicks::Now() - component_state->last_check;
sorin7c717622015-05-26 19:59:09281 if (delta < base::TimeDelta::FromSeconds(config_->OnDemandDelay()))
282 return false;
283 }
284
sorin842703b2016-11-02 23:59:23285 OnDemandUpdateInternal(id, Callback());
sorin4c520182016-08-19 17:27:44286 return true;
sorin7c717622015-05-26 19:59:09287}
288
sorin4c520182016-08-19 17:27:44289void CrxUpdateService::OnDemandUpdateInternal(const std::string& id,
sorin842703b2016-11-02 23:59:23290 const Callback& callback) {
sorin7c717622015-05-26 19:59:09291 DCHECK(thread_checker_.CalledOnValidThread());
292
sorin85953dc2016-03-10 00:32:48293 UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.Calls", UPDATE_TYPE_MANUAL,
294 UPDATE_TYPE_COUNT);
sorin7c717622015-05-26 19:59:09295 update_client_->Install(
296 id, base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)),
sorin85953dc2016-03-10 00:32:48297 base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this),
sorin4c520182016-08-19 17:27:44298 callback, base::TimeTicks::Now()));
sorin7c717622015-05-26 19:59:09299}
300
301bool CrxUpdateService::CheckForUpdates() {
302 DCHECK(thread_checker_.CalledOnValidThread());
sorin85953dc2016-03-10 00:32:48303
304 UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.Calls", UPDATE_TYPE_AUTOMATIC,
305 UPDATE_TYPE_COUNT);
306
sorinfccbf2d2016-04-04 20:34:34307 std::vector<std::string> secure_ids; // Requires HTTPS for update checks.
308 std::vector<std::string> unsecure_ids; // Can fallback to HTTP.
sorin7c717622015-05-26 19:59:09309 for (const auto id : components_order_) {
310 DCHECK(components_.find(id) != components_.end());
sorinfccbf2d2016-04-04 20:34:34311
vmpstr2de366b2016-07-20 21:35:48312 auto* component(GetComponent(id));
sorinfccbf2d2016-04-04 20:34:34313 if (!component || component->requires_network_encryption)
314 secure_ids.push_back(id);
315 else
316 unsecure_ids.push_back(id);
sorin7c717622015-05-26 19:59:09317 }
318
sorinfccbf2d2016-04-04 20:34:34319 if (!unsecure_ids.empty()) {
320 update_client_->Update(
321 unsecure_ids,
322 base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)),
323 base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this),
sorin842703b2016-11-02 23:59:23324 Callback(), base::TimeTicks::Now()));
sorinfccbf2d2016-04-04 20:34:34325 }
326
327 if (!secure_ids.empty()) {
328 update_client_->Update(
329 secure_ids,
330 base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)),
331 base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this),
sorin842703b2016-11-02 23:59:23332 Callback(), base::TimeTicks::Now()));
sorinfccbf2d2016-04-04 20:34:34333 }
sorin7c717622015-05-26 19:59:09334
335 return true;
[email protected]78efe2e92014-08-08 15:53:22336}
337
sorin7c717622015-05-26 19:59:09338bool CrxUpdateService::GetComponentDetails(const std::string& id,
[email protected]f392e372014-06-12 07:25:57339 CrxUpdateItem* item) const {
[email protected]ed6fb982014-07-23 16:56:52340 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:09341
342 // First, if this component is currently being updated, return its state from
343 // the update client.
344 if (update_client_->GetCrxUpdateState(id, item))
345 return true;
346
347 // Otherwise, return the last seen state of the component, if such a
348 // state exists.
349 const auto component_states_it = component_states_.find(id);
350 if (component_states_it != component_states_.end()) {
351 *item = component_states_it->second;
352 return true;
353 }
354
355 return false;
[email protected]2e919ddd2013-08-21 05:05:17356}
357
sorin7c717622015-05-26 19:59:09358void CrxUpdateService::OnUpdate(const std::vector<std::string>& ids,
359 std::vector<CrxComponent>* components) {
360 DCHECK(thread_checker_.CalledOnValidThread());
361 DCHECK(components->empty());
362
363 for (const auto& id : ids) {
sorineae115a2016-08-26 02:27:20364 const update_client::CrxComponent* registered_component(GetComponent(id));
sorin97bd0292016-11-14 19:46:53365 if (registered_component)
sorin7c717622015-05-26 19:59:09366 components->push_back(*registered_component);
sorin7c717622015-05-26 19:59:09367 }
[email protected]ed6fb982014-07-23 16:56:52368}
369
sorin842703b2016-11-02 23:59:23370void CrxUpdateService::OnUpdateComplete(Callback callback,
sorin4c520182016-08-19 17:27:44371 const base::TimeTicks& start_time,
sorin7b8650522016-11-02 18:23:41372 update_client::Error error) {
sorin7c717622015-05-26 19:59:09373 DCHECK(thread_checker_.CalledOnValidThread());
sorin7b8650522016-11-02 18:23:41374 VLOG(1) << "Update completed with error " << static_cast<int>(error);
sorin7c717622015-05-26 19:59:09375
sorin7b8650522016-11-02 18:23:41376 UMA_HISTOGRAM_BOOLEAN("ComponentUpdater.UpdateCompleteResult",
377 error != update_client::Error::NONE);
sorin85953dc2016-03-10 00:32:48378 UMA_HISTOGRAM_LONG_TIMES_100("ComponentUpdater.UpdateCompleteTime",
379 base::TimeTicks::Now() - start_time);
380
sorin7c717622015-05-26 19:59:09381 for (const auto id : components_pending_unregistration_) {
382 if (!update_client_->IsUpdating(id)) {
vmpstr2de366b2016-07-20 21:35:48383 const auto* component = GetComponent(id);
sorin7c717622015-05-26 19:59:09384 if (component)
385 DoUnregisterComponent(*component);
386 }
387 }
sorin4c520182016-08-19 17:27:44388
389 if (!callback.is_null()) {
Sorin Jianuebd652462017-07-23 02:00:58390 base::ThreadTaskRunnerHandle::Get()->PostTask(
391 FROM_HERE, base::BindOnce(callback, error));
sorin4c520182016-08-19 17:27:44392 }
sorin7c717622015-05-26 19:59:09393}
394
395void CrxUpdateService::OnEvent(Events event, const std::string& id) {
[email protected]ed6fb982014-07-23 16:56:52396 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]93e8e2c2014-01-04 12:29:23397
sorin7c717622015-05-26 19:59:09398 // Unblock all throttles for the component.
399 if (event == Observer::Events::COMPONENT_UPDATED ||
400 event == Observer::Events::COMPONENT_NOT_UPDATED) {
401 auto callbacks = ready_callbacks_.equal_range(id);
402 for (auto it = callbacks.first; it != callbacks.second; ++it) {
403 it->second.Run();
404 }
405 ready_callbacks_.erase(id);
406 }
407
408 CrxUpdateItem update_item;
409 if (!update_client_->GetCrxUpdateState(id, &update_item))
[email protected]e8f96ff2011-08-03 05:07:33410 return;
[email protected]93e8e2c2014-01-04 12:29:23411
sorin7c717622015-05-26 19:59:09412 // Update the state of the item.
413 auto it = component_states_.find(id);
414 DCHECK(it != component_states_.end());
415 it->second = update_item;
bauerb1f6657e72015-02-09 00:00:27416
sorin7c717622015-05-26 19:59:09417 // Update the component registration with the new version.
418 if (event == Observer::Events::COMPONENT_UPDATED) {
vmpstr2de366b2016-07-20 21:35:48419 auto* component(const_cast<CrxComponent*>(GetComponent(id)));
sorin7c717622015-05-26 19:59:09420 if (component) {
421 component->version = update_item.next_version;
422 component->fingerprint = update_item.next_fp;
bauerb1f6657e72015-02-09 00:00:27423 }
424 }
[email protected]68bf09e2014-06-03 00:10:56425}
426
[email protected]00a77fa2013-11-02 04:18:46427///////////////////////////////////////////////////////////////////////////////
428
[email protected]e8f96ff2011-08-03 05:07:33429// The component update factory. Using the component updater as a singleton
430// is the job of the browser process.
sorin7c717622015-05-26 19:59:09431// TODO(sorin): consider making this a singleton.
dchenga0ee5fb82016-04-26 02:46:55432std::unique_ptr<ComponentUpdateService> ComponentUpdateServiceFactory(
sorin9797aba2015-04-17 17:15:03433 const scoped_refptr<Configurator>& config) {
[email protected]e8f96ff2011-08-03 05:07:33434 DCHECK(config);
sorin7c717622015-05-26 19:59:09435 auto update_client = update_client::UpdateClientFactory(config);
ricea85ec57952016-08-31 09:34:10436 return base::MakeUnique<CrxUpdateService>(config, std::move(update_client));
[email protected]e8f96ff2011-08-03 05:07:33437}
[email protected]2cddef42013-11-22 08:23:22438
[email protected]055981f2014-01-17 20:22:32439} // namespace component_updater