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