blob: 4b686e1e3604cf51c6289a1cb43d72cef582203b [file] [log] [blame]
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_NETWORK_STATE_NOTIFIER_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_NETWORK_STATE_NOTIFIER_H_
#include <memory>
#include "base/macros.h"
#include "base/optional.h"
#include "base/rand_util.h"
#include "base/single_thread_task_runner.h"
#include "third_party/blink/public/platform/web_connection_type.h"
#include "third_party/blink/public/platform/web_effective_connection_type.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
class PLATFORM_EXPORT NetworkStateNotifier {
USING_FAST_MALLOC(NetworkStateNotifier);
public:
struct NetworkState {
static const int kInvalidMaxBandwidth = -1;
bool on_line_initialized = false;
bool on_line = true;
bool connection_initialized = false;
WebConnectionType type = kWebConnectionTypeOther;
double max_bandwidth_mbps = kInvalidMaxBandwidth;
WebEffectiveConnectionType effective_type =
WebEffectiveConnectionType::kTypeUnknown;
base::Optional<base::TimeDelta> http_rtt;
base::Optional<base::TimeDelta> transport_rtt;
base::Optional<double> downlink_throughput_mbps;
bool save_data = false;
// If set, then network quality corresponding to
// |network_quality_web_holdback| should be returned to the web consumers.
// Consumers within Blink should still receive the actual network quality
// values.
base::Optional<WebEffectiveConnectionType> network_quality_web_holdback;
};
class NetworkStateObserver {
public:
// Will be called on the task runner that is passed in add*Observer.
virtual void ConnectionChange(
WebConnectionType,
double max_bandwidth_mbps,
WebEffectiveConnectionType,
const base::Optional<base::TimeDelta>& http_rtt,
const base::Optional<base::TimeDelta>& transport_rtt,
const base::Optional<double>& downlink_throughput_mbps,
bool save_data) {}
virtual void OnLineStateChange(bool on_line) {}
};
enum class ObserverType {
kOnLineState,
kConnectionType,
};
class PLATFORM_EXPORT NetworkStateObserverHandle {
USING_FAST_MALLOC(NetworkStateObserverHandle);
public:
NetworkStateObserverHandle(NetworkStateNotifier*,
ObserverType,
NetworkStateObserver*,
scoped_refptr<base::SingleThreadTaskRunner>);
~NetworkStateObserverHandle();
private:
NetworkStateNotifier* notifier_;
ObserverType type_;
NetworkStateObserver* observer_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
DISALLOW_COPY_AND_ASSIGN(NetworkStateObserverHandle);
};
NetworkStateNotifier() : has_override_(false) {}
~NetworkStateNotifier() {
DCHECK(connection_observers_.IsEmpty());
DCHECK(on_line_state_observers_.IsEmpty());
}
// Can be called on any thread.
bool OnLine() const {
MutexLocker locker(mutex_);
const NetworkState& state = has_override_ ? override_ : state_;
DCHECK(state.on_line_initialized);
return state.on_line;
}
// Returns the current effective connection type, which is the connection type
// whose typical performance is most similar to the measured performance of
// the network in use.
WebEffectiveConnectionType EffectiveType() const {
MutexLocker locker(mutex_);
const NetworkState& state = has_override_ ? override_ : state_;
// TODO (tbansal): Add a DCHECK to check that |state.on_line_initialized| is
// true once https://crbug.com/728771 is fixed.
return state.effective_type;
}
// Returns the current HTTP RTT estimate. If the estimate is unavailable, the
// returned optional value is null.
base::Optional<base::TimeDelta> HttpRtt() const {
MutexLocker locker(mutex_);
const NetworkState& state = has_override_ ? override_ : state_;
// TODO (tbansal): Add a DCHECK to check that |state.on_line_initialized| is
// true once https://crbug.com/728771 is fixed.
return state.http_rtt;
}
// Returns the current transport RTT estimate. If the estimate is unavailable,
// the returned optional value is null.
base::Optional<base::TimeDelta> TransportRtt() const {
MutexLocker locker(mutex_);
const NetworkState& state = has_override_ ? override_ : state_;
DCHECK(state.on_line_initialized);
return state.transport_rtt;
}
// Returns the current throughput estimate (in megabits per second). If the
// estimate is unavailable, the returned optional value is null.
base::Optional<double> DownlinkThroughputMbps() const {
MutexLocker locker(mutex_);
const NetworkState& state = has_override_ ? override_ : state_;
// TODO (tbansal): Add a DCHECK to check that |state.on_line_initialized| is
// true once https://crbug.com/728771 is fixed.
return state.downlink_throughput_mbps;
}
// Returns if the save data functionality has been enabled by the user.
// The returned value does not account for any holdback experiments that may
// be enabled.
bool SaveDataEnabled() const {
MutexLocker locker(mutex_);
const NetworkState& state = has_override_ ? override_ : state_;
// TODO (tbansal): Add a DCHECK to check that |state.on_line_initialized| is
// true once https://crbug.com/728771 is fixed.
return state.save_data;
}
void SetOnLine(bool);
// Can be called on any thread.
WebConnectionType ConnectionType() const {
MutexLocker locker(mutex_);
const NetworkState& state = has_override_ ? override_ : state_;
DCHECK(state.connection_initialized);
return state.type;
}
// Can be called on any thread.
bool IsCellularConnectionType() const {
switch (ConnectionType()) {
case kWebConnectionTypeCellular2G:
case kWebConnectionTypeCellular3G:
case kWebConnectionTypeCellular4G:
return true;
case kWebConnectionTypeBluetooth:
case kWebConnectionTypeEthernet:
case kWebConnectionTypeWifi:
case kWebConnectionTypeWimax:
case kWebConnectionTypeOther:
case kWebConnectionTypeNone:
case kWebConnectionTypeUnknown:
return false;
}
NOTREACHED();
return false;
}
// Can be called on any thread.
double MaxBandwidth() const {
MutexLocker locker(mutex_);
const NetworkState& state = has_override_ ? override_ : state_;
DCHECK(state.connection_initialized);
return state.max_bandwidth_mbps;
}
void SetWebConnection(WebConnectionType, double max_bandwidth_mbps);
void SetNetworkQuality(WebEffectiveConnectionType,
base::TimeDelta http_rtt,
base::TimeDelta transport_rtt,
int downlink_throughput_kbps);
void SetNetworkQualityWebHoldback(WebEffectiveConnectionType);
void SetSaveDataEnabled(bool enabled);
// When called, successive setWebConnectionType/setOnLine calls are stored,
// and supplied overridden values are used instead until clearOverride() is
// called. This is used for web tests (see crbug.com/377736) and inspector
// emulation.
// If |effective_type| is null, its value is computed using |http_rtt_msec|.
// |max_bandwidth_mbps| is used to override both the |max_bandwidth_mbps| and
// |downlink_throughput_mbps|.
//
// Since this class is a singleton, tests must clear override when completed
// to avoid indeterminate state across the test harness.
void SetNetworkConnectionInfoOverride(
bool on_line,
WebConnectionType,
base::Optional<WebEffectiveConnectionType> effective_type,
int64_t http_rtt_msec,
double max_bandwidth_mbps);
void SetSaveDataEnabledOverride(bool enabled);
void ClearOverride();
// Must be called on the given task runner. An added observer must be removed
// before the observer or its execution context goes away. It's possible for
// an observer to be called twice for the same event if it is first removed
// and then added during notification.
std::unique_ptr<NetworkStateObserverHandle> AddConnectionObserver(
NetworkStateObserver*,
scoped_refptr<base::SingleThreadTaskRunner>);
std::unique_ptr<NetworkStateObserverHandle> AddOnLineObserver(
NetworkStateObserver*,
scoped_refptr<base::SingleThreadTaskRunner>);
// Returns the String equivalent for a given WebEffectiveCOnnectionType.
static String EffectiveConnectionTypeToString(WebEffectiveConnectionType);
// Returns |rtt| after adding host-specific random noise, and rounding it as
// per the NetInfo spec to improve privacy.
uint32_t RoundRtt(const String& host,
const base::Optional<base::TimeDelta>& rtt) const;
// Returns |downlink_mbps| after adding host-specific random noise, and
// rounding it as per the NetInfo spec and to improve privacy.
double RoundMbps(const String& host,
const base::Optional<double>& downlink_mbps) const;
// Returns the randomization salt (weak and insecure) that should be used when
// adding noise to the network quality metrics. This is known only to the
// device, and is generated only once. This makes it possible to add the same
// amount of noise for a given origin.
uint8_t RandomizationSalt() const { return randomization_salt_; }
// Returns the overriding effective connection type that should be returned to
// the web consumers. If the returned value is null, then the actual network
// quality value should be returned to the web consumers.
// Consumers within Blink should not call this API.
base::Optional<WebEffectiveConnectionType> GetWebHoldbackEffectiveType()
const;
// Returns the overriding HTTP RTT estimate that should be returned to
// the web consumers. If the returned value is null, then the actual network
// quality value should be returned to the web consumers.
// Consumers within Blink should not call this API.
base::Optional<base::TimeDelta> GetWebHoldbackHttpRtt() const;
// Returns the overriding HTTP RTT estimate that should be returned to
// the web consumers. If the returned value is null, then the actual network
// quality value should be returned to the web consumers.
// Consumers within Blink should not call this API.
base::Optional<double> GetWebHoldbackDownlinkThroughputMbps() const;
// Sets the metrics of all the values while taking into account any network
// quality web holdbacks in place. The caller must guarantee that all pointers
// are non-null.
void GetMetricsWithWebHoldback(WebConnectionType* type,
double* downlink_max_mbps,
WebEffectiveConnectionType* effective_type,
base::Optional<base::TimeDelta>* http_rtt,
base::Optional<double>* downlink_mbps,
bool* save_data) const;
private:
friend class NetworkStateObserverHandle;
struct ObserverList {
ObserverList() : iterating(false) {}
bool iterating;
Vector<NetworkStateObserver*> observers;
Vector<wtf_size_t> zeroed_observers; // Indices in observers that are 0.
};
// This helper scope issues required notifications when mutating the state if
// something has changed. It's only possible to mutate the state on the main
// thread. Note that ScopedNotifier must be destroyed when not holding a lock
// so that onLine notifications can be dispatched without a deadlock.
class ScopedNotifier {
STACK_ALLOCATED();
public:
explicit ScopedNotifier(NetworkStateNotifier&);
~ScopedNotifier();
private:
NetworkStateNotifier& notifier_;
NetworkState before_;
};
// The ObserverListMap is cross-thread accessed, adding/removing Observers
// running on a task runner.
using ObserverListMap = HashMap<scoped_refptr<base::SingleThreadTaskRunner>,
std::unique_ptr<ObserverList>>;
void NotifyObservers(ObserverListMap&, ObserverType, const NetworkState&);
void NotifyObserversOnTaskRunner(ObserverListMap*,
ObserverType,
scoped_refptr<base::SingleThreadTaskRunner>,
const NetworkState&);
void AddObserverToMap(ObserverListMap&,
NetworkStateObserver*,
scoped_refptr<base::SingleThreadTaskRunner>);
void RemoveObserver(ObserverType,
NetworkStateObserver*,
scoped_refptr<base::SingleThreadTaskRunner>);
void RemoveObserverFromMap(ObserverListMap&,
NetworkStateObserver*,
scoped_refptr<base::SingleThreadTaskRunner>);
ObserverList* LockAndFindObserverList(
ObserverListMap&,
scoped_refptr<base::SingleThreadTaskRunner>);
// Removed observers are nulled out in the list in case the list is being
// iterated over. Once done iterating, call this to clean up nulled
// observers.
void CollectZeroedObservers(ObserverListMap&,
ObserverList*,
scoped_refptr<base::SingleThreadTaskRunner>);
// A random number by which the RTT and downlink estimates are multiplied
// with. The returned random multiplier is a function of the hostname.
// Adding this noise reduces the chances of cross-origin fingerprinting.
double GetRandomMultiplier(const String& host) const;
mutable Mutex mutex_;
NetworkState state_ GUARDED_BY(mutex_);
bool has_override_ GUARDED_BY(mutex_);
NetworkState override_ GUARDED_BY(mutex_);
ObserverListMap connection_observers_;
ObserverListMap on_line_state_observers_;
const uint8_t randomization_salt_ = base::RandInt(1, 20);
DISALLOW_COPY_AND_ASSIGN(NetworkStateNotifier);
};
PLATFORM_EXPORT NetworkStateNotifier& GetNetworkStateNotifier();
} // namespace blink
namespace WTF {
template <>
struct CrossThreadCopier<blink::NetworkStateNotifier::NetworkState>
: public CrossThreadCopierPassThrough<
blink::NetworkStateNotifier::NetworkState> {
STATIC_ONLY(CrossThreadCopier);
};
} // namespace WTF
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_NETWORK_STATE_NOTIFIER_H_