1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/component_updater/component_updater_service.h"
6 
7 #include <algorithm>
8 #include <map>
9 #include <string>
10 #include <utility>
11 #include <vector>
12 
13 #include "base/bind.h"
14 #include "base/bind_helpers.h"
15 #include "base/callback.h"
16 #include "base/files/file_path.h"
17 #include "base/files/file_util.h"
18 #include "base/logging.h"
19 #include "base/macros.h"
20 #include "base/metrics/histogram_macros.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/threading/thread_checker.h"
23 #include "base/threading/thread_task_runner_handle.h"
24 #include "base/time/time.h"
25 #include "base/timer/timer.h"
26 #include "components/component_updater/component_updater_service_internal.h"
27 #include "components/update_client/configurator.h"
28 #include "components/update_client/crx_update_item.h"
29 #include "components/update_client/update_client.h"
30 #include "components/update_client/update_client_errors.h"
31 #include "components/update_client/utils.h"
32 #include "url/gurl.h"
33 
34 using CrxInstaller = update_client::CrxInstaller;
35 using UpdateClient = update_client::UpdateClient;
36 
37 namespace {
38 
39 enum UpdateType {
40   UPDATE_TYPE_MANUAL = 0,
41   UPDATE_TYPE_AUTOMATIC,
42   UPDATE_TYPE_COUNT,
43 };
44 
45 }  // namespace
46 
47 namespace component_updater {
48 
ComponentInfo(const std::string & id,const std::string & fingerprint,const base::string16 & name,const base::Version & version)49 ComponentInfo::ComponentInfo(const std::string& id,
50                              const std::string& fingerprint,
51                              const base::string16& name,
52                              const base::Version& version)
53     : id(id), fingerprint(fingerprint), name(name), version(version) {}
54 ComponentInfo::ComponentInfo(const ComponentInfo& other) = default;
55 ComponentInfo::ComponentInfo(ComponentInfo&& other) = default;
56 ComponentInfo::~ComponentInfo() = default;
57 
CrxUpdateService(scoped_refptr<Configurator> config,std::unique_ptr<UpdateScheduler> scheduler,scoped_refptr<UpdateClient> update_client)58 CrxUpdateService::CrxUpdateService(scoped_refptr<Configurator> config,
59                                    std::unique_ptr<UpdateScheduler> scheduler,
60                                    scoped_refptr<UpdateClient> update_client)
61     : config_(config),
62       scheduler_(std::move(scheduler)),
63       update_client_(update_client) {
64   AddObserver(this);
65 }
66 
~CrxUpdateService()67 CrxUpdateService::~CrxUpdateService() {
68   DCHECK(thread_checker_.CalledOnValidThread());
69 
70   for (auto& item : ready_callbacks_) {
71     std::move(item.second).Run();
72   }
73 
74   RemoveObserver(this);
75 
76   Stop();
77 }
78 
AddObserver(Observer * observer)79 void CrxUpdateService::AddObserver(Observer* observer) {
80   DCHECK(thread_checker_.CalledOnValidThread());
81   update_client_->AddObserver(observer);
82 }
83 
RemoveObserver(Observer * observer)84 void CrxUpdateService::RemoveObserver(Observer* observer) {
85   DCHECK(thread_checker_.CalledOnValidThread());
86   update_client_->RemoveObserver(observer);
87 }
88 
Start()89 void 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. ";
96 
97   scheduler_->Schedule(
98       base::TimeDelta::FromSeconds(config_->InitialDelay()),
99       base::TimeDelta::FromSeconds(config_->NextCheckDelay()),
100       base::BindRepeating(
101           base::IgnoreResult(&CrxUpdateService::CheckForUpdates),
102           base::Unretained(this)),
103       base::DoNothing());
104 }
105 
106 // Stops the update loop. In flight operations will be completed.
Stop()107 void CrxUpdateService::Stop() {
108   DCHECK(thread_checker_.CalledOnValidThread());
109   VLOG(1) << "CrxUpdateService stopping";
110   scheduler_->Stop();
111   update_client_->Stop();
112 }
113 
114 // Adds a component to be checked for upgrades. If the component exists it
115 // it will be replaced.
RegisterComponent(const CrxComponent & component)116 bool CrxUpdateService::RegisterComponent(const CrxComponent& component) {
117   DCHECK(thread_checker_.CalledOnValidThread());
118   if (component.app_id.empty() || !component.version.IsValid() ||
119       !component.installer) {
120     return false;
121   }
122 
123   // Update the registration data if the component has been registered before.
124   auto it = components_.find(component.app_id);
125   if (it != components_.end()) {
126     it->second = component;
127     return true;
128   }
129 
130   components_.insert(std::make_pair(component.app_id, component));
131   components_order_.push_back(component.app_id);
132   for (const auto& mime_type : component.handled_mime_types)
133     component_ids_by_mime_type_[mime_type] = component.app_id;
134 
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 = component.app_id;
139   item.component = component;
140   const auto inserted =
141       component_states_.insert(std::make_pair(component.app_id, item));
142   DCHECK(inserted.second);
143 
144   // Start the timer if this is the first component registered. The first timer
145   // event occurs after an interval defined by the component update
146   // configurator. The subsequent timer events are repeated with a period
147   // defined by the same configurator.
148   if (components_.size() == 1)
149     Start();
150 
151   return true;
152 }
153 
UnregisterComponent(const std::string & id)154 bool CrxUpdateService::UnregisterComponent(const std::string& id) {
155   DCHECK(thread_checker_.CalledOnValidThread());
156   auto it = components_.find(id);
157   if (it == components_.end())
158     return false;
159 
160   DCHECK_EQ(id, it->first);
161 
162   // Delay the uninstall of the component if the component is being updated.
163   if (update_client_->IsUpdating(id)) {
164     components_pending_unregistration_.push_back(id);
165     return true;
166   }
167 
168   return DoUnregisterComponent(it->second);
169 }
170 
DoUnregisterComponent(const CrxComponent & component)171 bool CrxUpdateService::DoUnregisterComponent(const CrxComponent& component) {
172   DCHECK(thread_checker_.CalledOnValidThread());
173 
174   const auto id = GetCrxComponentID(component);
175   DCHECK(ready_callbacks_.find(id) == ready_callbacks_.end());
176 
177   const bool result = component.installer->Uninstall();
178 
179   const auto pos =
180       std::find(components_order_.begin(), components_order_.end(), id);
181   if (pos != components_order_.end())
182     components_order_.erase(pos);
183 
184   components_.erase(id);
185   component_states_.erase(id);
186 
187   return result;
188 }
189 
GetComponentIDs() const190 std::vector<std::string> CrxUpdateService::GetComponentIDs() const {
191   DCHECK(thread_checker_.CalledOnValidThread());
192   std::vector<std::string> ids;
193   for (const auto& it : components_)
194     ids.push_back(it.first);
195   return ids;
196 }
197 
GetComponentForMimeType(const std::string & mime_type) const198 std::unique_ptr<ComponentInfo> CrxUpdateService::GetComponentForMimeType(
199     const std::string& mime_type) const {
200   DCHECK(thread_checker_.CalledOnValidThread());
201   const auto it = component_ids_by_mime_type_.find(mime_type);
202   if (it == component_ids_by_mime_type_.end())
203     return nullptr;
204   const auto component = GetComponent(it->second);
205   if (!component)
206     return nullptr;
207   return std::make_unique<ComponentInfo>(
208       GetCrxComponentID(*component), component->fingerprint,
209       base::UTF8ToUTF16(component->name), component->version);
210 }
211 
GetComponents() const212 std::vector<ComponentInfo> CrxUpdateService::GetComponents() const {
213   DCHECK(thread_checker_.CalledOnValidThread());
214   std::vector<ComponentInfo> result;
215   for (const auto& it : components_) {
216     result.push_back(ComponentInfo(it.first, it.second.fingerprint,
217                                    base::UTF8ToUTF16(it.second.name),
218                                    it.second.version));
219   }
220   return result;
221 }
222 
GetOnDemandUpdater()223 OnDemandUpdater& CrxUpdateService::GetOnDemandUpdater() {
224   DCHECK(thread_checker_.CalledOnValidThread());
225   return *this;
226 }
227 
GetComponent(const std::string & id) const228 base::Optional<CrxComponent> CrxUpdateService::GetComponent(
229     const std::string& id) const {
230   DCHECK(thread_checker_.CalledOnValidThread());
231   const auto it = components_.find(id);
232   if (it != components_.end())
233     return it->second;
234   return base::nullopt;
235 }
236 
GetComponentState(const std::string & id) const237 const CrxUpdateItem* CrxUpdateService::GetComponentState(
238     const std::string& id) const {
239   DCHECK(thread_checker_.CalledOnValidThread());
240   const auto it(component_states_.find(id));
241   return it != component_states_.end() ? &it->second : nullptr;
242 }
243 
MaybeThrottle(const std::string & id,base::OnceClosure callback)244 void CrxUpdateService::MaybeThrottle(const std::string& id,
245                                      base::OnceClosure callback) {
246   DCHECK(thread_checker_.CalledOnValidThread());
247   const auto it = components_.find(id);
248   if (it != components_.end()) {
249     DCHECK_EQ(it->first, id);
250     if (OnDemandUpdateWithCooldown(id)) {
251       ready_callbacks_.insert(std::make_pair(id, std::move(callback)));
252       return;
253     }
254   }
255 
256   // Unblock the request if the request can't be throttled.
257   std::move(callback).Run();
258 }
259 
OnDemandUpdate(const std::string & id,Priority priority,Callback callback)260 void CrxUpdateService::OnDemandUpdate(const std::string& id,
261                                       Priority priority,
262                                       Callback callback) {
263   DCHECK(thread_checker_.CalledOnValidThread());
264 
265   if (!GetComponent(id)) {
266     if (!callback.is_null()) {
267       base::ThreadTaskRunnerHandle::Get()->PostTask(
268           FROM_HERE, base::BindOnce(std::move(callback),
269                                     update_client::Error::INVALID_ARGUMENT));
270     }
271     return;
272   }
273 
274   OnDemandUpdateInternal(id, priority, std::move(callback));
275 }
276 
OnDemandUpdateWithCooldown(const std::string & id)277 bool CrxUpdateService::OnDemandUpdateWithCooldown(const std::string& id) {
278   DCHECK(thread_checker_.CalledOnValidThread());
279 
280   DCHECK(GetComponent(id));
281 
282   // Check if the request is too soon.
283   const auto* component_state(GetComponentState(id));
284   if (component_state && !component_state->last_check.is_null()) {
285     base::TimeDelta delta =
286         base::TimeTicks::Now() - component_state->last_check;
287     if (delta < base::TimeDelta::FromSeconds(config_->OnDemandDelay()))
288       return false;
289   }
290 
291   OnDemandUpdateInternal(id, Priority::FOREGROUND, Callback());
292   return true;
293 }
294 
OnDemandUpdateInternal(const std::string & id,Priority priority,Callback callback)295 void CrxUpdateService::OnDemandUpdateInternal(const std::string& id,
296                                               Priority priority,
297                                               Callback callback) {
298   DCHECK(thread_checker_.CalledOnValidThread());
299 
300   UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.Calls", UPDATE_TYPE_MANUAL,
301                             UPDATE_TYPE_COUNT);
302 
303   auto crx_data_callback = base::BindOnce(&CrxUpdateService::GetCrxComponents,
304                                           base::Unretained(this));
305   auto update_complete_callback = base::BindOnce(
306       &CrxUpdateService::OnUpdateComplete, base::Unretained(this),
307       std::move(callback), base::TimeTicks::Now());
308 
309   if (priority == Priority::FOREGROUND)
310     update_client_->Install(id, std::move(crx_data_callback), {},
311                             std::move(update_complete_callback));
312   else if (priority == Priority::BACKGROUND)
313     update_client_->Update({id}, std::move(crx_data_callback), {}, false,
314                            std::move(update_complete_callback));
315   else
316     NOTREACHED();
317 }
318 
CheckForUpdates(UpdateScheduler::OnFinishedCallback on_finished)319 bool CrxUpdateService::CheckForUpdates(
320     UpdateScheduler::OnFinishedCallback on_finished) {
321   DCHECK(thread_checker_.CalledOnValidThread());
322 
323   // TODO(xiaochu): remove this log after https://crbug.com/851151 is fixed.
324   VLOG(1) << "CheckForUpdates: automatic updatecheck for components.";
325 
326   UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.Calls", UPDATE_TYPE_AUTOMATIC,
327                             UPDATE_TYPE_COUNT);
328 
329   std::vector<std::string> secure_ids;    // Requires HTTPS for update checks.
330   std::vector<std::string> unsecure_ids;  // Can fallback to HTTP.
331   for (const auto& id : components_order_) {
332     DCHECK(components_.find(id) != components_.end());
333 
334     const auto component = GetComponent(id);
335     if (!component || component->requires_network_encryption)
336       secure_ids.push_back(id);
337     else
338       unsecure_ids.push_back(id);
339   }
340 
341   if (unsecure_ids.empty() && secure_ids.empty()) {
342     base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
343                                                   std::move(on_finished));
344     return true;
345   }
346 
347   Callback on_finished_callback = base::BindOnce(
348       [](UpdateScheduler::OnFinishedCallback on_finished,
349          update_client::Error error) { std::move(on_finished).Run(); },
350       std::move(on_finished));
351 
352   if (!unsecure_ids.empty()) {
353     update_client_->Update(
354         unsecure_ids,
355         base::BindOnce(&CrxUpdateService::GetCrxComponents,
356                        base::Unretained(this)),
357         {}, false,
358         base::BindOnce(
359             &CrxUpdateService::OnUpdateComplete, base::Unretained(this),
360             secure_ids.empty() ? std::move(on_finished_callback) : Callback(),
361             base::TimeTicks::Now()));
362   }
363 
364   if (!secure_ids.empty()) {
365     update_client_->Update(
366         secure_ids,
367         base::BindOnce(&CrxUpdateService::GetCrxComponents,
368                        base::Unretained(this)),
369         {}, false,
370         base::BindOnce(&CrxUpdateService::OnUpdateComplete,
371                        base::Unretained(this), std::move(on_finished_callback),
372                        base::TimeTicks::Now()));
373   }
374 
375   return true;
376 }
377 
GetComponentDetails(const std::string & id,CrxUpdateItem * item) const378 bool CrxUpdateService::GetComponentDetails(const std::string& id,
379                                            CrxUpdateItem* item) const {
380   DCHECK(thread_checker_.CalledOnValidThread());
381 
382   // First, if this component is currently being updated, return its state from
383   // the update client.
384   if (update_client_->GetCrxUpdateState(id, item))
385     return true;
386 
387   // Otherwise, return the last seen state of the component, if such a
388   // state exists.
389   const auto component_states_it = component_states_.find(id);
390   if (component_states_it != component_states_.end()) {
391     *item = component_states_it->second;
392     return true;
393   }
394 
395   return false;
396 }
397 
GetCrxComponents(const std::vector<std::string> & ids)398 std::vector<base::Optional<CrxComponent>> CrxUpdateService::GetCrxComponents(
399     const std::vector<std::string>& ids) {
400   DCHECK(thread_checker_.CalledOnValidThread());
401   std::vector<base::Optional<CrxComponent>> components;
402   for (const auto& id : ids)
403     components.push_back(GetComponent(id));
404   return components;
405 }
406 
OnUpdateComplete(Callback callback,const base::TimeTicks & start_time,update_client::Error error)407 void CrxUpdateService::OnUpdateComplete(Callback callback,
408                                         const base::TimeTicks& start_time,
409                                         update_client::Error error) {
410   DCHECK(thread_checker_.CalledOnValidThread());
411   VLOG(1) << "Update completed with error " << static_cast<int>(error);
412 
413   UMA_HISTOGRAM_BOOLEAN("ComponentUpdater.UpdateCompleteResult",
414                         error != update_client::Error::NONE);
415   UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.UpdateCompleteError", error,
416                             update_client::Error::MAX_VALUE);
417   UMA_HISTOGRAM_LONG_TIMES_100("ComponentUpdater.UpdateCompleteTime",
418                                base::TimeTicks::Now() - start_time);
419 
420   for (const auto& id : components_pending_unregistration_) {
421     if (!update_client_->IsUpdating(id)) {
422       const auto component = GetComponent(id);
423       if (component)
424         DoUnregisterComponent(*component);
425     }
426   }
427 
428   if (!callback.is_null()) {
429     base::ThreadTaskRunnerHandle::Get()->PostTask(
430         FROM_HERE, base::BindOnce(std::move(callback), error));
431   }
432 }
433 
OnEvent(Events event,const std::string & id)434 void CrxUpdateService::OnEvent(Events event, const std::string& id) {
435   DCHECK(thread_checker_.CalledOnValidThread());
436 
437   // Unblock all throttles for the component.
438   if (event == Observer::Events::COMPONENT_UPDATED ||
439       event == Observer::Events::COMPONENT_NOT_UPDATED ||
440       event == Observer::Events::COMPONENT_UPDATE_ERROR) {
441     auto callbacks = ready_callbacks_.equal_range(id);
442     for (auto it = callbacks.first; it != callbacks.second; ++it) {
443       std::move(it->second).Run();
444     }
445     ready_callbacks_.erase(id);
446   }
447 
448   CrxUpdateItem update_item;
449   if (!update_client_->GetCrxUpdateState(id, &update_item))
450     return;
451 
452   // Update the state of the item.
453   const auto it = component_states_.find(id);
454   if (it != component_states_.end())
455     it->second = update_item;
456 
457   // Update the component registration with the new version.
458   if (event == Observer::Events::COMPONENT_UPDATED) {
459     const auto it = components_.find(id);
460     if (it != components_.end()) {
461       it->second.version = update_item.next_version;
462       it->second.fingerprint = update_item.next_fp;
463     }
464   }
465 }
466 
467 ///////////////////////////////////////////////////////////////////////////////
468 
469 // The component update factory. Using the component updater as a singleton
470 // is the job of the browser process.
471 // TODO(sorin): consider making this a singleton.
ComponentUpdateServiceFactory(scoped_refptr<Configurator> config,std::unique_ptr<UpdateScheduler> scheduler)472 std::unique_ptr<ComponentUpdateService> ComponentUpdateServiceFactory(
473     scoped_refptr<Configurator> config,
474     std::unique_ptr<UpdateScheduler> scheduler) {
475   DCHECK(config);
476   DCHECK(scheduler);
477   auto update_client = update_client::UpdateClientFactory(config);
478   return std::make_unique<CrxUpdateService>(config, std::move(scheduler),
479                                             std::move(update_client));
480 }
481 
482 }  // namespace component_updater
483