blob: 9be0f5c349488038bb67e3dc01c0fd7f4ffc28c8 [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"
sorin7b8650522016-11-02 18:23:4135#include "components/update_client/update_client_errors.h"
sorin52ac0882015-01-24 01:15:0036#include "components/update_client/utils.h"
[email protected]761fa4702013-07-02 15:25:1537#include "url/gurl.h"
[email protected]e8f96ff2011-08-03 05:07:3338
sorin7c717622015-05-26 19:59:0939using CrxInstaller = update_client::CrxInstaller;
40using UpdateClient = update_client::UpdateClient;
sorin52ac0882015-01-24 01:15:0041
sorin85953dc2016-03-10 00:32:4842namespace {
43
44enum UpdateType {
45 UPDATE_TYPE_MANUAL = 0,
46 UPDATE_TYPE_AUTOMATIC,
47 UPDATE_TYPE_COUNT,
48};
49
50} // namespace
51
[email protected]055981f2014-01-17 20:22:3252namespace component_updater {
[email protected]3a0092d2013-12-18 03:04:3553
wafflese7759f72016-10-10 23:41:2554ComponentInfo::ComponentInfo(const std::string& id, const base::string16& name,
55 const base::Version& version)
56 : id(id), name(name), version(version) {}
waffles77255cc2016-08-02 17:25:1257ComponentInfo::~ComponentInfo() {}
58
sorin7c717622015-05-26 19:59:0959CrxUpdateService::CrxUpdateService(
60 const scoped_refptr<Configurator>& config,
61 const scoped_refptr<UpdateClient>& update_client)
[email protected]e8f96ff2011-08-03 05:07:3362 : config_(config),
sorin7c717622015-05-26 19:59:0963 update_client_(update_client),
64 blocking_task_runner_(config->GetSequencedTaskRunner()) {
65 AddObserver(this);
[email protected]1ea21ad2013-09-02 04:20:3966}
[email protected]e8f96ff2011-08-03 05:07:3367
68CrxUpdateService::~CrxUpdateService() {
sorin7c717622015-05-26 19:59:0969 DCHECK(thread_checker_.CalledOnValidThread());
70
71 for (const auto item : ready_callbacks_) {
72 item.second.Run();
73 }
74
75 RemoveObserver(this);
76
[email protected]e8f96ff2011-08-03 05:07:3377 Stop();
[email protected]1ea21ad2013-09-02 04:20:3978}
[email protected]e8f96ff2011-08-03 05:07:3379
[email protected]d3268fe2014-04-25 02:14:2380void CrxUpdateService::AddObserver(Observer* observer) {
[email protected]ed6fb982014-07-23 16:56:5281 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:0982 update_client_->AddObserver(observer);
[email protected]d3268fe2014-04-25 02:14:2383}
84
85void CrxUpdateService::RemoveObserver(Observer* observer) {
[email protected]ed6fb982014-07-23 16:56:5286 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:0987 update_client_->RemoveObserver(observer);
[email protected]d3268fe2014-04-25 02:14:2388}
89
sorin7c717622015-05-26 19:59:0990void CrxUpdateService::Start() {
91 DCHECK(thread_checker_.CalledOnValidThread());
92 VLOG(1) << "CrxUpdateService starting up. "
93 << "First update attempt will take place in "
94 << config_->InitialDelay() << " seconds. "
95 << "Next update attempt will take place in "
96 << config_->NextCheckDelay() << " seconds. ";
[email protected]e8f96ff2011-08-03 05:07:3397
sorin7c717622015-05-26 19:59:0998 timer_.Start(
99 base::TimeDelta::FromSeconds(config_->InitialDelay()),
100 base::TimeDelta::FromSeconds(config_->NextCheckDelay()),
101 base::Bind(base::IgnoreResult(&CrxUpdateService::CheckForUpdates),
102 base::Unretained(this)));
[email protected]e8f96ff2011-08-03 05:07:33103}
104
sorin7c717622015-05-26 19:59:09105// Stops the update loop. In flight operations will be completed.
106void CrxUpdateService::Stop() {
107 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]fb53e652014-04-30 11:27:19108 VLOG(1) << "CrxUpdateService stopping";
[email protected]e8f96ff2011-08-03 05:07:33109 timer_.Stop();
sorinecaad3e2015-11-13 19:15:52110 update_client_->Stop();
[email protected]e8f96ff2011-08-03 05:07:33111}
112
113// Adds a component to be checked for upgrades. If the component exists it
sorin7c717622015-05-26 19:59:09114// it will be replaced.
115bool CrxUpdateService::RegisterComponent(const CrxComponent& component) {
[email protected]ed6fb982014-07-23 16:56:52116 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]d0c8b8b42014-05-06 05:11:45117 if (component.pk_hash.empty() || !component.version.IsValid() ||
sorin7c717622015-05-26 19:59:09118 !component.installer) {
119 return false;
[email protected]e8f96ff2011-08-03 05:07:33120 }
121
sorin7c717622015-05-26 19:59:09122 // Update the registration data if the component has been registered before.
123 const std::string id(GetCrxComponentID(component));
124 auto it = components_.find(id);
125 if (it != components_.end()) {
126 it->second = component;
127 return true;
[email protected]10bc0222014-06-11 13:44:38128 }
[email protected]e8f96ff2011-08-03 05:07:33129
sorin7c717622015-05-26 19:59:09130 components_.insert(std::make_pair(id, component));
131 components_order_.push_back(id);
waffles77255cc2016-08-02 17:25:12132 for (const auto& mime_type : component.handled_mime_types)
133 component_ids_by_mime_type_[mime_type] = id;
sorin7c717622015-05-26 19:59:09134
135 // Create an initial state for this component. The state is mutated in
136 // response to events from the UpdateClient instance.
137 CrxUpdateItem item;
138 item.id = id;
139 item.component = component;
140 const auto inserted = component_states_.insert(std::make_pair(id, item));
141 DCHECK(inserted.second);
142
143 // Start the timer if this is the first component registered. The first timer
144 // event occurs after an interval defined by the component update
145 // configurator. The subsequent timer events are repeated with a period
146 // defined by the same configurator.
147 if (components_.size() == 1)
148 Start();
149
150 return true;
[email protected]e8f96ff2011-08-03 05:07:33151}
152
sorin7c717622015-05-26 19:59:09153bool CrxUpdateService::UnregisterComponent(const std::string& id) {
154 DCHECK(thread_checker_.CalledOnValidThread());
155 auto it = components_.find(id);
156 if (it == components_.end())
157 return false;
bauerb1f6657e72015-02-09 00:00:27158
sorin7c717622015-05-26 19:59:09159 DCHECK_EQ(id, it->first);
bauerb1f6657e72015-02-09 00:00:27160
sorin7c717622015-05-26 19:59:09161 // Delay the uninstall of the component if the component is being updated.
162 if (update_client_->IsUpdating(id)) {
163 components_pending_unregistration_.push_back(id);
164 return true;
165 }
166
167 return DoUnregisterComponent(it->second);
168}
169
170bool CrxUpdateService::DoUnregisterComponent(const CrxComponent& component) {
171 DCHECK(thread_checker_.CalledOnValidThread());
172
173 const auto id = GetCrxComponentID(component);
174 DCHECK(ready_callbacks_.find(id) == ready_callbacks_.end());
175
176 const bool result = component.installer->Uninstall();
177
178 const auto pos =
179 std::find(components_order_.begin(), components_order_.end(), id);
180 if (pos != components_order_.end())
181 components_order_.erase(pos);
182
183 components_.erase(id);
184 component_states_.erase(id);
185
186 return result;
bauerb1f6657e72015-02-09 00:00:27187}
188
[email protected]68bf09e2014-06-03 00:10:56189std::vector<std::string> CrxUpdateService::GetComponentIDs() const {
[email protected]ed6fb982014-07-23 16:56:52190 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:09191 std::vector<std::string> ids;
192 for (const auto& it : components_)
193 ids.push_back(it.first);
194 return ids;
[email protected]68bf09e2014-06-03 00:10:56195}
196
waffles77255cc2016-08-02 17:25:12197std::unique_ptr<ComponentInfo> CrxUpdateService::GetComponentForMimeType(
198 const std::string& mime_type) const {
199 DCHECK(thread_checker_.CalledOnValidThread());
200 const auto it = component_ids_by_mime_type_.find(mime_type);
201 if (it == component_ids_by_mime_type_.end())
202 return nullptr;
vmpstr6d9996c82017-02-23 00:43:25203 auto* const component = GetComponent(it->second);
waffles77255cc2016-08-02 17:25:12204 if (!component)
205 return nullptr;
206 return base::MakeUnique<ComponentInfo>(GetCrxComponentID(*component),
wafflese7759f72016-10-10 23:41:25207 base::UTF8ToUTF16(component->name),
208 component->version);
waffles77255cc2016-08-02 17:25:12209}
210
[email protected]78efe2e92014-08-08 15:53:22211OnDemandUpdater& CrxUpdateService::GetOnDemandUpdater() {
sorin7c717622015-05-26 19:59:09212 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]78efe2e92014-08-08 15:53:22213 return *this;
214}
215
sorin7c717622015-05-26 19:59:09216const CrxComponent* CrxUpdateService::GetComponent(
217 const std::string& id) const {
218 DCHECK(thread_checker_.CalledOnValidThread());
219 const auto it(components_.find(id));
220 return it != components_.end() ? &(it->second) : NULL;
221}
222
223const CrxUpdateItem* CrxUpdateService::GetComponentState(
224 const std::string& id) const {
225 DCHECK(thread_checker_.CalledOnValidThread());
226 const auto it(component_states_.find(id));
227 return it != component_states_.end() ? &it->second : NULL;
228}
229
230void CrxUpdateService::MaybeThrottle(const std::string& id,
[email protected]78efe2e92014-08-08 15:53:22231 const base::Closure& callback) {
232 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:09233 auto it = components_.find(id);
234 if (it != components_.end()) {
235 DCHECK_EQ(it->first, id);
236 if (OnDemandUpdateWithCooldown(id)) {
237 ready_callbacks_.insert(std::make_pair(id, callback));
238 return;
239 }
[email protected]78efe2e92014-08-08 15:53:22240 }
sorin7c717622015-05-26 19:59:09241
242 callback.Run(); // Unblock the request if the request can't be throttled.
243}
244
sorin4c520182016-08-19 17:27:44245void CrxUpdateService::OnDemandUpdate(const std::string& id,
sorin842703b2016-11-02 23:59:23246 const Callback& callback) {
sorin7c717622015-05-26 19:59:09247 DCHECK(thread_checker_.CalledOnValidThread());
248
sorin4c520182016-08-19 17:27:44249 if (!GetComponent(id)) {
drbasic6d4d9ce2017-02-22 10:33:59250 if (!callback.is_null()) {
251 base::ThreadTaskRunnerHandle::Get()->PostTask(
252 FROM_HERE,
253 base::Bind(callback, update_client::Error::INVALID_ARGUMENT));
254 }
sorin4c520182016-08-19 17:27:44255 return;
256 }
sorin7c717622015-05-26 19:59:09257
sorin4c520182016-08-19 17:27:44258 OnDemandUpdateInternal(id, callback);
sorin7c717622015-05-26 19:59:09259}
260
261bool CrxUpdateService::OnDemandUpdateWithCooldown(const std::string& id) {
262 DCHECK(thread_checker_.CalledOnValidThread());
263
264 DCHECK(GetComponent(id));
265
266 // Check if the request is too soon.
vmpstr2de366b2016-07-20 21:35:48267 const auto* component_state(GetComponentState(id));
sorin7c717622015-05-26 19:59:09268 if (component_state) {
sorin1827db12016-09-15 20:43:31269 base::TimeDelta delta =
270 base::TimeTicks::Now() - component_state->last_check;
sorin7c717622015-05-26 19:59:09271 if (delta < base::TimeDelta::FromSeconds(config_->OnDemandDelay()))
272 return false;
273 }
274
sorin842703b2016-11-02 23:59:23275 OnDemandUpdateInternal(id, Callback());
sorin4c520182016-08-19 17:27:44276 return true;
sorin7c717622015-05-26 19:59:09277}
278
sorin4c520182016-08-19 17:27:44279void CrxUpdateService::OnDemandUpdateInternal(const std::string& id,
sorin842703b2016-11-02 23:59:23280 const Callback& callback) {
sorin7c717622015-05-26 19:59:09281 DCHECK(thread_checker_.CalledOnValidThread());
282
sorin85953dc2016-03-10 00:32:48283 UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.Calls", UPDATE_TYPE_MANUAL,
284 UPDATE_TYPE_COUNT);
sorin7c717622015-05-26 19:59:09285 update_client_->Install(
286 id, base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)),
sorin85953dc2016-03-10 00:32:48287 base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this),
sorin4c520182016-08-19 17:27:44288 callback, base::TimeTicks::Now()));
sorin7c717622015-05-26 19:59:09289}
290
291bool CrxUpdateService::CheckForUpdates() {
292 DCHECK(thread_checker_.CalledOnValidThread());
sorin85953dc2016-03-10 00:32:48293
294 UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.Calls", UPDATE_TYPE_AUTOMATIC,
295 UPDATE_TYPE_COUNT);
296
sorinfccbf2d2016-04-04 20:34:34297 std::vector<std::string> secure_ids; // Requires HTTPS for update checks.
298 std::vector<std::string> unsecure_ids; // Can fallback to HTTP.
sorin7c717622015-05-26 19:59:09299 for (const auto id : components_order_) {
300 DCHECK(components_.find(id) != components_.end());
sorinfccbf2d2016-04-04 20:34:34301
vmpstr2de366b2016-07-20 21:35:48302 auto* component(GetComponent(id));
sorinfccbf2d2016-04-04 20:34:34303 if (!component || component->requires_network_encryption)
304 secure_ids.push_back(id);
305 else
306 unsecure_ids.push_back(id);
sorin7c717622015-05-26 19:59:09307 }
308
sorinfccbf2d2016-04-04 20:34:34309 if (!unsecure_ids.empty()) {
310 update_client_->Update(
311 unsecure_ids,
312 base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)),
313 base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this),
sorin842703b2016-11-02 23:59:23314 Callback(), base::TimeTicks::Now()));
sorinfccbf2d2016-04-04 20:34:34315 }
316
317 if (!secure_ids.empty()) {
318 update_client_->Update(
319 secure_ids,
320 base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)),
321 base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this),
sorin842703b2016-11-02 23:59:23322 Callback(), base::TimeTicks::Now()));
sorinfccbf2d2016-04-04 20:34:34323 }
sorin7c717622015-05-26 19:59:09324
325 return true;
[email protected]78efe2e92014-08-08 15:53:22326}
327
328scoped_refptr<base::SequencedTaskRunner>
329CrxUpdateService::GetSequencedTaskRunner() {
sorin7c717622015-05-26 19:59:09330 DCHECK(thread_checker_.CalledOnValidThread());
bauerb1f6657e72015-02-09 00:00:27331 return blocking_task_runner_;
[email protected]78efe2e92014-08-08 15:53:22332}
333
sorin7c717622015-05-26 19:59:09334bool CrxUpdateService::GetComponentDetails(const std::string& id,
[email protected]f392e372014-06-12 07:25:57335 CrxUpdateItem* item) const {
[email protected]ed6fb982014-07-23 16:56:52336 DCHECK(thread_checker_.CalledOnValidThread());
sorin7c717622015-05-26 19:59:09337
338 // First, if this component is currently being updated, return its state from
339 // the update client.
340 if (update_client_->GetCrxUpdateState(id, item))
341 return true;
342
343 // Otherwise, return the last seen state of the component, if such a
344 // state exists.
345 const auto component_states_it = component_states_.find(id);
346 if (component_states_it != component_states_.end()) {
347 *item = component_states_it->second;
348 return true;
349 }
350
351 return false;
[email protected]2e919ddd2013-08-21 05:05:17352}
353
sorin7c717622015-05-26 19:59:09354void CrxUpdateService::OnUpdate(const std::vector<std::string>& ids,
355 std::vector<CrxComponent>* components) {
356 DCHECK(thread_checker_.CalledOnValidThread());
357 DCHECK(components->empty());
358
359 for (const auto& id : ids) {
sorineae115a2016-08-26 02:27:20360 const update_client::CrxComponent* registered_component(GetComponent(id));
sorin97bd0292016-11-14 19:46:53361 if (registered_component)
sorin7c717622015-05-26 19:59:09362 components->push_back(*registered_component);
sorin7c717622015-05-26 19:59:09363 }
[email protected]ed6fb982014-07-23 16:56:52364}
365
sorin842703b2016-11-02 23:59:23366void CrxUpdateService::OnUpdateComplete(Callback callback,
sorin4c520182016-08-19 17:27:44367 const base::TimeTicks& start_time,
sorin7b8650522016-11-02 18:23:41368 update_client::Error error) {
sorin7c717622015-05-26 19:59:09369 DCHECK(thread_checker_.CalledOnValidThread());
sorin7b8650522016-11-02 18:23:41370 VLOG(1) << "Update completed with error " << static_cast<int>(error);
sorin7c717622015-05-26 19:59:09371
sorin7b8650522016-11-02 18:23:41372 UMA_HISTOGRAM_BOOLEAN("ComponentUpdater.UpdateCompleteResult",
373 error != update_client::Error::NONE);
sorin85953dc2016-03-10 00:32:48374 UMA_HISTOGRAM_LONG_TIMES_100("ComponentUpdater.UpdateCompleteTime",
375 base::TimeTicks::Now() - start_time);
376
sorin7c717622015-05-26 19:59:09377 for (const auto id : components_pending_unregistration_) {
378 if (!update_client_->IsUpdating(id)) {
vmpstr2de366b2016-07-20 21:35:48379 const auto* component = GetComponent(id);
sorin7c717622015-05-26 19:59:09380 if (component)
381 DoUnregisterComponent(*component);
382 }
383 }
sorin4c520182016-08-19 17:27:44384
385 if (!callback.is_null()) {
386 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
387 base::Bind(callback, error));
388 }
sorin7c717622015-05-26 19:59:09389}
390
391void CrxUpdateService::OnEvent(Events event, const std::string& id) {
[email protected]ed6fb982014-07-23 16:56:52392 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]93e8e2c2014-01-04 12:29:23393
sorin7c717622015-05-26 19:59:09394 // Unblock all throttles for the component.
395 if (event == Observer::Events::COMPONENT_UPDATED ||
396 event == Observer::Events::COMPONENT_NOT_UPDATED) {
397 auto callbacks = ready_callbacks_.equal_range(id);
398 for (auto it = callbacks.first; it != callbacks.second; ++it) {
399 it->second.Run();
400 }
401 ready_callbacks_.erase(id);
402 }
403
404 CrxUpdateItem update_item;
405 if (!update_client_->GetCrxUpdateState(id, &update_item))
[email protected]e8f96ff2011-08-03 05:07:33406 return;
[email protected]93e8e2c2014-01-04 12:29:23407
sorin7c717622015-05-26 19:59:09408 // Update the state of the item.
409 auto it = component_states_.find(id);
410 DCHECK(it != component_states_.end());
411 it->second = update_item;
bauerb1f6657e72015-02-09 00:00:27412
sorin7c717622015-05-26 19:59:09413 // Update the component registration with the new version.
414 if (event == Observer::Events::COMPONENT_UPDATED) {
vmpstr2de366b2016-07-20 21:35:48415 auto* component(const_cast<CrxComponent*>(GetComponent(id)));
sorin7c717622015-05-26 19:59:09416 if (component) {
417 component->version = update_item.next_version;
418 component->fingerprint = update_item.next_fp;
bauerb1f6657e72015-02-09 00:00:27419 }
420 }
[email protected]68bf09e2014-06-03 00:10:56421}
422
[email protected]00a77fa2013-11-02 04:18:46423///////////////////////////////////////////////////////////////////////////////
424
[email protected]e8f96ff2011-08-03 05:07:33425// The component update factory. Using the component updater as a singleton
426// is the job of the browser process.
sorin7c717622015-05-26 19:59:09427// TODO(sorin): consider making this a singleton.
dchenga0ee5fb82016-04-26 02:46:55428std::unique_ptr<ComponentUpdateService> ComponentUpdateServiceFactory(
sorin9797aba2015-04-17 17:15:03429 const scoped_refptr<Configurator>& config) {
[email protected]e8f96ff2011-08-03 05:07:33430 DCHECK(config);
sorin7c717622015-05-26 19:59:09431 auto update_client = update_client::UpdateClientFactory(config);
ricea85ec57952016-08-31 09:34:10432 return base::MakeUnique<CrxUpdateService>(config, std::move(update_client));
[email protected]e8f96ff2011-08-03 05:07:33433}
[email protected]2cddef42013-11-22 08:23:22434
[email protected]055981f2014-01-17 20:22:32435} // namespace component_updater