1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "third_party/blink/renderer/platform/network/network_state_notifier.h"
27 
28 #include <memory>
29 
30 #include "net/nqe/effective_connection_type.h"
31 #include "net/nqe/network_quality_estimator_params.h"
32 #include "third_party/blink/public/common/client_hints/client_hints.h"
33 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
34 #include "third_party/blink/renderer/platform/wtf/assertions.h"
35 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
36 #include "third_party/blink/renderer/platform/wtf/functional.h"
37 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
38 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
39 #include "third_party/blink/renderer/platform/wtf/threading.h"
40 #include "third_party/blink/renderer/platform/wtf/wtf.h"
41 
42 namespace blink {
43 
44 namespace {
45 
46 // Typical HTTP RTT value corresponding to a given WebEffectiveConnectionType
47 // value. Taken from
48 // https://cs.chromium.org/chromium/src/net/nqe/network_quality_estimator_params.cc.
49 const base::TimeDelta kTypicalHttpRttEffectiveConnectionType
50     [static_cast<size_t>(WebEffectiveConnectionType::kMaxValue) + 1] = {
51         base::TimeDelta::FromMilliseconds(0),
52         base::TimeDelta::FromMilliseconds(0),
53         base::TimeDelta::FromMilliseconds(3600),
54         base::TimeDelta::FromMilliseconds(1800),
55         base::TimeDelta::FromMilliseconds(450),
56         base::TimeDelta::FromMilliseconds(175)};
57 
58 // Typical downlink throughput (in Mbps) value corresponding to a given
59 // WebEffectiveConnectionType value. Taken from
60 // https://cs.chromium.org/chromium/src/net/nqe/network_quality_estimator_params.cc.
61 const double kTypicalDownlinkMbpsEffectiveConnectionType
62     [static_cast<size_t>(WebEffectiveConnectionType::kMaxValue) + 1] = {
63         0, 0, 0.040, 0.075, 0.400, 1.600};
64 
65 }  // namespace
66 
GetNetworkStateNotifier()67 NetworkStateNotifier& GetNetworkStateNotifier() {
68   DEFINE_THREAD_SAFE_STATIC_LOCAL(NetworkStateNotifier, network_state_notifier,
69                                   ());
70   return network_state_notifier;
71 }
72 
ScopedNotifier(NetworkStateNotifier & notifier)73 NetworkStateNotifier::ScopedNotifier::ScopedNotifier(
74     NetworkStateNotifier& notifier)
75     : notifier_(notifier) {
76   DCHECK(IsMainThread());
77   before_ = notifier_.has_override_ ? notifier_.override_ : notifier_.state_;
78 }
79 
~ScopedNotifier()80 NetworkStateNotifier::ScopedNotifier::~ScopedNotifier() {
81   DCHECK(IsMainThread());
82   const NetworkState& after =
83       notifier_.has_override_ ? notifier_.override_ : notifier_.state_;
84   if ((after.type != before_.type ||
85        after.max_bandwidth_mbps != before_.max_bandwidth_mbps ||
86        after.effective_type != before_.effective_type ||
87        after.http_rtt != before_.http_rtt ||
88        after.transport_rtt != before_.transport_rtt ||
89        after.downlink_throughput_mbps != before_.downlink_throughput_mbps ||
90        after.save_data != before_.save_data) &&
91       before_.connection_initialized) {
92     notifier_.NotifyObservers(notifier_.connection_observers_,
93                               ObserverType::kConnectionType, after);
94   }
95   if (after.on_line != before_.on_line && before_.on_line_initialized) {
96     notifier_.NotifyObservers(notifier_.on_line_state_observers_,
97                               ObserverType::kOnLineState, after);
98   }
99 }
100 
NetworkStateObserverHandle(NetworkStateNotifier * notifier,NetworkStateNotifier::ObserverType type,NetworkStateNotifier::NetworkStateObserver * observer,scoped_refptr<base::SingleThreadTaskRunner> task_runner)101 NetworkStateNotifier::NetworkStateObserverHandle::NetworkStateObserverHandle(
102     NetworkStateNotifier* notifier,
103     NetworkStateNotifier::ObserverType type,
104     NetworkStateNotifier::NetworkStateObserver* observer,
105     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
106     : notifier_(notifier),
107       type_(type),
108       observer_(observer),
109       task_runner_(std::move(task_runner)) {}
110 
111 NetworkStateNotifier::NetworkStateObserverHandle::
~NetworkStateObserverHandle()112     ~NetworkStateObserverHandle() {
113   notifier_->RemoveObserver(type_, observer_, std::move(task_runner_));
114 }
115 
SetOnLine(bool on_line)116 void NetworkStateNotifier::SetOnLine(bool on_line) {
117   DCHECK(IsMainThread());
118   ScopedNotifier notifier(*this);
119   {
120     MutexLocker locker(mutex_);
121     state_.on_line_initialized = true;
122     state_.on_line = on_line;
123   }
124 }
125 
SetWebConnection(WebConnectionType type,double max_bandwidth_mbps)126 void NetworkStateNotifier::SetWebConnection(WebConnectionType type,
127                                             double max_bandwidth_mbps) {
128   DCHECK(IsMainThread());
129   ScopedNotifier notifier(*this);
130   {
131     MutexLocker locker(mutex_);
132     state_.connection_initialized = true;
133     state_.type = type;
134     state_.max_bandwidth_mbps = max_bandwidth_mbps;
135   }
136 }
137 
SetNetworkQuality(WebEffectiveConnectionType type,base::TimeDelta http_rtt,base::TimeDelta transport_rtt,int downlink_throughput_kbps)138 void NetworkStateNotifier::SetNetworkQuality(WebEffectiveConnectionType type,
139                                              base::TimeDelta http_rtt,
140                                              base::TimeDelta transport_rtt,
141                                              int downlink_throughput_kbps) {
142   DCHECK(IsMainThread());
143   ScopedNotifier notifier(*this);
144   {
145     MutexLocker locker(mutex_);
146 
147     state_.effective_type = type;
148     state_.http_rtt = base::nullopt;
149     state_.transport_rtt = base::nullopt;
150     state_.downlink_throughput_mbps = base::nullopt;
151 
152     if (http_rtt.InMilliseconds() >= 0)
153       state_.http_rtt = http_rtt;
154 
155     if (transport_rtt.InMilliseconds() >= 0)
156       state_.transport_rtt = transport_rtt;
157 
158     if (downlink_throughput_kbps >= 0) {
159       state_.downlink_throughput_mbps =
160           static_cast<double>(downlink_throughput_kbps) / 1000;
161     }
162   }
163 }
164 
SetNetworkQualityWebHoldback(WebEffectiveConnectionType type)165 void NetworkStateNotifier::SetNetworkQualityWebHoldback(
166     WebEffectiveConnectionType type) {
167   DCHECK(IsMainThread());
168   if (type == WebEffectiveConnectionType::kTypeUnknown)
169     return;
170   ScopedNotifier notifier(*this);
171   {
172     MutexLocker locker(mutex_);
173 
174     state_.network_quality_web_holdback = type;
175   }
176 }
177 
178 std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle>
AddConnectionObserver(NetworkStateObserver * observer,scoped_refptr<base::SingleThreadTaskRunner> task_runner)179 NetworkStateNotifier::AddConnectionObserver(
180     NetworkStateObserver* observer,
181     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
182   AddObserverToMap(connection_observers_, observer, task_runner);
183   return std::make_unique<NetworkStateNotifier::NetworkStateObserverHandle>(
184       this, ObserverType::kConnectionType, observer, task_runner);
185 }
186 
SetSaveDataEnabled(bool enabled)187 void NetworkStateNotifier::SetSaveDataEnabled(bool enabled) {
188   DCHECK(IsMainThread());
189   ScopedNotifier notifier(*this);
190   {
191     MutexLocker locker(mutex_);
192     state_.save_data = enabled;
193   }
194 }
195 
196 std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle>
AddOnLineObserver(NetworkStateObserver * observer,scoped_refptr<base::SingleThreadTaskRunner> task_runner)197 NetworkStateNotifier::AddOnLineObserver(
198     NetworkStateObserver* observer,
199     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
200   AddObserverToMap(on_line_state_observers_, observer, task_runner);
201   return std::make_unique<NetworkStateNotifier::NetworkStateObserverHandle>(
202       this, ObserverType::kOnLineState, observer, task_runner);
203 }
204 
SetNetworkConnectionInfoOverride(bool on_line,WebConnectionType type,base::Optional<WebEffectiveConnectionType> effective_type,int64_t http_rtt_msec,double max_bandwidth_mbps)205 void NetworkStateNotifier::SetNetworkConnectionInfoOverride(
206     bool on_line,
207     WebConnectionType type,
208     base::Optional<WebEffectiveConnectionType> effective_type,
209     int64_t http_rtt_msec,
210     double max_bandwidth_mbps) {
211   DCHECK(IsMainThread());
212   ScopedNotifier notifier(*this);
213   {
214     MutexLocker locker(mutex_);
215     has_override_ = true;
216     override_.on_line_initialized = true;
217     override_.on_line = on_line;
218     override_.connection_initialized = true;
219     override_.type = type;
220     override_.max_bandwidth_mbps = max_bandwidth_mbps;
221 
222     if (!effective_type && http_rtt_msec > 0) {
223       base::TimeDelta http_rtt(
224           base::TimeDelta::FromMilliseconds(http_rtt_msec));
225       // Threshold values taken from
226       // net/nqe/network_quality_estimator_params.cc.
227       if (http_rtt >= net::kHttpRttEffectiveConnectionTypeThresholds
228                           [net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G]) {
229         effective_type = WebEffectiveConnectionType::kTypeSlow2G;
230       } else if (http_rtt >= net::kHttpRttEffectiveConnectionTypeThresholds
231                                  [net::EFFECTIVE_CONNECTION_TYPE_2G]) {
232         effective_type = WebEffectiveConnectionType::kType2G;
233       } else if (http_rtt >= net::kHttpRttEffectiveConnectionTypeThresholds
234                                  [net::EFFECTIVE_CONNECTION_TYPE_3G]) {
235         effective_type = WebEffectiveConnectionType::kType3G;
236       } else {
237         effective_type = WebEffectiveConnectionType::kType4G;
238       }
239     }
240     override_.effective_type = effective_type
241                                    ? effective_type.value()
242                                    : WebEffectiveConnectionType::kTypeUnknown;
243     override_.http_rtt = base::TimeDelta::FromMilliseconds(http_rtt_msec);
244     override_.downlink_throughput_mbps = max_bandwidth_mbps;
245   }
246 }
247 
SetSaveDataEnabledOverride(bool enabled)248 void NetworkStateNotifier::SetSaveDataEnabledOverride(bool enabled) {
249   DCHECK(IsMainThread());
250   ScopedNotifier notifier(*this);
251   {
252     MutexLocker locker(mutex_);
253     has_override_ = true;
254     override_.on_line_initialized = true;
255     override_.connection_initialized = true;
256     override_.save_data = enabled;
257   }
258 }
259 
ClearOverride()260 void NetworkStateNotifier::ClearOverride() {
261   DCHECK(IsMainThread());
262   ScopedNotifier notifier(*this);
263   {
264     MutexLocker locker(mutex_);
265     has_override_ = false;
266   }
267 }
268 
NotifyObservers(ObserverListMap & map,ObserverType type,const NetworkState & state)269 void NetworkStateNotifier::NotifyObservers(ObserverListMap& map,
270                                            ObserverType type,
271                                            const NetworkState& state) {
272   DCHECK(IsMainThread());
273   MutexLocker locker(mutex_);
274   for (const auto& entry : map) {
275     scoped_refptr<base::SingleThreadTaskRunner> task_runner = entry.key;
276     PostCrossThreadTask(
277         *task_runner, FROM_HERE,
278         CrossThreadBindOnce(&NetworkStateNotifier::NotifyObserversOnTaskRunner,
279                             CrossThreadUnretained(this),
280                             CrossThreadUnretained(&map), type, task_runner,
281                             state));
282   }
283 }
284 
NotifyObserversOnTaskRunner(ObserverListMap * map,ObserverType type,scoped_refptr<base::SingleThreadTaskRunner> task_runner,const NetworkState & state)285 void NetworkStateNotifier::NotifyObserversOnTaskRunner(
286     ObserverListMap* map,
287     ObserverType type,
288     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
289     const NetworkState& state) {
290   ObserverList* observer_list = LockAndFindObserverList(*map, task_runner);
291 
292   // The context could have been removed before the notification task got to
293   // run.
294   if (!observer_list)
295     return;
296 
297   DCHECK(task_runner->RunsTasksInCurrentSequence());
298 
299   observer_list->iterating = true;
300 
301   for (wtf_size_t i = 0; i < observer_list->observers.size(); ++i) {
302     // Observers removed during iteration are zeroed out, skip them.
303     if (!observer_list->observers[i])
304       continue;
305     switch (type) {
306       case ObserverType::kOnLineState:
307         observer_list->observers[i]->OnLineStateChange(state.on_line);
308         continue;
309       case ObserverType::kConnectionType:
310         observer_list->observers[i]->ConnectionChange(
311             state.type, state.max_bandwidth_mbps, state.effective_type,
312             state.http_rtt, state.transport_rtt, state.downlink_throughput_mbps,
313             state.save_data);
314         continue;
315     }
316     NOTREACHED();
317   }
318 
319   observer_list->iterating = false;
320 
321   if (!observer_list->zeroed_observers.IsEmpty())
322     CollectZeroedObservers(*map, observer_list, std::move(task_runner));
323 }
324 
AddObserverToMap(ObserverListMap & map,NetworkStateObserver * observer,scoped_refptr<base::SingleThreadTaskRunner> task_runner)325 void NetworkStateNotifier::AddObserverToMap(
326     ObserverListMap& map,
327     NetworkStateObserver* observer,
328     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
329   DCHECK(task_runner->RunsTasksInCurrentSequence());
330   DCHECK(observer);
331 
332   MutexLocker locker(mutex_);
333   ObserverListMap::AddResult result =
334       map.insert(std::move(task_runner), nullptr);
335   if (result.is_new_entry)
336     result.stored_value->value = std::make_unique<ObserverList>();
337 
338   DCHECK(result.stored_value->value->observers.Find(observer) == kNotFound);
339   result.stored_value->value->observers.push_back(observer);
340 }
341 
RemoveObserver(ObserverType type,NetworkStateObserver * observer,scoped_refptr<base::SingleThreadTaskRunner> task_runner)342 void NetworkStateNotifier::RemoveObserver(
343     ObserverType type,
344     NetworkStateObserver* observer,
345     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
346   switch (type) {
347     case ObserverType::kConnectionType:
348       RemoveObserverFromMap(connection_observers_, observer,
349                             std::move(task_runner));
350       break;
351     case ObserverType::kOnLineState:
352       RemoveObserverFromMap(on_line_state_observers_, observer,
353                             std::move(task_runner));
354       break;
355   }
356 }
357 
RemoveObserverFromMap(ObserverListMap & map,NetworkStateObserver * observer,scoped_refptr<base::SingleThreadTaskRunner> task_runner)358 void NetworkStateNotifier::RemoveObserverFromMap(
359     ObserverListMap& map,
360     NetworkStateObserver* observer,
361     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
362   DCHECK(task_runner->RunsTasksInCurrentSequence());
363   DCHECK(observer);
364 
365   ObserverList* observer_list = LockAndFindObserverList(map, task_runner);
366   if (!observer_list)
367     return;
368 
369   Vector<NetworkStateObserver*>& observers = observer_list->observers;
370   wtf_size_t index = observers.Find(observer);
371   if (index != kNotFound) {
372     observers[index] = 0;
373     observer_list->zeroed_observers.push_back(index);
374   }
375 
376   if (!observer_list->iterating && !observer_list->zeroed_observers.IsEmpty())
377     CollectZeroedObservers(map, observer_list, std::move(task_runner));
378 }
379 
380 NetworkStateNotifier::ObserverList*
LockAndFindObserverList(ObserverListMap & map,scoped_refptr<base::SingleThreadTaskRunner> task_runner)381 NetworkStateNotifier::LockAndFindObserverList(
382     ObserverListMap& map,
383     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
384   MutexLocker locker(mutex_);
385   ObserverListMap::iterator it = map.find(task_runner);
386   return it == map.end() ? nullptr : it->value.get();
387 }
388 
CollectZeroedObservers(ObserverListMap & map,ObserverList * list,scoped_refptr<base::SingleThreadTaskRunner> task_runner)389 void NetworkStateNotifier::CollectZeroedObservers(
390     ObserverListMap& map,
391     ObserverList* list,
392     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
393   DCHECK(task_runner->RunsTasksInCurrentSequence());
394   DCHECK(!list->iterating);
395 
396   // If any observers were removed during the iteration they will have
397   // 0 values, clean them up.
398   for (wtf_size_t i = 0; i < list->zeroed_observers.size(); ++i)
399     list->observers.EraseAt(list->zeroed_observers[i]);
400 
401   list->zeroed_observers.clear();
402 
403   if (list->observers.IsEmpty()) {
404     MutexLocker locker(mutex_);
405     map.erase(task_runner);  // deletes list
406   }
407 }
408 
409 // static
EffectiveConnectionTypeToString(WebEffectiveConnectionType type)410 String NetworkStateNotifier::EffectiveConnectionTypeToString(
411     WebEffectiveConnectionType type) {
412   DCHECK_GT(kWebEffectiveConnectionTypeMappingCount, static_cast<size_t>(type));
413   return kWebEffectiveConnectionTypeMapping[static_cast<int>(type)];
414 }
415 
GetRandomMultiplier(const String & host) const416 double NetworkStateNotifier::GetRandomMultiplier(const String& host) const {
417   // The random number should be a function of the hostname to reduce
418   // cross-origin fingerprinting. The random number should also be a function
419   // of randomized salt which is known only to the device. This prevents
420   // origin from removing noise from the estimates.
421   if (!host)
422     return 1.0;
423 
424   unsigned hash = StringHash::GetHash(host) + RandomizationSalt();
425   double random_multiplier = 0.9 + static_cast<double>((hash % 21)) * 0.01;
426   DCHECK_LE(0.90, random_multiplier);
427   DCHECK_GE(1.10, random_multiplier);
428   return random_multiplier;
429 }
430 
RoundRtt(const String & host,const base::Optional<base::TimeDelta> & rtt) const431 uint32_t NetworkStateNotifier::RoundRtt(
432     const String& host,
433     const base::Optional<base::TimeDelta>& rtt) const {
434   if (!rtt.has_value()) {
435     // RTT is unavailable. So, return the fastest value.
436     return 0;
437   }
438 
439   // Limit the maximum reported value and the granularity to reduce
440   // fingerprinting.
441   constexpr auto kMaxRtt = base::TimeDelta::FromSeconds(3);
442   constexpr auto kGranularity = base::TimeDelta::FromMilliseconds(50);
443 
444   const base::TimeDelta modified_rtt =
445       std::min(rtt.value() * GetRandomMultiplier(host), kMaxRtt);
446   DCHECK_GE(modified_rtt, base::TimeDelta());
447   return static_cast<uint32_t>(
448       modified_rtt.RoundToMultiple(kGranularity).InMilliseconds());
449 }
450 
RoundMbps(const String & host,const base::Optional<double> & downlink_mbps) const451 double NetworkStateNotifier::RoundMbps(
452     const String& host,
453     const base::Optional<double>& downlink_mbps) const {
454   // Limit the size of the buckets and the maximum reported value to reduce
455   // fingerprinting.
456   static const size_t kBucketSize = 50;
457   static const double kMaxDownlinkKbps = 10.0 * 1000;
458 
459   double downlink_kbps = 0;
460   if (!downlink_mbps.has_value()) {
461     // Throughput is unavailable. So, return the fastest value.
462     downlink_kbps = kMaxDownlinkKbps;
463   } else {
464     downlink_kbps = downlink_mbps.value() * 1000;
465   }
466   downlink_kbps *= GetRandomMultiplier(host);
467 
468   downlink_kbps = std::min(downlink_kbps, kMaxDownlinkKbps);
469 
470   DCHECK_LE(0, downlink_kbps);
471   DCHECK_GE(kMaxDownlinkKbps, downlink_kbps);
472   // Round down to the nearest kBucketSize kbps value.
473   double downlink_kbps_rounded =
474       std::round(downlink_kbps / kBucketSize) * kBucketSize;
475 
476   // Convert from Kbps to Mbps.
477   return downlink_kbps_rounded / 1000;
478 }
479 
480 base::Optional<WebEffectiveConnectionType>
GetWebHoldbackEffectiveType() const481 NetworkStateNotifier::GetWebHoldbackEffectiveType() const {
482   MutexLocker locker(mutex_);
483 
484   const NetworkState& state = has_override_ ? override_ : state_;
485   // TODO (tbansal): Add a DCHECK to check that |state.on_line_initialized| is
486   // true once https://crbug.com/728771 is fixed.
487   return state.network_quality_web_holdback;
488 }
489 
GetWebHoldbackHttpRtt() const490 base::Optional<base::TimeDelta> NetworkStateNotifier::GetWebHoldbackHttpRtt()
491     const {
492   base::Optional<WebEffectiveConnectionType> override_ect =
493       GetWebHoldbackEffectiveType();
494 
495   if (override_ect) {
496     return kTypicalHttpRttEffectiveConnectionType[static_cast<size_t>(
497         override_ect.value())];
498   }
499   return base::nullopt;
500 }
501 
502 base::Optional<double>
GetWebHoldbackDownlinkThroughputMbps() const503 NetworkStateNotifier::GetWebHoldbackDownlinkThroughputMbps() const {
504   base::Optional<WebEffectiveConnectionType> override_ect =
505       GetWebHoldbackEffectiveType();
506 
507   if (override_ect) {
508     return kTypicalDownlinkMbpsEffectiveConnectionType[static_cast<size_t>(
509         override_ect.value())];
510   }
511   return base::nullopt;
512 }
513 
GetMetricsWithWebHoldback(WebConnectionType * type,double * downlink_max_mbps,WebEffectiveConnectionType * effective_type,base::Optional<base::TimeDelta> * http_rtt,base::Optional<double> * downlink_mbps,bool * save_data) const514 void NetworkStateNotifier::GetMetricsWithWebHoldback(
515     WebConnectionType* type,
516     double* downlink_max_mbps,
517     WebEffectiveConnectionType* effective_type,
518     base::Optional<base::TimeDelta>* http_rtt,
519     base::Optional<double>* downlink_mbps,
520     bool* save_data) const {
521   MutexLocker locker(mutex_);
522   const NetworkState& state = has_override_ ? override_ : state_;
523 
524   *type = state.type;
525   *downlink_max_mbps = state.max_bandwidth_mbps;
526 
527   base::Optional<WebEffectiveConnectionType> override_ect =
528       state.network_quality_web_holdback;
529   if (override_ect) {
530     *effective_type = override_ect.value();
531     *http_rtt = kTypicalHttpRttEffectiveConnectionType[static_cast<size_t>(
532         override_ect.value())];
533     *downlink_mbps =
534         kTypicalDownlinkMbpsEffectiveConnectionType[static_cast<size_t>(
535             override_ect.value())];
536   } else {
537     *effective_type = state.effective_type;
538     *http_rtt = state.http_rtt;
539     *downlink_mbps = state.downlink_throughput_mbps;
540   }
541   *save_data = state.save_data;
542 }
543 
544 }  // namespace blink
545